今天我在代码库中发现了以下令人不安的模糊情况:
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,我认为它只会调用该函数的Base版本。如果我有指向A的指针怎么办?指向B的指针怎么样?堆上的A或B怎么样?
感谢。
答案 0 :(得分:5)
这取决于你如何调用该函数。如果您通过A *
,A &
或A
来电话,那么您将拨打A::Get_Container()
。如果您通过Base *
拨打Base &
(即使他们指向/引用A
),您也会拨打Base::Get_Container()
。
答案 1 :(得分:3)
只要没有虚拟继承,就很容易了。如果你直接使用一个对象,它就是被调用的对象的方法;如果您正在使用指针或引用,则它是确定方法的指针或引用的类型,并且指向的对象的类型无关紧要。
答案 2 :(得分:1)
首先根据对象的静态类型查找方法。如果它是非虚拟的,那么你就完成了:那就是被调用的方法。动态类型是virtual methods,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方法,否则会出现编译错误。
答案 3 :(得分:1)
这里还要注意几点:
名称查找发生在单个范围内;例如。在对静态类型为“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();
}
};