2015-03-19 2 views
0

В последнее время я работал над проектом, и дело в том, что мы столкнулись с ситуацией, в которой мы должны иметь возможность делать подобные вещи.CRTP/Macros/Избегайте указателя на производный класс

#define TYPED(Type) \ 
    virtual Type *typedThis() const { return (Type*) this; } 

class A { 
public: 
TYPED(A) 
virtual void describe() { std::cout << "I am type A\n"; } 
static int x; 
}; 

int A::x = 1; 

class B : public A { 
public: 
TYPED(B) 
virtual void describe() { std::cout << "I am type B\n"; } 
static int x; 
}; 

int B::x = 2; 

int 
main(int argc, char** argv) 
{ 
    B* b = new B(); 
    A* b2 = b; 

    b->describe(); 
    b2->describe(); 

    std::cout << b->typedThis()->x << std::endl; 
    std::cout << b2->typedThis()->x << std::endl; // AQUI DEBERIA DAR 2 !! :c 
} 

Это, конечно, только пример игрушки. Основная идея того, что мы хотели бы сделать, - определить функцию typedThis(), которая выполняет литье указателя в правильный тип, а затем доступ к правильной переменной x и распечатку 2 вместо 1.

Однако вывод состоит в следующем:

I am type B 
I am type B 
2 
1 //Here should also be 2 

что я нахожу очень интересным является то, что виртуальный метод описания(), кажется, работает так, как мы хотим. Поэтому я мог бы сделать вывод о том, что метод typedThis() также работает так, как нам хотелось бы. Но если да, то почему C++ видит этот указатель как A * вместо B *. Если C++ видел этот указатель как B *, тогда он использовал бы правильную переменную x. Может кто-нибудь объяснить это мне?

Я пробовал использовать CRTP, однако я не чувствую, что это облегчит ситуацию, потому что в проекте мы будем использовать много (ЛОТ) разных классов, которые производят между ними постоянно, я видел некоторые статьи о том, как использовать CRTP при наличии множественного наследования, однако они действительно беспорядочны и трудно интегрируются с тем, что мы имеем до сих пор.

+0

Почему должно быть 2, если оно не является виртуальным? Он будет разрешен с базовым классом указателя вместо проверки на любую виртуальную таблицу для решения этой функции. – Raistmaj

+0

pba должен быть виртуальным. – NathanOliver

+0

Да. Поскольку это всего лишь игрушечный пример, я использовал фиктивный метод, который просто распечатывает число. Но в реальном коде мы хотим получить доступ к некоторым атрибутам, которые имеют одинаковое имя между классами. Поэтому предположим, что оба класса A и B имеют статический атрибут x. Затем мы получаем указатель A *, но с указателем экземпляра B. Тогда, если мы хотим получить значение x. Мы получаем значение x класса A, а не класс B.Который в этом примере был бы эквивалентен высказыванию, мы печатаем с использованием метода pba() для A вместо метода pba() из B. – Fertaku

ответ

0

Я удалил все отвлечения из примера:

class A { 
public: 
    virtual A *typedThis() const { return (A*) this; } 
    static int x = 1; 
}; 

class B : public A { 
public: 
    virtual B *typedThis() const { return (B*) this; } 
    static int x = 2; 
}; 

int main() 
{ 
    B* b1 = new B; 
    A* b2 = b1; 

    std::cout << b1->typedThis()->x << "\n"; 
    std::cout << b2->typedThis()->x << "\n"; 
} 

typedThis не делает ничего .

b1->typedThis() возвращает B*, который указывает на B.

Аналогичным образом, b1 сам по себе является B*, который указывает на B.

b2->typedThis() возвращает A*, который указывает на B.

Аналогичным образом, b2 сам по себе является A*, который указывает на B.

Так b1->typedThis() такая же, как b1 и b2->typedThis() такая же, как b2, а последние две строки примера эквивалентны следующему:

std::cout << b1->x << "\n"; 
    std::cout << b2->x << "\n"; 

Также обратите внимание, что C-стиле бросает отбрасывать const квалификаторы объектов.

+0

Он точно не решил мои проблемы, но был самым полезным ответом, спасибо! – Fertaku

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

  • Нет связанных вопросов^_^