Вызов виртуальной функции в подклассе из суперкласса - PullRequest
5 голосов
/ 22 августа 2010

Я знаю, что этот вопрос должен был освещаться бесконечно много раз, но я искал предыдущие вопросы, и, похоже, ничего не появляется.

Речь идет о наследовании и виртуальных функциях в C ++.У меня проблема с вызовом виртуальных функций в подклассах из суперкласса.

Позвольте мне привести пример.Начнем с трех классов, которые наследуются друг от друга.

class A {

    void foo() { bar() }
    virtual void bar() { }

};

class B : public A {

    virtual void bar() { }

};

class C : public B {

    virtual void bar() { // do something }

};

Теперь я хочу иметь переменную, объявленную как B *, но созданную как C *.

B* myObject = new C();
myObject->foo();

Когда я делаю этои вызовите foo () для myObject, тогда A :: foo () вызывает bar ().Но вызывается только B :: bar (), а не C :: Bar (), которым на самом деле является myObject, даже если он объявлен как B, что опять же влияет на то, что «// ничего не делать» не выполняется.

Как мне сказать A :: foo (), что он должен смотреть на низшую реализацию?

Имеет смысл?

// Trenskow

EDIT:

C :: Foo не проблема.Foo вызывается в классе A, поскольку это единственное место, где он реализован.Проблема возникает, когда A: Foo вызывает Bar ().Затем вызывается B: Bar, а не C :: Bar.

Возможно, проблема в том, что в моей реализации я получаю только указатель void * на объект в A.

Вот так:

void A:Foo(void *a) {

    A* tmpA = static_cast<A*> (a);
    tmpA->bar();

}

Теперь компилятор думает, что tmpA является A. Но каким-то образом ему удается понять, что это B *, и вызывает B :: Bar, когда на самом деле tmpA является C *, и этодолжен вызывать C :: Bar.

Ответы [ 6 ]

5 голосов
/ 22 августа 2010

Следующее печатает «A :: foo C :: bar», как и ожидалось.Вы получаете что-то другое?B::bar никогда не вызывается, потому что C - это фактический тип времени выполнения объекта.В C::bar вы можете явно вызвать B::bar, добавив B::bar(); к его телу.

#include <iostream>
using namespace std;

class A {
public:
    void foo() { cout << "A::foo "; bar(); }
    virtual void bar() { }
};

class B : public A {
public:
    virtual void bar() { cout << "B::bar" << endl; }
};

class C : public B {
public:
    virtual void bar() { cout << "C::bar" << endl; }
};

int main()
{
    B* c = new C();
    c->foo();
    return 0;
}
2 голосов
/ 22 августа 2010
void A:Foo(void *a) {

    A* tmpA = static_cast<A*> (a);
    tmpA->bar();

}

Это неопределенное поведение.Вы не можете разыграть B * в пустоте *, а затем разыграть эту пустоту * обратно в A *.Если вы хотите, чтобы это работало должным образом, вы должны ликвидировать пустоту *.Кроме того, вы можете попробовать dynamic_cast.

0 голосов
/ 22 августа 2010

Какой компилятор вы используете?В Visual Studio (IIRC) информация о типе среды выполнения обычно отключена по умолчанию, поэтому, может быть, это просто что-то простое?

0 голосов
/ 22 августа 2010

Я не следую. Вы говорите

Но вызывается только B :: bar (), а не C :: Bar ()

Нет. Вы вызвали конструктор класса C, что означает, что vtable заставляет bar () указывать на C :: bar (), поэтому вызов foo () в этом случае будет идти прямо к C :: bar ().

Если вы хотите заставить A :: foo () явно вызывать только реализацию A, вы можете сделать это, просто написав

void foo() { A::bar(); }

Что именно вы пытаетесь сделать?

0 голосов
/ 22 августа 2010

Разве вы не имеете в виду:

B* myObject = new C();
myObject->foo(); // not variable->foo()

class A
{
public:
    void foo() { bar(); }
    virtual void bar() { std::cout << "A"; };
};

class B : public A
{
public:
    virtual void bar() { std::cout << "B";};
};

class C : public B
{
public:
    virtual void bar() { std::cout << "C"; }
};

Это печатает 'C', как и ожидалось.

0 голосов
/ 22 августа 2010

Предполагается, что вы неправильно набрали последний блок кода и имена совпадают:

B* variable = new C();
variable->foo();

Затем вызывается метод C :: Foo или вы используете ужасно плохой компилятор.

(Это также предполагает, что на самом деле у вас нет ошибки компилятора в C :: Foo, и что комментарий на самом деле похож на std::cout << "Hi mom!" << std::endl;)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...