作为 class 数据成员的对象,使用带和不带初始化列表的默认构造函数

objects as class data members using default constructor with and without initializer list

以下节目

#include <iostream>
using namespace std;

class university{
private:
    string uni;
public:
    university(){
        cout<<"default constructor of university is invoked"<<endl;
    }
    university(string u){
        uni =u;
        cout<<"parametrized constructor of university is invoked: "<<uni;
    }
};

class student{
private:
    university u;
public:
    student() {
        u = university("ABC");
    }
};

int main()
{
    student s;

    return 0;
}

输出是:

default constructor of university is invoked
parametrized constructor of university is invoked: ABC

但是当更改 student class 的构造函数以使用如下初始化列表时:

    student(): u(university("ABC")){

    }

输出是:

parametrized constructor of university is invoked: ABC

我的问题是: 对于第二种情况,当编译器在 student class 的私有部分运行 university u 行时,它是否创建了 class 'university' 的对象 'u' 和为其分配内存地址,还是在初始化列表中创建对象?如果是前者,那为什么不调用默认构造函数呢?

对于第一种情况,我有同样的问题,对象是在哪里创建并分配内存位置的。

“分配内存”和“分配内存位置”与此处无关。您问的是如何构建作为另一个 class 成员的对象。试图在这里引入内存分配的主题只会使事情变得混乱。高级 C++ 技术允许“在同一位置”重复构造和销毁对象(通过 placement new 和显式析构函数调用)。实际内存位置和对象 constructions/destructions 完全无关紧要。它们彼此无关,如果您只是忘记有关内存位置的所有内容,而只关注对象构造和销毁的主题,这将很有帮助。说了这么多:

对象构造函数负责构造对象的所有成员。这是规则。没有例外。句号。

student(): u(university("ABC")){

这会奏效,但在令人困惑的成员中也会这样做。这阐明了以下事件顺序:构造临时 university 对象,然后 copy-constructs student class 的实际成员,最后临时对象被销毁。这是不必要的,只会把水搅浑。使用更简单、现代的 C++ 语法,这应该是:

student(): u{"ABC"}{

这种更短、更直接的语法以简单、简洁的方式准确地描述了正在发生的事情:对象的构造函数构造了该对象的所有成员,即这里只有一个成员。它被称为uu 的构造函数被调用,将一个字符串作为参数传递给它。 university 的“参数化”构造函数被调用。构造函数的成员初始化部分只不过是一个简单的列表:这里是我的 class 成员,这里是它们各自构造函数的参数。 Abracadabra:它们是构造出来的。

student() {

如果构造函数没有成员初始化部分,或者没有在初始化部分列出对象的成员,则会调用相应的 class 成员的默认构造函数。

就是这样,就是这样:你的 class *这里 class 被称为 student) 有成员,class 的构造函数显式初始化它们,为 class 成员调用相应的构造函数。如果任何 class 成员未在初始化部分中列出,则调用其默认构造函数,如果 class 成员没有默认构造函数,则生成的代码为 ill-formed 并且不会编译。

成员没有在构造函数的主体中初始化。它们在构造函数主体执行之前被初始化。当你写:

class student{
private:
    university u;
public:
    student() {
        u = university("ABC");
    }
};

u = unisversizy("ABC");不是初始化。由于您没有为 u 成员提供初始化程序,它将通过调用其默认构造函数进行初始化。然后在构造函数中 university("ABC") 通过调用另一个构造函数创建另一个对象并将其分配给 u.

PS:在我看来,“参数化构造函数”这个术语是一个非常具有误导性的术语。 “参数化构造函数”和教程中经常出现的默认构造函数之间的区别是错误的。默认构造函数是可以不带参数调用的构造函数,因此下面是默认构造函数(并且可以带参数调用):

 struct foo {
      foo(int x = 0) {} // a default constructor
                        // is this a parametrized constructor ?!?
 };

案例一

这里我们考虑的情况是:

student() {
        u = university("ABC"); //this is assignment not initialization
    }

当你写道:

student s; 

以上语句的效果如下:

  1. 调用 class student 的默认构造函数 student::student()
  2. Before entering/executing 默认构造函数 student::student() 的主体,class [=23] 的默认构造函数 university::university() =] 是 隐式调用 。结果,您得到输出:
default constructor of university is invoked
  1. 现在一旦 university::university() 的主体完全执行,默认 ctor student::student() 的主体的执行是 resumed.

  2. 在执行student::student()的主体时遇到以下语句:

u = university("ABC");

这是一个赋值,而不是初始化。由于表达式 univerisity("ABC"),使用 class university 参数化构造函数 创建了一个临时 university 对象,因此您得到输出:

parametrized constructor of university is invoked: ABC
  1. 上一步创建的临时文件赋值给数据成员u

案例二

这里我们考虑的情况是:

student(): u(university("ABC")){

    }

当你写道:

student s;

事情是这样的:

  1. 调用了 class student 的默认构造函数
  2. 进入默认构造函数student::student()之前,参数化构造函数university::university(std::string)的class university 被调用是因为你有表达式 university("ABC") 并且数据成员 u 初始化 。请注意,由于调用了参数化的 ctor,您将获得输出:
default constructor of university is invoked
  1. 现在,当参数化构造函数的主体完成时,class student::student() 的默认构造函数主体的执行 student 恢复 .

备注

在您的示例的情况 2 中,您应该将 student():u(university("ABC")){} 更改为:

student(): u("ABC"){

    }