【问题标题】:Call base class constructor after the derived class constructor in C++C++派生类构造函数后调用基类构造函数
【发布时间】:2015-08-18 19:28:23
【问题描述】:

考虑以下情况:

#include <iostream>
using namespace std;

class A {
  public:
    int y;
    A(int &x) {
        x = 2;
        y = 1;
    }
};

class B : public A {
  public:
    int *p;
    B(int t) : A(*p) {}
};

int main() {
    B b(2);
    return 0;
}

调用B的构造函数时,p有一个垃圾值。所以,当*p 被传递给A() 时,它给了我一个分段错误。我想在调用A(*p) 之前初始化p = new int; - 这可能吗?

编辑:有趣的是,不带参数调用B 的构造函数并没有给出分段错误。

【问题讨论】:

  • 您必须使用 int*p 从类(在 A 之前)继承才能做到这一点。
  • 这段代码不会给你一个分段错误。
  • 我认为这是一个玩具示例,这就是为什么 B::pint* 而不仅仅是 int
  • @T.C.我做了一个编辑,现在这段代码给出了 Segmentation Fault。
  • 一般来说,需要这样做是不良类设计的标志;看看您是否可以重新设计您的类层次结构,以便在基础构造期间基不必实际修改派生中的数据。

标签: c++ pointers inheritance constructor


【解决方案1】:

这样做的方法是引入另一个结构:

class A {
  public:
    int y;
    A(int &x) {
        x = 2;
        y = 1;
    }
};

struct C
{
    explicit C(int* p) p(p) {}
    int* p;
}

class B : private C, public A {
  public:
    B(int t) : C(new int), A(*p) {}
    ~B() {delete p;}
    B(const B&) = delete;
    B& operator =(const B&) = delete;
};

【讨论】:

    【解决方案2】:

    不,你不能。顺序很好地定义为:

    • 首先,最派生类的构造函数调用虚拟基类子对象的构造函数。虚拟基类按深度优先、从左到右的顺序初始化。
    • 接下来,直接基类子对象按照它们在类定义中声明的顺序构造。
    • 接下来,(非静态)成员子对象按照它们的顺序构造 在类定义中声明。
    • 最后,构造函数的主体被执行。

    如果您发现自己处于某种情况下不得不做这种奇怪的事情,那么是时候重新审查设计了。

    【讨论】:

      【解决方案3】:

      我以另一种方式尝试过。我不知道它是否符合您的要求,也不知道它是否正确。不过看看吧。

      class A
      {
      public:
        int y;
        A() {}
        A(int &x)
        {
          x = 2;
          y = 1;
        }
      };
      
      class B : public A
      {
      public:
        int *p;
        B(int x)  
        {
          p = new int();
          A::A(*p);
        }
      };
      
      int _tmain(int argc, _TCHAR* argv[])
      {
        B b(2);
        return 0;
      }
      

      希望它的正确和帮助。

      注意:- 在我看来,在基本初始化列表中传递初始化指针是不可能的。

      【讨论】:

      • A::A(*p); 不正确。我认为这实际上是非法的,但即使您的意思是A(*p);,这也会创建一个临时对象并立即将其销毁。基类对象都是在进入构造函数体之前构造好的。
      • @MattMcNabb:A::A(*p) 不是非法的。上述代码在 Visual Studio 中正常运行。该调用将被编译器解释为对基类构造函数的特殊调用,并将编译和运行。此外,由于我在堆上分配,因此不能通过显式调用 delete 来销毁它。如果您浏览代码,您会看到我添加了一个默认构造函数来构建主体。要求您在投反对票之前正确检查。请在您的系统中运行代码。
      • 我做了并得到了error: cannot call constructor 'B::A' directly。我相信 gcc 而不是 VS。要获得正确答案,必须阅读 C++ 标准
      • 以下链接确实谈到了它。 [链接]stackoverflow.com/questions/21395395/…可以从派生类调用基类构造函数。这将导致创建另一个对象,但它是合法的。我看到 gcc 有很多奇怪的问题,所以我更信任 VS。
      • 该代码谈论的是Person(2),而不是Person::Person(2)
      【解决方案4】:

      我认为这是不可能的,因为在调用 B() 之前还没有分配 p。在调用构造函数之前,它不会在初始化列表中分配。

      B() : p(new int(10) )  // p is still a junk value here
      {
         // p already has a valid value here
      }
      

      【讨论】:

      • 我不知道这是什么意思,pp(new int(10)) 执行后不是垃圾值
      • p(new int(10) 它只是一个初始值,但在进入构造函数之前不会被赋值
      • p(new int(10)) 执行后立即分配;例如,你可以在它后面加上q(p)
      猜你喜欢
      • 2016-07-19
      • 2018-07-21
      • 2018-07-16
      • 2020-11-28
      • 1970-01-01
      • 1970-01-01
      • 2013-01-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多