内容纲要
拷贝构造函数调用时机
C++ 中拷贝构造函数调用时机通常有三种情况:
- 使用一个已经创建完毕的对象来初始化一个新对象
- 值传递的方式给函数参数传值
- 以值方式返回局部对象
详细笔记请看下方代码
笔记全在代码之中!!
- 注意:
值传递的时候是生成副本,会(自动)调用拷贝函数。
返回值时,也是返回副本,也会(自动)调用拷贝函数。
#include <iostream>
using namespace std;
class Person{
public:
Person() {
cout << "Person 默认构造函数被调用~" << endl;
}
Person(int x) {
m_Age = x;
cout << " Person 有参构造函数被调用~~ age = " << m_Age << endl;
}
Person(const Person& p) {
m_Age = p.m_Age;
cout << " Person 拷贝构造函数被调用~~~ age = " << m_Age << endl;
}
int m_Age;
~Person() {
cout << "~~析构函数被调用~~~喵~~~" << endl;
}
};
class Person2 { //测试当一个函数销毁时,多个对象的析构函数 是否有先后执行顺序。
public:
Person2() {
cout << "Person2 默认构造函数被调用~" << endl;
}
~Person2() {
cout << "Person2 析构函数被调用" << endl;
}
};
//拷贝构造函数调用时机
//1.使用一个已经创建完毕的对象来初始化一个新对象
void test01() {
Person p2(10);
Person p3(p2); //使用已经创建好的对象 来初始化另外一个新对象。
}
//2.值传递的方式给函数参数传值
void doWork2(Person p) { //值传递 是会将值生成一个副本,在局部变量中修改其内容,并不会影响它本身。
}
void test02() {
Person p1;
doWork2(p1); //这里会拷贝一份,我个人理解就是 doWork(Person p = p1){ } 在这里执行的显式调用拷贝构造函数。
//如果将其拷贝构造函数注释掉,test02() 函数执行完之后依旧会调用 两次 析构函数。
}
//3.值方式返回局部对象。
Person doWork03() {
Person p1(10);
cout << "p1的地址为: " << (int*)&p1 << endl;
return p1; //返回时会拷贝它自己一份。
}
void test03() {
Person p = doWork03();
//doWork03(); //如果没人接收它,它就会被系统立即回收。。可以用析构函数出现的位置来证明。
cout << "p的地址为: " << (int*)&p << endl; //这里的地址和 doWork03() 创建的对象 地址不一样!!!
}
void main() {
test03();
}
构造函数调用规则
默认情况下,c++ 编译器至少给一个类添加3 个函数。
- 默认构造函数(无参,函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝构造函数,对属性经行值拷贝
构造函数调用规则:
- 如果用户定义有参构造函数,C++ 不再提供默认无参构造,但会提供默认拷贝构造。
- 如果用户定义拷贝构造函数,C++ 不会再提供其它构造函数(包括无参构造函数)
深拷贝、浅拷贝
浅拷贝:简单的赋值拷贝操作。
深拷贝:在堆区重新申请空间,进行拷贝操作
- 浅拷贝问题点。
//下方代码运行时会报错。因为析构函数将浅拷贝的内存地址西沟了,但是在拷贝函数中再析构一次时,对象已经置空,就会报错。
#include <iostream>
using namespace std;
class Student
{
public:
Student() {
cout << "Student默认构造函数被调用" << endl;
}
Student(int age,int Height) {
m_Height = new int(Height);
m_age = age;
cout << "Student有参构造器被调用" << endl;
cout << "Stu的年龄为: " << m_age << endl <<
"Stu年龄的地址为:" << (int*)m_age << endl;
}
Student(const Student& stu) { //拷贝构造函数
m_Height = stu.m_Height;
m_age = stu.m_age;
cout << "Student拷贝构造函数被调用" << endl;
}
~Student() {
if (m_Height != NULL)
{
delete(m_Height);
m_Height = NULL;
}
cout << "Student析构函数被调用" << endl;
}
int m_age;
int* m_Height;
};
void test() {
Student stu(18,160);
Student stu_copy(stu);
cout << "stu_copy 的年龄为:" << stu_copy.m_age << endl <<
"stu_copy中年龄的地址为:" << (int*)stu_copy.m_age << endl;
}
void main() {
test();
}
//析构代码:养成一个好习惯,在堆中开辟的空间要主动释放,并将对象置空。
~Student() {
if (m_Height != NULL)
{
delete(m_Height);
m_Height = NULL;
}
cout << "Student析构函数被调用" << endl;
}
- 问题点解决办法:使用深拷贝来解决。
总结:如果有属性需要在堆中开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题。
初始化列表
作用:
C++ 提供了初始化列表语法,用来初始化属性
语法:构造函数():属性1(值1),属性2(值2)...{}