首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C++ 11委托构造器纯虚拟方法和函数调用--危险?

C++ 11委托构造器纯虚拟方法和函数调用--危险?
EN

Stack Overflow用户
提问于 2013-02-04 06:18:10
回答 2查看 4.5K关注 0票数 14

不是https://stackoverflow.com/questions/8642363/invoking-virtual-function-and-pure-virtual-function-from-a-constructor的副本

前一个问题与C++ 03有关,而不是C++ 11中新的构造器委托行为,并且该问题没有解决通过使用委托来确保在执行纯虚拟实现之前正确构造而减轻未定义行为的问题。

在C++ 11中,在构造过程中在类的构造函数中调用纯虚拟函数的危险是什么,但是在类/对象已经通过构造函数委托“完全构建”之后呢?

显然,在C++ 11规范的某个地方存在这样的约束,

可以为正在构建的对象调用成员函数(包括虚拟成员函数,10.3)。类似地,正在构造的对象可以是typeid操作符的操作数。- C++工作草案的12.6.2 #13无法找到已发布的规范的“合理使用”版本。 C++11考虑在任何构造函数完成执行后构造的对象。由于将允许执行多个构造函数,这意味着每个委托构造函数都将在其自己类型的完整构造对象上执行。派生类构造函数将在基类中的所有委托完成后执行。- 维基百科说这是C++ 11。

实际C++ 11引用未知。

下面的示例在Visual 2012 C++编译器的Nov中编译和运行:

代码语言:javascript
复制
#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;
}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-02-04 07:18:14

对于这些更新,示例代码在我看来是可以的,但请注意,如果您曾经对派生生成一个子类,那么派生(const std::string &)将不会调用子类Do(),而派生::Do()仍然会被调用;这可能不是您想要的。特别是,当从派生的构造函数(const std::string &)调用Initialize()时,对象仍然是“仅”一个派生对象,而不是SubDerived对象(因为构造代码的SubDerived层尚未启动),这就是为什么要调用派生::Do()而不是Sub派生::Do()。

问:如果子类使用相同的委托模式来确保一切都以相同的方式实例化,该怎么办?

答:大多数情况下,这是可行的,但前提是派生::Do()被调用之前被调用::Do()。

特别是,假设您有类SubDerived,它所做的事情与上面的派生操作相同。然后当调用代码执行以下操作时:

代码语言:javascript
复制
SubDerived foo("Hello");

将发生以下调用顺序:

代码语言:javascript
复制
Base()
Derived()
Derived(const std::string &)
  Base::Initialize()
    Derived::Do()
SubDerived()
SubDerived(const std::string &)
  Base::Initialize()
    SubDerived::Do()

..。是的,Sub派生::Do()最终会被调用,但是派生的::Do()也会被调用。这是否会成为一个问题取决于各种Do()方法实际执行的是什么。

一些建议:从构造函数中调用虚拟方法通常不是最好的方法。在构造对象之后,您可能需要简单地要求调用代码对对象手动调用Do()。对于调用代码来说,这需要做更多的工作,但优点是您可以避免在对部分构造的对象执行虚拟方法调用时使用的不太明显或方便的语义。

票数 8
EN

Stack Overflow用户

发布于 2013-02-04 07:17:50

在典型的单构造函数继承场景中,在基本构造函数中调用纯虚拟函数是UB:

可以从抽象类的构造函数(或析构函数)调用[C++11: 10.4/6]:成员函数;直接或间接地为从此类构造函数(或析构函数)创建(或销毁)对象的对象进行虚拟调用(10.3)的效果未定义。

代码语言:javascript
复制
struct Base
{
   Base()
   {
      foo();  // UB
   }

   virtual void foo() = 0;
};

struct Derived : Base
{
   virtual void foo() {}
};

这里不允许在委托构造函数调用中进行这样的调用,因为此时对象中派生更多的部分仍未被构造。

代码语言:javascript
复制
struct Base
{
   Base()
   {
      foo();  // still UB
   }

   Base(int) : Base() {};

   virtual void foo() = 0;
};

struct Derived : Base
{
   virtual void foo() {}
};

这是你引用的维基百科文章:

C++11考虑在任何构造函数完成执行后构造的对象。由于将允许多个构造函数执行,这意味着每个委托构造函数都将在其自身类型的完全构造对象上执行。派生类构造函数将在基类中的所有委托完成后执行。

关键是第二个粗体句子,而不是第一个,因为一个人可能会误解从一个快速的一瞥。

However,发布的代码片段是很好的,这是因为派生的构造函数体正在执行,而不是抽象类的构造函数,抽象类已经被完全构造。尽管如此,您必须要求提供安全的证据这一事实表明,这并不是最具表现力或最直观的方法,我将在您的设计中尽量避免这种做法。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14681349

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档