-->

C ++ 11委派的构造纯虚拟方法和函数调用 - 危险?(C++ 11 Delegated Cons

2019-07-19 05:33发布

不是的复制从构造函数调用虚函数和纯虚函数 :

前一个问题涉及到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/论文/ 2011 / n3242.pdf )找不到发布规格的“合理使用”的版本。

C ++ 11认为构造一旦对象的任何构造完成执行。 由于多个构造将被允许执行,这将意味着每个代表构造函数将其自己的类型的全构造的对象上执行。 派生类的构造函数将执行后,在其基类的所有代表团完成。 - 维基百科说,这是一个C ++ 11的事情。

实际C ++ 11参考未知的。

以下示例编译并运行在Visual Studio 2012 C ++编译器的十一月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;
}

Answer 1:

随着更新,示例代码看起来还好我,需要提醒的是,如果你曾经做衍生的子类,但()的子类的覆盖将不会被派生(常量的std :: string&)调用,而派生:: DO()仍然会被调用; 这可能不是你想要的。 特别是,当初始化()从衍生(常量的std :: string&)构造调用时,对象仍是“唯一”的一个派生对象,而不是一个对象SubDerived还(因为建筑代码SubDerived层具有不还没开始),这就是为什么派生:: DO()将被调用,而不是SubDerived :: DO()。

问:如果在子类中使用相同的委托模式,以确保一切都在以同样的方式被实例化?

答:这将主要工作,但只有当它的好了衍生:: DO()被SubDerived :: DO()被调用之前调用。

特别是,说你有这样做的派生同样的事情类SubDerived上面一样。 然后,当调用代码这样做:

SubDerived foo("Hello");

会发生以下调用序列:

Base()
Derived()
Derived(const std::string &)
  Base::Initialize()
    Derived::Do()
SubDerived()
SubDerived(const std::string &)
  Base::Initialize()
    SubDerived::Do()

...所以,是的,SubDerived :: DO()最终会被调用,但是派生:: DO()将已经也叫。 无论这是否将是一个问题取决于什么不同DO()方法实际上做。

一些建议:从调用构造函数中的虚方法通常不是最好的一段路要走。 你可能要考虑只简单地要求调用代码的对象被创建后手动呼叫执行()的对象。 它是调用代码更多的工作,但好处是,它可以避开不是非常明显,或-方便语义做虚拟方法调用部分构造的对象时发挥作用。



Answer 2:

在一个典型的单构造继承方案中,它是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++ 11 Delegated Constructor Pure Virtual Method & Function Calls — Dangers?