我如何知道哪个函数会被调用?

问题描述:

今天,我发现了以下令人不安的暧昧情况在我们的代码库:我如何知道哪个函数会被调用?

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怎么样?

谢谢。

+0

如果您已经在基类中使用`= 0;`来编写它,它将不会编译。 `= 0`只能用于纯虚拟方法。 – wheaties 2011-01-27 23:16:48

这取决于你如何调用函数。如果您通过A *A &A拨打电话,则您将拨打A::Get_Container()。如果您通过Base *Base &(即使他们指向/参考A)拨打电话,则您将拨打Base::Get_Container()

只要不存在虚拟继承,这很容易。如果您直接使用对象,则会调用该对象的方法;如果您正在使用指针或引用,则它是确定方法的指针或引用的类型,并且指向的对象的类型无关紧要。

首先根据对象的静态类型查找方法。如果它是非虚拟的,那么你就完成了:这就是所谓的方法。动态类型是虚拟方法,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方法是可访问的,否则那些将是编译错误。

这里需要注意几点:

名称查找发生在单个作用域中;例如。在静态类型为'B'的对象上调用该方法时,编译器会认为'B'的接口确定是否存在有效的匹配。如果没有,它只会看基地的接口来找到一个匹配。这就是为什么从编译器的角度来看,没有任何不明之处,它可以解决这个问题。如果你的真实代码有重载等,这可能是一个问题。其次,经常忘记'受保护的'关键字适用于类而不是对象级。例如:

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(); 
    } 
};