#c #casting #typeid
#c #Кастинг #typeid
Вопрос:
Отредактировано. смотрите ниже.
Рассмотрим следующую ситуацию — у меня есть базовый класс и несколько производных :
class B {...[virtual...]...};
class D1 : public B {...};
class D2 : public B {...};
и у меня есть функция, которая должна вести себя по-разному для каждого из них (с точки зрения реализации. концептуально все они делают одно и то же).
теперь, я бы, конечно, реализовал вспомогательную функцию для каждого :
void f_aux (B * b);
void f_aux (D1 * d1);
void f_aux (D2 * d2);
и тогда я могу реализовать основную функцию, выполнив что-то вроде этого :
void f (B * bd1d2) {
if (typeid(bd1d2) == typeid(B*) { f_aux((B*)bd1d2); }
else if (typeid(bd1d2) == typeid(D1*) { f_aux((D1*)bd1d2); }
else if (typeid(bd1d2) == typeid(D2*) { f_aux((D2*)bd1d2); }
}
теперь мой вопрос — могу ли я сделать это общим способом, имея в виду что-то вроде :
void f (B * bd1d2) {
f_aux(CAST_TO_REAL_TYPE(bd1d2));
}
Я попытался напрямую использовать typeid, что, конечно, не удалось.
Я попытался выполнить поиск в Интернете, но ничего не нашел..
итак, есть ли способ сделать это? и если нет, то почему?
большое спасибо..
Редактировать :
Я попытался упростить проблему, и, похоже, у меня получилось наоборот.. Позвольте мне попробовать еще раз: в реальных настройках у меня есть 3 класса: Line, Ray и Segment, и я бы хотел, чтобы другой класс (Graph-Edge) содержал общий указатель на объект любого из типов — это означает, что у него есть поле, которое может быть либо линейным лучом, либо сегментом. Теперь я хотел бы иметь возможность получить точку пересечения двух ребер графа или ребра графа с заданной линией / лучом / сегментом, не затрагивая вопрос «что это за ребро?» ни на одном из двух. Я также хотел бы, чтобы мои классы геометрии были как можно более универсальными, даже если некоторые функциональные возможности не являются необходимыми для ребер графа.. Итак, я создал суперкласс: LinearObject с виртуальной функцией getCut (LinearObject amp; lo) и следовал приведенным ниже ответам, и лучшее, что у меня пока есть, это :
class Line;
class Segment;
class LinearObject {
public :
virtual void getCut(LinearObject * lo) { printf("lo->lon"); }
private :
virtual void getCut_aux(Line * l) = 0;
virtual void getCut_aux(Segment * s) = 0;
friend class Line; friend class Segment;
};
class Line : public LinearObject {
public :
void getCut(LinearObject * lo) { printf("l->lon"); lo->getCut_aux(this); }
private :
void getCut_aux(Line * l) { printf("l->ln"); }
void getCut_aux(Segment * s) { printf("l->sn"); }
};
class Segment : public LinearObject {
public :
void getCut(LinearObject * lo) {
printf("s->lon");
lo->getCut_aux(this);
}
private :
void getCut_aux(Line * l) { printf("s->ln"); }
void getCut_aux(Segment * s) { printf("s->sn"); }
};
int main() {
Line l; Segment s;
LinearObject *lop = amp;l, *lop2 = amp;s;
lop->getCut(lop2);
s.getCut(amp;l);
return 0;
}
Это работает, но могу ли я сделать что-нибудь лучше (элегантно)?
Комментарии:
1. Обязательно ли реализовывать
f_aux
функцию вне класса?2. @Holt не совсем. Я опубликую более подробный ответ в качестве комментария к вашему ответу.
Ответ №1:
Редактировать: Редактировать после того, как вы зададите вопрос edit, надеюсь, я понял все, что вы хотите…
#include <cstdio>
class Line;
class Segment;
class LinearObject {
protected :
virtual void getCut_aux(LinearObject *) { printf("LinearObject->getCut_auxn") ; }
friend class LinearObject_ ;
};
class LinearObject_ : public LinearObject {
public:
void getCut (LinearObject * lo) { printf("getCut()n"); lo->getCut_aux(this) ; }
};
class Line : public LinearObject_ {
protected :
virtual void getCut_aux(LinearObject *) { printf("Line->getCut_auxn") ; }
};
class Segment : public LinearObject_ {
protected:
virtual void getCut_aux(LinearObject *) { printf("Segment->getCut_auxn") ; }
};
int main() {
Line l; Segment s;
LinearObject *lop = amp;l, *lop2 = amp;s;
l.getCut(lop2);
s.getCut(amp;l);
return 0;
}
Окончание редактирования.
Если вы можете реализовать f_aux
внутри класса, просто воспользуйтесь преимуществами полиморфизма:
class B {
/* ...[virtual...]... */
virtual void f_aux () { /* ... */ }
};
class D1 : public B {
/* ... */
virtual void f_aux () { /* ... */ }
};
class D2 : public B {
/* ... */
virtual void f_aux () { /* ... */ }
};
Затем просто выполните:
void f (B * bd1d2) {
bd1d2->f_aux () ; // Will call the correct function depending on what bd1d2 points to
}
Редактировать: В ответ на ваш комментарий, если вы хотите иметь что-то вроде, d->f_aux(b)
просто добавьте промежуточный класс D
:
class D {
void f (B *b) { b->f_aux(this) ; }
};
class D1 : public B, public D { /* ... */ };
B *pdb = new B (), *pd3 = new D3 () ;
D1 *pd1 = new D1 () ;
D2 *pd2 = new D2 () ;
pd1->f(b) ;
pd2->f(pd3) ;
Комментарии:
1. на самом деле это было упрощением реальной ситуации. В реальной задаче я хочу реализовать двоичную функцию между двумя экземплярами переменных B. использование было бы примерно таким: p1-> f (p2), где каждый из p1, p2 может указывать на D1, D2, D3 и т.д. Но p1, p2 всегда будут определяться как B *. Думаю, я мог бы последовать вашему совету и сделать что-то вроде: void D #::f(B * b) { return b-> f_aux(this); }, верно? но я действительно хотел что-то более общее и менее хакерское…
2. @elad: Я думаю, что решение состоит в том, чтобы разделить
f_aux
на две части: часть первого параметра и часть второго параметра.3. @elad Почему определение
f
внутри класса является хакерским?4. @Holt, потому что это заставляет меня объявлять «ненужные» чисто виртуальные вспомогательные функции в базовом классе, по одной для каждого производного класса, и вообще, я хотел бы, чтобы c также знал, как динамически выбирать между функциями по реальному типу аргументов указателя / ссылки или, по крайней мере, предоставлять чистую опцию для принудительного выполнения такого действия. ну, это немного лучше, чем if-else для typeid. еще раз спасибо..
5. @Dani, я тебя не понял .. функция f касается отношения между двумя объектами. как бы я разделил его?
Ответ №2:
«Правильное» решение для этого — сделать что-то вроде использования виртуальных функций:
void f_aux (B * b)
{
b->do_f_aux();
}
где вы объявляете do_f_aux
как
class B
{
...
virtual void do_f_aux() { ... };
}
и, конечно, реализовать это do_f_aux
по-разному в d1
и d2
.
[Предполагается, что вам нужна f_aux
как отдельная функция — если вам просто нужна функция, которая делает это, тогда просто вызовите виртуальную функцию f_aux
и вызывайте ее непосредственно в f
as bd1d2->f_aux()
]
Редактировать:
Приведение объектов так, как вы описываете, «плохо пахнет» (http://en.wikipedia.org/wiki/Code_smell ) — симптом «вы делаете это неправильно». Этого почти всегда можно избежать, используя виртуальные функции.