根据标准,我们知道构造函数总是会在内部对其进行虚函数的早期绑定,因为它们对派生类层次结构的缺点尚不完全了解。
在这种情况下,如果在我的基本构造函数中使用了早期绑定,那么我已经将派生对象传递给了完全可以接受的基类指针(此处是向上转换)。如果使用早期绑定,则虚函数的选择应基于指针的类型(此处为Base *),而不是指针的内容(指针所指向的对象,因为我们不知道确切的对象)被指出)。在那种情况下,由于指针类型为Base *,在两种情况下我们都应该仅调用Base类虚拟函数。有人可以澄清一下吗?
我认为这里使用动态绑定,而不是早期绑定。如果我的理解错误,请纠正我。
调用base的输出的第一行是完全可以的
class Base
{
public:
Base(){
fun();
}
Base(Base *p)
{
p->fun();
}
virtual void fun()
{
cout<<"In Base"<<endl;
}
};
class Derived : public Base
{
public:
void fun()
{
cout<<"In Derived"<<endl;
}
};
int main()
{
Derived d;
Base b(&d);
}
O / P:
In Base
In Derived
答案 0 :(得分:1)
构造函数体内的虚拟调用规则适用于当前正在构造的对象,因为该对象尚未被视为任何派生类的对象。 (对于当前被破坏的对象也有类似的规则。)从使用编译时类型(例如语法ClassName::member_func()
force)的意义上讲,它与“早期绑定”没有任何关系。
您的代码具有两个不同的对象d
和b
,并且d
的构造函数在到达p->fun();
行时已完全完成。
详细信息:
main
。d
的隐式声明的默认构造函数创建对象Derived
。Derived::Derived()
要做的第一件事是通过调用默认构造函数Base::Base()
创建基类子对象。Base::Base()
的主体调用fun()
。由于我们尚未输入Derived::Derived()
构造函数的主体,因此此虚拟查找将调用Base::fun()
。Base::Base()
完成。Derived::Derived()
的正文为空,执行并结束。main
,通过将指向b
的指针传递给构造函数d
来创建对象Base::Base(Base*)
。Base::Base(Base *p)
的主体调用p->fun()
。由于p
是指向d
的指针,Derived
已经是类型为Derived::fun()
的完全构造的对象,因此此虚拟查找将调用Derived
。与此示例稍有不同,我们定义了一个默认构造函数this
,以将Base*
(隐式转换为Base
)传递给Base
子对象的构造函数。 (这是有效的,但是如果以其他方式使用指针(例如在#include <iostream>
using std::cout;
using std::endl;
class Base
{
public:
Base(){
fun();
}
Base(Base *p)
{
p->fun();
}
virtual void fun()
{
cout<<"In Base"<<endl;
}
};
class Derived
{
public:
Derived() : Base(this) {}
virtual void fun() override
{
cout << "In Derived" << endl;
}
};
int main()
{
Derived d;
}
的基或成员的初始化程序中使用指针可能会带来风险)。
Base::Base(Base *p)
该程序将仅打印“在基础中”,因为现在在p
中,RAILS_MASTER_KEY
确实指向当前正在构造的同一对象。
答案 1 :(得分:0)
原因是C ++类是从基类构造到派生类的,并且在对象创建过程完成时会创建完整对象的虚拟调用表。因此,在上面的代码摘录中调用了基类函数。除非另有规定,否则永远不要在构造函数中进行虚函数调用。以下是Bjarne Stroustrup's C++ Style and Technique FAQ的摘录:
在构造函数中,虚拟调用机制被禁用,因为 派生类的替代尚未发生。对象是从头开始构造的,即“派生之前的基础”。
销毁是在“基础类之前的派生类”完成的,因此是虚拟的 函数的行为与构造函数相同:仅使用本地定义– 并且不会调用覆盖函数来避免触摸(现在 销毁)对象的派生类部分。