2011-01-27 7 views
1

Сегодня я нашел следующее волнующе неоднозначной ситуации в нашей базе кода:Как узнать, какая функция будет вызвана?

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++ для определения того, какую версию функции она должна вызывать? Я хотел бы начать с «известного состояния» того, что должно было получиться, и затем выяснить, что там происходит.

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

Спасибо.

+0

Если у вас есть '= 0;' в базовом классе, как вы его написали, он не будет компилироваться. '= 0' может использоваться только с использованием виртуальных методов. – wheaties

+0

Спасибо, я забыл виртуальный там (и возвращаемый тип, oops). – Colen

ответ

5

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

3

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

1

Метод сначала проверяется согласно статическому типу объекта . Если он не виртуальный, вы закончили: это вызванный метод. Динамический тип - это то, что используют виртуальные методы, 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 доступен, в противном случае это будут ошибки компиляции.

1

Несколько дополнительных пунктов здесь отметить:

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

Во-вторых, часто забывают, что ключевое слово '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(); 
    } 
};