2012-06-08 4 views
0

Пусть у меня есть пара базовых классов:Многократные Ссылки Наследование Класс

class A 
{}; 

class B 
{ 
    virtual void foo(A* ref); 
    virtual void foo2(A* ref); 
}; 

и от них, несколько производных классов:

class C : virtual public A 
{ 
    int data; 
}; 

class D : public B 
{ 
    virtual void foo(A* ref) 
    { 
     ((C*) (ref)).data = 5; 
    } 
}; 

class E : virtual public A 
{ 
    int otherData; 
}; 

class F : public B 
{ 
    virtual void foo2(A* ref) 
    { 
     ((E*) (ref)).otherData = 6; 
    } 
}; 

И, наконец, у нас есть класс, который следует костюм, как таковые:

class G : public E, public C 
{ 
}; 

с основной функцией, которая идет следующим образом:

int main() 
{ 
    B* myD = new D(); 
    B* myF = new F(); 

    A* myA = new G(); 

    myD->foo(myA); 
    myF->foo2(myA); 

    return 0; 
} 

Хорошо, не обращая внимания на очевидный факт, что это «ужасный алмаз» (который, «составными как» проблема была предотвращена благодаря virtual), общая идея заключается в том, что я хочу, чтобы все мои объекты будут Gs, Ds и Fs, но сохраняются как ссылки на As и Bs. Я хочу иметь возможность использовать виртуальные функции B (которые на самом деле всегда являются D или F) для изменения значений G ... Однако A и B не знают ни одного из своих дочерних классов и поэтому не могут быть объявлены с использованием их. Это означает, что аргументы виртуальной функции всегда должны быть указателем на A. Однако D хочет, чтобы его вход всегда был C, а F хочет, чтобы он был символом E. Хотя это может и не быть проблемой, если G был наследующий только от одного родителя, наш текущий G наследуется от 2 родителей; Я не знаю, как указатели this действительно работают в контексте контекста таких ситуаций ... однако я не чувствую, что это будет возможная ситуация.

Может кто-нибудь захочет пролить свет на механизмы указателей каста на родительские/дочерние классы, и если будет какой-нибудь способ, это можно было бы снять?

+0

На самом деле, я боюсь, что здесь нет алмаза, это: 1/использование непроверенных указателей =>, пожалуйста, передайте 'ref' по ссылке и 2/использование C-casts => используйте соответствующие C++ casts (здесь, вероятно, с проверкой времени выполнения). –

ответ

1

Предполагая, что у вас включен RTTI, вы можете использовать dynamic_cast, чтобы попытаться подстроить указатель базового класса на указатель класса производных (или родных). Объект, на который ссылается, тестируется во время выполнения, чтобы узнать, является ли он совместимым классом, если он не является достоверным upcast, чем возвращает 0, и в этом случае вы можете проверить это и разветвить.

http://en.wikipedia.org/wiki/Dynamic_cast

1

Поскольку A виртуальная база G, вы не можете преобразовать указатель на A к указателю G статически. Весь смысл virtual «определяется во время выполнения», поэтому характер объекта A, который у вас есть, может быть известен только во время выполнения. Например, в вашем случае рассмотрим:

A * p = new G, q = new C; 

Теперь оба p и q пункт некоторыеA -subobject, но природа наиболее производный объект, в котором они содержатся, определяется только фактическим наиболее производного типа. Таким образом, невозможно иметь одно правило фиксированного преобразования от A* до G* или от A* до C*.

Единственный способ выполнить бросок динамически, то есть с помощью dynamic_cast<C*>(p) т.д.

(В отличии от этого, в не виртуальном наследовании оно всегда известно, как подобъект относится к его более-производным объектам, и, таким образом, указатели может быть преобразован статически.Могут возникнуть проблемы с неоднозначностью присвоения имен, если у вас много повторных оснований, но нет проблемы с поиском базовых подобъектов.)

0

Как говорят Керрек и Эндрю, вы должны использовать dynamic_cast. Однако, если дети A являются статическими, вы можете использовать другой трюк, который может быть быстрее, чем dynamic_cast (так как он не зависит от RTTI, см. this question для получения дополнительной информации).

class C; 
class D; 
class E; 
class F; 

class A 
{ 
public: 
    virtual C * isC() { return 0; } 
    virtual E * isE() { return 0; } 
}; 

class C : virtual public A 
{ 
    friend class D; 
    int data; 
    C * isC() { return this; } 
}; 

class E : virtual public A 
{ 
    friend class F; 
    int otherData; 
    E * isE() { return this; } 
}; 

Теперь D и F может запросить A чтобы увидеть, если isC или isE возвращает действительный указатель.

+0

В основном это зависит от того, как RTTI внутренне реализуется, но для многих платформ, что вы описываете, не больше - не меньше, чем реализация dynamic_cast (функции неявно добавляются в иерархию параллельных объектов) , Поэтому я не ожидал каких-либо особых различий в производительности. Если, конечно, особая неэффективность компилятора! –

+0

@EmilioGaravaglia: проверьте [this] (http://stackoverflow.com/questions/4050901/performance-of-dynamic-cast), в частности ответ VladV. – jxh

Смежные вопросы