面向对象编程,继承和复制构造函数

3
假设我有一个基类“person”。并且我公开从基类“person”继承了一个类“student”。我没有为基类和派生类编写复制构造函数。现在假设我在主程序中编写:
main()
{
student sobj1("name", "computer science");
student sobj2=sobj1;
}

现在在第二行,将调用学生类的默认编译器生成的复制构造函数,但在执行之前,将调用基类的默认复制构造函数,该函数创建一个匿名对象并初始化它,然后控制权返回到学生类的复制构造函数,该函数初始化对象的学生部分。
这是我们不编写复制构造函数的情况下的演示。 现在假设我们为两个类都编写了复制构造函数,我已经测试了当我写下:

student sobj2=sobj1;

发生的情况是,这行代码调用了Student类的复制构造函数(成功执行),但此时基类的复制构造函数不会被调用(默认基类构造函数将被调用),我的问题是为什么?


看起来这是一个C++问题。你是否想加入Java标签? - Tyler
这绝对不是Java语言,第二行不会实例化任何类。 - Andreas Dolk
同时,“publically inherits” 暗示了 C++。 - Douglas Leeder
是的,这是C++,对此很抱歉。 - Zia ur Rahman
3个回答

4

vava几乎已经正确地列出了他的规则,这里是更明确的版本:

  • 在构造派生类子对象之前,不能有派生类实例:所有基类的构造函数都会先执行。(Ctorconstructor的缩写。)
  • 在构造每个类的成员之前,不能进入构造函数的主体:所有成员的构造函数在基类构造函数之后执行
  • 编译器生成的默认构造函数使用默认构造函数用于基类和成员。
  • 编译器生成的复制构造函数进行逐成员复制:使用所有基类和成员的复制构造函数。(Cctorcopy constructor的缩写。)
  • 编译器生成的赋值运算符进行逐成员赋值:使用基类和成员的赋值运算符。(assignment operator也称为operator=op=。)
  • 在自己实现构造函数时(任何构造函数,包括复制构造函数),任何没有初始化参数的基类或成员将使用其默认构造函数(在构造函数初始化程序中,即冒号和构造函数的函数体之前的部分)。
  • 如果数据成员的默认构造函数是平凡的(或多或少是“如果它是编译器生成的”),并且您没有在构造函数初始化程序中指定该成员,则该成员的值将是垃圾。
    • 这指出了这些规则仍然过于简化;请参见C++标准中的12.6.2 / 3-4以获取精美的详细信息。
    • 这仅适用于具有平凡默认构造函数的数据成员。
    • 具有平凡默认构造函数的基类始终是垃圾。

vava关于指定Person的复制构造函数的示例完全正确:

struct Student : Person {
  Student(Student const& other) : Person(other) {}
};

最后一个要点的例子:

struct TrivialCtor { int n; };
struct A {
  TrivialCtor m;
  A() {}          // m.n is garbage
  A(int) : m() {} // m.n is 0

  friend ostream& operator<<(ostream& s, A const& v) {
    s << "m.n = " << v.m.n;
    return s;
  }
};

int main() {
  cout << "()   : " << A() << '\n';
  cout << "(int): " << A(42) << '\n';
  return 0;
}

3
我认为规则如下:
  1. 派生类的构造函数调用之前应始终先调用基类的构造函数。
  2. 您可以通过在初始化列表中显式调用其之一来选择调用哪个基类构造函数。
  3. 如果您不这样做,则会调用默认构造函数。
  4. 当类没有复制构造函数时,编译器会生成一个。它将调用该类所有成员的默认构造函数和基类的复制构造函数,就像您手写的构造函数实际上应该做的那样。
所以,除非您调用基类的复制构造函数,否则将使用默认构造函数,但编译器足够聪明,实际上会在自己生成的复制构造函数中调用它。
如果您不知道如何调用它,这里是一个例子。
Student(Student const & p): Person(p) {
}

0

Roger和vava已经回答了您的技术问题。

然而,您可能现在还不知道,但您不想支持从多态类层次结构中的对象复制。

首先,因为复制实体没有意义。复制学生的含义是什么?没有!

其次,当将学生复制到人员中,然后再返回到学生时,您将不得不解决切片问题。而且,您将不得不使用变通方法来支持赋值--“信封字母习惯用语”是最著名的。

只需通过继承boost::noncopyable之类的类,在基类中抑制复制和赋值(用于C++98/03的纯解决方案),并通过指针处理实体。如果出于某种奇怪的原因,您确实需要复制实体,请查看通常依赖于受保护的复制构造函数的克隆()函数。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接