02-3 拷贝构造函数的调用时机
在 c++中使用拷贝构造函数的时机通常有以下三个:
- 使用一个已经创建完毕的对象来初始化一个新的对象(常用)
- 值传递的方法给函数的参数传值
- 以值方式返回局部对象
我们先创建一个类用于演示:
class Person
{
private:
int m_age;
public:
Person()
{
cout << "这是 Person 的默认构造函数调用。" << endl;
}
Person(int age)
{
m_age = age;
cout << "这是 Person 的有参构造函数调用。" << endl;
}
Person(const class Person& p)
{
m_age = p.m_age;
cout << "这是 Person 的拷贝构造函数调用。" << endl;
}
~Person()
{
cout << "这是 Person 的析构函数调用。" << endl;
}
};
在这个类Person
之中,只有一个变量m_age
,之后我们创建了默认构造函数、有参构造函数、拷贝构造函数和一个析构函数(析构函数有且只有一个)。
注意,之后的代码中会出现system("pause");
,这是 windows 环境下特有的,目的是暂停程序。当运行到这一行的时候,调试会显示请按任意键继续. . .
的字样。(这个在最开始的笔记已经补上了)
-
使用一个已经创建完毕的对象来初始化一个新的对象
void main() { class Person p1(20); class Person p2(p1); system("pause"); }
运行结果:(环境:Windows11(arm/Apple M VM)/Visual Studio 2022/Debug/arm64)
这是 Person 的有参构造函数调用。 这是 Person 的拷贝构造函数调用。 请按任意键继续. . . 这是 Person 的析构函数调用。 这是 Person 的析构函数调用。
在这里,我们创建了一个对象
p1
,p1
的创建是使用有参构造函数,也就是Person(int age)
。这里面会传入一个age
,在Person(int age)
中,会把传入的age
赋值给类内对象m_age
。之后会使用拷贝构造函数,将
p1
传入,将p1
身上的m_age
赋值给新创建的对象(也就是p2
)的m_age
。所以这是使用已经创建好的对象来初始化一个新对象。当然这也是最常用的调用时机。
-
值传递的方法给函数的参数传值
void func1(class Person p); void main() { class Person p1; func1(p1); system("pause"); } void func1(class Person p) { cout << "这是一个简单的打印" << endl; }
这边我们创建了一个函数
func1
,它的传入参数是class Person
。本来打算空实现的但是还是在func1
中加上了一行打印命令。调用
func1
,需要传入的是class Person
。这是一个值传递。值传递的本质是将传入的内容拷贝出一份完全一样的值。在实参传递给形参的时候会调用一下拷贝构造函数。按照上面的例子,传入的是
p1
,调用func1
的时候会再次创建出一个class Person p
,而创建这个对象使用的是拷贝构造。运行结果:(环境:Windows11(arm/Apple M VM)/Visual Studio 2022/Debug/arm64)
这是 Person 的默认构造函数调用。 这是 Person 的拷贝构造函数调用。 这是一个简单的打印 这是 Person 的析构函数调用。 请按任意键继续. . . 这是 Person 的析构函数调用。
为什么在“请按任意键继续. . .”前会出现一次“这是 Person 的析构函数调用。”?这是因为函数占用的内存存放在栈区,栈区数据在函数运行结束后自动删除,而删除的是一个
class Person
的对象,所以删除的时候会调用一下析构函数。 -
以值方式返回局部对象
class Person func2(); void main() { class Person p = func2(); system("pause"); } class Person func2() { class Person p1; return p1; }
运行结果:(环境:Windows11(arm/Apple M VM)/Visual Studio 2022/Debug/arm64)
这是 Person 的默认构造函数调用。 这是 Person 的拷贝构造函数调用。 这是 Person 的析构函数调用。 请按任意键继续. . . 这是 Person 的析构函数调用。
这边定义了一个函数
func2
,这个函数将会返回一个class Person
的对象。我们在func2
中创建了一个p1
并且将这个p1
作为返回值返回出来。之后在main
函数中,将func2
的返回值赋值给p
。在
func2
返回的时候,返回的是一个值,不会返回p1
,会拷贝一个新的对象返回给p
。当然可能我这边编译器有点问题,无法显示出两个,所以可以这样试一下:
class Person func2(); void main() { class Person p = func2(); cout << p.m_age << endl; system("pause"); } class Person func2() { class Person p1(10); return p1; }
p.m_age
可以正确的被打印,所以这是正确的。
:-)