C++ 11 个委托构造函数 纯虚拟方法和函数调用 -- 危险?
C++ 11 Delegated Constructor Pure Virtual Method & Function Calls -- Dangers?
不是从构造函数调用虚拟函数和纯虚拟函数的重复:
前一个问题与C++03有关,而不是C++11中新的构造函数委派行为,该问题没有解决通过使用委派来缓解未定义行为的问题,以确保在执行纯虚拟实现之前进行正确的构建
在C++11中,在类的构造函数中,在构造过程中,但在通过构造函数委托"完全构造"了类/对象之后,调用纯虚拟函数有什么危险?
显然,在C++11规范的某个地方存在这样的约束,
会员功能(包括虚拟会员功能,10.3)可以要求建造一个物体。类似地,下面的对象构造可以是typeid运算符的操作数。。-12.6.2[C++工作草案]第13条(http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3242.pdf)找不到已发布规范的"合理使用"版本。
C++11考虑在任何构造函数完成后构造的对象处决由于将允许执行多个构造函数,这意味着每个委托构造函数都将在完全构造的自己类型的对象。派生类构造函数将在其基类中的所有委派完成后执行。-维基百科说这是一个C++11的东西。
实际的C++11引用未知。
以下示例在Visual Studio 2012 C++编译器的11月CTP中编译和运行:
#include <string>
/**************************************/
class Base
{
public:
int sum;
virtual int Do() = 0;
void Initialize()
{
Do();
}
Base()
{
}
};
/**************************************/
// Optionally declare class as "final" to avoid
// issues with further sub-derivations.
class Derived final : public Base
{
public:
virtual int Do() override final
{
sum = 0 ? 1 : sum;
return sum / 2 ; // .5 if not already set.
}
Derived(const std::string & test)
: Derived() // Ensure "this" object is constructed.
{
Initialize(); // Call Pure Virtual Method.
}
Derived()
: Base()
{
// Effectively Instantiating the Base Class.
// Then Instantiating This.
// The the target constructor completes.
}
};
/********************************************************************/
int main(int args, char* argv[])
{
Derived d;
return 0;
}
随着更新,示例代码对我来说还可以,但需要注意的是,如果您创建了Derived的子类,则Derived(conststd::string&)不会调用该子类对Do()的覆盖,相反,Derived::Do(;)仍然会被调用;这可能不是你想要的。特别是,当从Derived(const std::string&)构造函数调用Initialize()时,该对象仍然"只是"一个Derived对象,而不是SubDerived对象(因为构造代码的SubDerived层尚未启动),这就是为什么要调用Derived::Do()而不是SubDerive::Do)。
Q: 如果子类使用相同的委托模式来确保所有东西都以相同的方式实例化,该怎么办?
A: 这基本上是可行的,但前提是可以在调用SubDerive::Do()之前调用Derived::Do)。
特别是,假设您有一个类SubDerived,它做了与上面Derived相同的事情。然后当调用代码这样做时:
SubDerived foo("Hello");
将出现以下调用序列:
Base()
Derived()
Derived(const std::string &)
Base::Initialize()
Derived::Do()
SubDerived()
SubDerived(const std::string &)
Base::Initialize()
SubDerived::Do()
因此,是的,SubDerived::Do()最终会被调用,但Derived:::Do()也会被调用。这是否会成为一个问题取决于各种Do()方法的实际作用
一些建议:从构造函数中调用虚拟方法通常不是最好的方法。您可能需要考虑简单地要求调用代码在构建对象后手动调用对象上的Do()。调用代码需要做更多的工作,但优点是可以避免在对部分构建的对象进行虚拟方法调用时出现的不太明显或不方便的语义。
在典型的单构造函数继承场景中,UB调用基构造函数中的纯虚拟函数:
[C++11: 10.4/6]:
成员函数可以从抽象类的构造函数(或析构函数)调用;从这样的构造函数(或析构函数)直接或间接地为正在创建(或销毁)的对象对纯虚拟函数进行虚拟调用(10.3)的效果是未定义的。
struct Base
{
Base()
{
foo(); // UB
}
virtual void foo() = 0;
};
struct Derived : Base
{
virtual void foo() {}
};
对于在委托构造函数调用中进行的此类调用,这里没有任何豁免,因为在这一点上,对象的派生程度更高的部分仍然没有被构造。
struct Base
{
Base()
{
foo(); // still UB
}
Base(int) : Base() {};
virtual void foo() = 0;
};
struct Derived : Base
{
virtual void foo() {}
};
这是你引用的维基百科文章:
C++11考虑在任何构造函数完成执行后构造的对象由于将允许执行多个构造函数,这意味着每个委托构造函数将在其自己类型的完全构造的对象上执行。派生类构造函数将在其基类中的所有委托完成后执行
关键是第二个粗体的句子,而不是第一个,因为人们可能会一眼就误解。
但是,您发布的代码片段很好,这是因为派生的构造函数主体正在执行,而不是已经完全构造好的抽象类的构造函数。也就是说,你必须要求证明它是安全的,这应该表明这不是最具表现力或直观的方法,我会在你的设计中尽量避免它。
- 获取从C++中同一类中的构造函数调用的方法返回的值
- C++11:模板方法的模板函数调用无法编译?
- 静态 std::map instatiation 在类的方法中调用构造函数吗?
- 使用回调函数从构造函数调用虚拟/派生方法的替代方法?
- 有没有一种简单的方法来调用带有默认参数的函数?
- C++:这是使用整数变量作为函数调用指针的正确方法吗
- 在 MySQL 连接器C++ API 中使用一个函数调用执行多个查询的正确方法是什么?
- 是否有一种方法可以调用一个函数,而不会创建变量,而不会创建变量
- 有没有一种单行方法来调用集合上的 lambda 函数
- 使用 emplace_back 避免移动构造函数调用的最佳方法?
- 如何从具有多个对象/字段的类中调用方法函数
- 简化静态成员函数调用的方法
- 对静态重载(类)函数/方法的调用是不明确的
- 在 C++ vs Java 中从构造函数调用被覆盖的方法
- 从C++中的虚拟析构函数调用虚拟方法
- 在类方法内调用lambda函数中的类方法
- 子类中的方法不会被父类中的虚函数调用
- 在其赋值运算符方法中调用对象的析构函数
- 默认参数允许构造函数调用私有方法
- 调试可视化工具在预览中使用成员方法/函数调用