есть ли в c способ привести указатель к реальному типу, на который он указывает?

#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 ) — симптом «вы делаете это неправильно». Этого почти всегда можно избежать, используя виртуальные функции.