Как узнать, какая функция будет вызвана? - PullRequest
1 голос
/ 28 января 2011

Сегодня я обнаружил следующую тревожно-неоднозначную ситуацию в нашей базе кода:

class Base {
public:
    virtual void Irrelevant_Function(void) = 0;

protected:
    C_Container *   Get_Container(void);
};

class A : public Base, public Not_Important {
public:
    inline C_Container *    Get_Container(void);
};

class B : public Base, protected SomethingElse {
public:
    C_Container *   Get_Container(void);
};

Многие вещи вызывали метод Get_Container, но не всегда вызывали правильный - обратите внимание, что ни одна из этих функций не была виртуальной.

Мне нужно переименовать методы Get_Base_Container, Get_A_Container и т. Д., Чтобы устранить неоднозначность.Какие правила использует C ++, чтобы определить, какую версию функции он должен вызывать?Я хотел бы начать с «известного состояния» того, что должно было быть вызвано, а затем выяснить ошибки оттуда.

Например, если у меня есть указатель на Base и вызову Get_Container,Я предполагаю, что это просто вызовет базовую версию функции.Что если у меня есть указатель на A?Как насчет указателя на B?А как насчет A или B в куче?

Спасибо.

Ответы [ 4 ]

5 голосов
/ 28 января 2011

Зависит от того, как вы вызываете функцию. Если вы звоните через A *, A & или A, то вы будете звонить A::Get_Container(). Если вы звоните через Base *, Base & (даже если они указывают / ссылаются на A), то вы будете звонить Base::Get_Container().

3 голосов
/ 28 января 2011

Пока не происходит виртуальное наследование, это довольно просто.Если вы работаете напрямую с объектом, вызывается метод объекта;если вы работаете с указателем или ссылкой, метод указывает тип указателя или ссылки, а тип объекта, на который указывает объект, не имеет значения.

1 голос
/ 28 января 2011

Здесь необходимо отметить пару дополнительных моментов:

Поиск имени происходит в одной области видимости;Например, при вызове метода для объекта со статическим типом «B» компилятор учитывает интерфейс «B», чтобы определить, существует ли допустимое совпадение.Если нет, он только тогда просматривает интерфейс Base, чтобы найти совпадение.Вот почему, с точки зрения компилятора, нет никакой двусмысленности, и он может разрешить вызов.Если ваш реальный код перегружен и т. Д., Это может быть проблемой.

Во-вторых, часто забывают, что ключевое слово «protected» применяется на уровне класса, а не объекта.Так, например:

class Base {
protected:
    C_Container *   Get_Container(void);
};

class B : public Base{
public:
    C_Container *   Get_Container(void)
    {
        B b;
        // Call the 'protected' base class method on another object.
        return b.Base::Get_Container();
    }
};
1 голос
/ 28 января 2011

Метод сначала ищется в соответствии с статическим типом объекта. Если он не виртуальный, то все готово: это метод, который вызывается. Динамический тип - это то, что используют виртуальные методы, dynamic_cast и typeid, и это «фактический» тип объекта. Статический тип - это то, с чем работает система статических типов.

A a;                       // Static type and dynamic type are identical.
Base &a_base = a;          // Static type is Base; dynamic type is A.

a.Get_Contaienr();         // Calls A::Get_Container.
a_base.Get_Container();    // Calls Base::Get_Container.

B *pb = new B();           // Static type and dynamic type of *pb (the pointed-to
                           // object) are identical.
Base *pb_base = pb;        // Static type is Base; dynamic type is B.

pb->Get_Container();       // Calls B::Get_Container.
pb_base->Get_Container();  // Calls Base::Get_Container.

Выше я предположил, что защищенный метод Base :: Get_Container доступен, иначе это будут ошибки компиляции.

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