#include<iostream>
#include<string>
using namespace std;
class Phone { //手机类
public:
string m_PName;
Phone() {};
Phone(string pName) {
m_PName = pName;
cout <<"Phone类的构造函数被调用" << endl;
}
~Phone() {
cout << "Phone类的析构函数的调用" << endl;
}
};
class Person { //人类
public:
//手机
string m_Name;
//手机
Phone m_Phone;
Person(string name, string pName) {
m_Name = name;
m_Phone = pName;
cout << "Person类的构造函数被调用" << endl;
}
~Person() {
cout << "Person类的析构函数被调用" << endl;
}
};
void test01() {
Person p=Person("张三","苹果");
cout << "m_Name:" << p.m_Name << "m_Phone:" << p.m_Phone.m_PName << endl;
}
int main() {
test01();
}
由上述代码,我们可以得到以下结果:

从上图中,我们可以看出,Phone类的析构函数被调用了两次。
让我们来分析上述代码:
void test01() {
Person p = Person("张三", "苹果");
cout << "m_Name:" << p.m_Name << "m_Phone:" << p.m_Phone.m_PName << endl;
}
在这个函数中,我创建了一个 Person 对象 p,并通过 Person("张三", "苹果") 的方式进行初始化。这实际上会发生两个步骤:
-
构造
通过Phone对象:Phone("苹果")构造一个临时的Phone对象,然后拷贝到m_Phone成员中。 -
构造
Person对象:Person对象p的构造函数被调用,其中包括m_Phone成员的构造。
因此,这里会调用两次 Phone 类的构造函数。第一次是创建临时对象,第二次是在 Person 对象中构造 m_Phone。
接下来,当 test01 函数执行完毕时,Person 对象 p 超出了作用域,导致 Person 类的析构函数被调用。由于 Person 类中有一个 Phone 类的成员 m_Phone,这也导致了 Phone 类的析构函数被调用。
因此,Phone 类的析构函数被调用两次是因为它在两个地方被构造(test01 函数内和 Person 类内),相应地也被析构两次。
但,当我将“Person类的构造函数”写成以下形式:
Person(string name, string pName) : m_Name(name), m_Phone(pName) {
cout << "Person类的构造函数被调用" << endl;
}
可以得到以下结果:

从上图中,我们可以看出,Phone类的析构函数只被调用了一次。
为什么? 让我们来分析,在C++中,初始化列表和在构造函数里赋值有什么区别,就可以得出该结论。
初始化列表:在构造函数的头部,通过成员初始化列表对类的成员进行初始化。
这是在构造函数体执行之前完成的。成员初始化列表直接在对象创建时初始化成员,避免了先调用默认构造函数,再进行赋值的开销。
在构造函数里赋值:
在构造函数体内,通过赋值语句来给成员变量赋初值。这是在对象创建后,构造函数体执行时进行的。
如果成员是一个类对象,这种方式会调用该类对象的默认构造函数,然后再通过赋值操作进行初始化。
本文通过C++代码示例解释了构造函数和析构函数在创建和销毁对象时的调用情况,特别关注了初始化列表与构造函数内赋值的区别,指出前者仅构造一次,后者可能因嵌套类而构造多次。

被折叠的 条评论
为什么被折叠?



