一文详解构造函数和析构函数
一、对象的初始化和清理
C++中面向对象的思想来源于现实,是对现实事物的抽象模拟,具体来说,当我们创建对象的时候,这个对象应该有一个初始状态,当对象销毁之前应该销毁自己创建的一些数据
对象的初始化和清理也是两个非常重要的安全问题,一个对象或者变量没有初始化,对其使用后果是未知的,同样的使用完一个变量,没有及时清理,也会造成一定的安全问题,C++为了给我们提供这种问题的解决方案,构造函数和析构函数,这两个函数将会被编译器自动调用,完成对象初始化和对象清理工作。
对象的初始化和清理工作是编译器强制我们要做的事情,即使你不提供初始化操作和清理操作,编译器也会给你增加默认的操作,只是这个默认初始化操作不会做任何事,所以编写类就应该顺便提供初始化函数
简单的案例
#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
class Maker {
public:
// 构造函数 初始化成员变量 编译器调用
Maker()
{
a = 10;
cout << "构造函数" << endl;
}
~Maker()
{
// 析构函数
// 对象销毁前 编译器调用
cout<<"析构函数"<<endl
}
public:
int a;
};
void test01()
{
// 创建对象 分配空间 局部变量在栈区
// 调用构造函数
Maker m;// 创建对象时 调用构造函数
int b = m.a;
cout << b << endl;
}
int main()
{
//cout << "hello" << endl;
test01();
return EXIT_SUCCESS;
}
有参数构造:
#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
class Maker {
public:
// 构造函数
Maker()
{
a = 10;
cout << "构造函数" << endl;
}
public:
int a;
};
void test01()
{
Maker m;// 创建对象时 调用构造函数
int b = m.a;
cout << b << endl;
}
// 析构函数的作用
class Maker2
{
public:
// 有参数构造
Maker2(const char* name, int age)
{
cout << "有参数构造" << endl;
// 从堆区进行申请
pName = (char*)malloc(sizeof(char) * (strlen(name) + 1));// 申请字符串长度的字节个数 +1 是因为填充\0
strcpy(pName,name);// 赋值字符串 参数直接写成字符串地址即可
mAge = age;
}
void Print()
{
cout << "打印参数" << pName << mAge << endl;
}
private:
char* pName;
int mAge;
};
void test02()
{
Maker2 m("hh",11);
m.Print();
}
int main()
{
test02();
return EXIT_SUCCESS;
}
二、析构函数
析构函数的作用就是在对象销毁之前,回收内存资源
#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
// 析构函数的作用
class Maker2
{
public:
// 有参数构造
Maker2(const char* name, int age)
{
cout << "有参数构造" << endl;
// 从堆区进行申请
pName = (char*)malloc(sizeof(char) * (strlen(name) + 1));// 申请字符串长度的字节个数 +1 是因为填充\0
strcpy(pName,name);// 赋值字符串 参数直接写成字符串地址即可
mAge = age;
}
void Print()
{
cout << "打印参数" << pName << mAge << endl;
}
~Maker2()
{
cout << "析构函数 " << endl;
//释放堆区空间
if (pName != NULL)
{
free(pName);
pName = NULL;
}
}
private:
char* pName;
int mAge;
};
void test02()
{
Maker2 m("hh",11);
m.Print();
}
int main()
{
test02();
return EXIT_SUCCESS;
}
三、构造函数可以重载
class Maker3
{
public:
// 构造函数可以重载
Maker3()
{
// 无参构造函数
cout
}
Maker3(int a)
{
// 有参构造函数
}
};
构造函数必须声明为public,如果是私有化,创建对象会被报错,没办法调用构造函数
有对象产生必然会调用构造函数,有对象销毁必然会调用析构函数,有多少次对象产生就会调用多少次构造函数,有多少次对象销毁就会调用多少次析构函数
#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
class Maker3
{
public:
// 构造函数可以重载
Maker3()
{
// 无参构造函数
cout << "无参数构造" << endl;
}
Maker3(int a)
{
// 有参构造函数
cout << "有参数构造" << endl;
}
~Maker3()
{
// 析构函数
cout << "析构函数" << endl;
}
};
void test03()
{
Maker3 m;
Maker3 m1(1);
}
int main()
{
//cout << "hello" << endl;
test03();
return EXIT_SUCCESS;
}
注意:
构造函数和析构函数必须是共有权限,否则创建对象时,会报错
初始化用构造函数,清理使用析构函数,这两个函数是编译器调用
构造函数可以进行重载
构造函数没有返回值,不可以使用void,析构函数没有返回值,不能使用void
四、默认构造函数和默认析构函数
#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
class Maker
{
public:
// 编译器提供默认的构造函数和析构函数 函数体都是空的
Maker()
{
}
~Maker()
{
}
// 编译器提供默认的构造函数和析构函数
void printMaker()
{
a = 100;
cout << a << endl;
}
private:
int a;
};
void test()
{
Maker m;
m.printMaker();
}
int main()
{
//cout << "hello" << endl;
test();
return EXIT_SUCCESS;
}
五、拷贝构造函数
#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
class Maker
{
public:
// 编译器提供默认的构造函数和析构函数 函数体都是空的
Maker()
{
cout << "构造函数" << endl;
a = 20;
}
// 拷贝构造函数 参数是对象的引用
Maker(const Maker &m)
{
cout << "拷贝构造函数" << endl;
a = m.a;// 赋值成员变量
}
~Maker()
{
}
// 编译器提供默认的构造函数和析构函数
void printMaker()
{
a = 100;
cout << a << endl;
}
private:
int a;
};
void test()
{
Maker m;
//m.printMaker();
Maker m1(m);// 用已有的对象生成另一个对象 涉及到构造函数的重载
m1.printMaker();
}
int main()
{
//cout << "hello" << endl;
test();
return EXIT_SUCCESS;
}
5.1 编译器提供了默认的构造函数
C++编译器提供了默认的拷贝构造函数,默认的拷贝构造函数进行了成员变量的简单赋值
#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
class Maker
{
public:
// 编译器提供默认的构造函数和析构函数 函数体都是空的
Maker()
{
cout << "构造函数" << endl;
a = 20;
}
// // 拷贝构造函数 参数是对象的引用
//Maker(const Maker &m)
//{
// cout << "拷贝构造函数" << endl;
// a = m.a;// 赋值成员变量
//}
~Maker()
{
}
// 编译器提供默认的构造函数和析构函数
void printMaker()
{
cout << a << endl;
}
private:
int a;
};
void test()
{
Maker m;
//m.printMaker();
Maker m1(m);// 用已有的对象生成另一个对象 涉及到构造函数的重载
m1.printMaker();
}
int main()
{
//cout << "hello" << endl;
test();
return EXIT_SUCCESS;
}
5.2 拷贝构造函数中形参必须要用引用类型
如果拷贝构造函数中的形参不是引用类型
Maker3(const Maker3 m)
{
cout<<"拷贝构造函数"<<endl;
}
1.Maker3 m2(m1);
2.const Maker3 m = m1;
3.const Maker3 m(m1);
4.const Mkaer3 m = m1;
5.进入死循环
六、构造函数的分类和调用
#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
class Maker {
public:
// 按照参数分类:无参数 有参数
Maker()
{
cout << "无参数构造" << endl;
}
Maker(int a)
{
cout << "有参数构造" << endl;
}
// 按照类型:普通构造函数 拷贝构造函数
Maker(const Maker& m)
{
cout << "拷贝构造函数" << endl;
}
};
void test()
{
Maker m;// 调用无参数构造函数
Maker m1(10);// 调用有参数构造函数
Maker m2(m1);// 调用拷贝构造函数
Maker m3 = m2;// 调用拷贝构造函数
Maker m4 = 10;// 调用有参构造
}
int main()
{
//cout << "hello" << endl;
test();
return EXIT_SUCCESS;
}
七、匿名对象
匿名对象的声明周期在当前行
#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
class Maker {
public:
// 按照参数分类:无参数 有参数
Maker()
{
cout << "无参数构造" << endl;
}
Maker(int a)
{
cout << "有参数构造" << endl;
}
// 按照类型:普通构造函数 拷贝构造函数
Maker(const Maker& m)
{
cout << "拷贝构造函数" << endl;
}
~Maker()
{
cout << "析构函数" << endl;
}
};
void test01()
{
Maker();// 匿名对象的生命周期在当前行
cout << "test01函数结束" << endl;
}
int main()
{
test01();
return EXIT_SUCCESS;
}
如果匿名对象前面有名字来接 那么就不是匿名对象 析构函数在函数结束之后 执行
#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
class Maker {
public:
// 按照参数分类:无参数 有参数
Maker()
{
cout << "无参数构造" << endl;
}
Maker(int a)
{
cout << "有参数构造" << endl;
}
// 按照类型:普通构造函数 拷贝构造函数
Maker(const Maker& m)
{
cout << "拷贝构造函数" << endl;
}
~Maker()
{
cout << "析构函数" << endl;
}
};
void test01()
{
Maker();// 匿名对象的生命周期在当前行
cout << "test01函数结束" << endl;
Maker m1 = Maker();// 如果匿名对象前面有名字来接 那么就不是匿名对象 析构函数在函数结束之后 执行
cout << "函数结束" << endl;
}
int main()
{
//cout << "hello" << endl;
test01();
return EXIT_SUCCESS;
}
八、拷贝构造函数的调用时机
8.1 对象以值的方式给函数参数
#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
class Maker {
public:
// 按照参数分类:无参数 有参数
Maker()
{
cout << "无参数构造" << endl;
}
Maker(int a)
{
cout << "有参数构造" << endl;
}
// 按照类型:普通构造函数 拷贝构造函数
Maker(const Maker& m)
{
cout << "拷贝构造函数" << endl;
}
~Maker()
{
cout << "析构函数" << endl;
}
};
// 对象以值的方式给函数参数
void func(Maker m)
{
// 以拷贝构造函数的形式进行赋值
}
void test01()
{
Maker m1;
func(m1); // Maker m = m1
}
int main()
{
//cout << "hello" << endl;
test01();
return EXIT_SUCCESS;
}
8.2 vs debug模式下的函数局部对象返回值
#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
class Maker {
public:
// 按照参数分类:无参数 有参数
Maker()
{
cout << "无参数构造" << endl;
}
Maker(int a)
{
cout << "有参数构造" << endl;
}
// 按照类型:普通构造函数 拷贝构造函数
Maker(const Maker& m)
{
cout << "拷贝构造函数" << endl;
}
~Maker()
{
cout << "析构函数" << endl;
}
};
// 对象以值的方式给函数参数
void func(Maker m)
{
// 以拷贝构造函数的形式进行赋值
}
// 拷贝构造函数的方式调用
void test01()
{
Maker m1;
func(m1); // Maker m = m1
}
// 函数的局部对象以值的方式从函数返回
Maker func2()
{
// 局部对象
Maker m;// 无参数构造
cout << "局部对象的地址:" << &m << endl;
return m;
}
// vs 的Debug模式下 会调用拷贝构造
void test03()
{
Maker m1 = func2();// 调用拷贝构造函数 vs debug模式下 fun2()中的对象已经释放 m1是新的地址
cout << "m1的对象的地址" << &m1 << endl; // 这里会发现 两个地址不一样 因为函数内部的局部对象 在函数结束之后 会释放内存
}
int main()
{
//cout << "hello" << endl;
test03();
return EXIT_SUCCESS;
}
8.3 vs Release模式下函数局部对象以值的方式从函数返回
#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
class Maker {
public:
// 按照参数分类:无参数 有参数
Maker()
{
cout << "无参数构造" << endl;
}
Maker(int a)
{
cout << "有参数构造" << endl;
}
// 按照类型:普通构造函数 拷贝构造函数
Maker(const Maker& m)
{
cout << "拷贝构造函数" << endl;
}
~Maker()
{
cout << "析构函数" << endl;
}
};
// 对象以值的方式给函数参数
void func(Maker m)
{
// 以拷贝构造函数的形式进行赋值
}
// 拷贝构造函数的方式调用
void test01()
{
Maker m1;
func(m1); // Maker m = m1
}
// 函数的局部对象以值的方式从函数返回
Maker func2()
{
// 局部对象
Maker m;// 无参数构造
cout << "局部对象的地址:" << &m << endl;
return m;
}
// vs 的Relase模式下 不会调用拷贝构造
void test03()
{
Maker m1 = func2();// 未调用拷贝构造函数 地址相同
cout << "m1的对象的地址" << &m1 << endl; // 这里会发现 两个地址一样 release为了节约内存 没有释放函数内部的对象
}
int main()
{
//cout << "hello" << endl;
test03();
return EXIT_SUCCESS;
}
九、构造函数的调用规则
9.1 如果程序员提供有参数构造,那么编译器不会提供默认构造函数,但是会提供默认的拷贝构造函数
#include<iostream>
using namespace std;
# define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
class Maker
{
public:
Maker(int a)
{
cout << "有参数构造函数" << endl;
}
};
// 如果程序员提供了有参数构造函数 那么编译器不会提供默认的构造函数 函数重载
void test01()
{
//Maker m;// 报错
Maker m(10);// 调用有参构造函数
Maker m1(m);// 编译器提供默认的拷贝构造函数
}
int main()
{
test01();
return EXIT_SUCCESS;
}
9.2 如果程序员提供了拷贝构造函数 那么编译器不会提供默认的构造函数和默认的拷贝构造函数
#include<iostream>
using namespace std;
# define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
class Maker
{
public:
Maker(const Maker& m)
{
cout << "拷贝构造函数" << endl;
}
};
// 如果程序员提供了有参数构造函数 那么编译器不会提供默认的构造函数 函数重载
void test01()
{
//Maker m;// 报错
}
// 如果程序员提供了拷贝构造函数 那么编译器不会提供默认构造函数和默认的拷贝构造函数
void test02()
{
Maker m;
}
int main()
{
return EXIT_SUCCESS;
}
十、多个对象的构造函数和析构函数
10.1 多个对象的构造函数和析构函数的规则
- 如果类有成员对象 那么先调用成员对象的构造函数 在调用本身的构造函数
- 析构函数的调用顺序相反
- 成员对象的构造函数调用和定义顺序一样
- 如果有成员对象 那么实例化对象时,必须保证成员对象的构造和析构能被调用
#include<iostream>
using namespace std;
# define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
class BMW
{
public:
BMW()
{
cout << "BMW() 构造" << endl;
}
~BMW()
{
cout << "BMW析构函数" << endl;
}
};
class Buiker
{
public:
Buiker()
{
cout << "Buiker() 构造" << endl;
}
~Buiker()
{
cout << "Buiker析构函数" << endl;
}
};
class Maker
{
public:
Maker()
{
cout << "Maker构造" << endl;
}
~Maker()
{
cout << "maker析构函数" << endl;
}
private:
BMW bmw;//成员对象
Buiker bui;
};
void test01()
{
// 如果类有成员对象 那么先调用成员对象的构造函数 在调用本身的构造函数
// 析构函数的调用顺序相反
// 成员对象的构造函数调用和定义顺序一样
// 如果有成员对象 那么实例化对象时,必须保证成员对象的构造和析构能被调用
Maker m;
}
int main()
{
test01();
return EXIT_SUCCESS;
}
10.2 多个对象初始化列表
#include<iostream>
using namespace std;
# define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
class BMW
{
public:
BMW(int a)
{
cout << "BMW() 构造" << a<<endl;
}
~BMW()
{
cout << "BMW析构函数" << endl;
}
};
class Buiker
{
public:
Buiker()
{
cout << "Buiker() 构造" << endl;
}
~Buiker()
{
cout << "Buiker析构函数" << endl;
}
};
class Maker
{
public:
// 初始化列表
// 注意:初始化列表只能写在构造函数
Maker() :bmw(10)
{
cout << "Maker构造" << endl;
}
~Maker()
{
cout << "maker析构函数" << endl;
}
private:
BMW bmw;//成员对象
Buiker bui;
};
void test01()
{
// 如果类有成员对象 那么先调用成员对象的构造函数 在调用本身的构造函数
// 析构函数的调用顺序相反
// 成员对象的构造函数调用和定义顺序一样
// 如果有成员对象 那么实例化对象时,必须保证成员对象的构造和析构能被调用
Maker m;
}
// 初始化列表是调用成员对象的指定构造函数
void test02()
{
Maker m;
}
int main()
{
test02();
return EXIT_SUCCESS;
}
或者:构造函数需要加上参数
#include<iostream>
using namespace std;
# define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
class BMW
{
public:
BMW(int a)
{
cout << "BMW() 构造" << a<<endl;
}
~BMW()
{
cout << "BMW析构函数" << endl;
}
};
class Buiker
{
public:
Buiker()
{
cout << "Buiker() 构造" << endl;
}
~Buiker()
{
cout << "Buiker析构函数" << endl;
}
};
class Maker
{
public:
// 初始化列表
// 注意:初始化列表只能写在构造函数
// 构造函数可以写参数 初始化直接写入参数
Maker(int a) :bmw(a)
{
cout << "Maker构造" << endl;
}
~Maker()
{
cout << "maker析构函数" << endl;
}
private:
BMW bmw;//成员对象
Buiker bui;
};
// 初始化列表是调用成员对象的指定构造函数
void test02()
{
Maker m(1);
}
int main()
{
test02();
return EXIT_SUCCESS;
}
注意:如果使用了初始化列表,那么所有的构造函数都要写初始化列表
#include<iostream>
using namespace std;
# define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
class BMW
{
public:
BMW(int a)
{
cout << "BMW() 构造" << a<<endl;
}
~BMW()
{
cout << "BMW析构函数" << endl;
}
};
class Buiker
{
public:
Buiker()
{
cout << "Buiker() 构造" << endl;
}
~Buiker()
{
cout << "Buiker析构函数" << endl;
}
};
class Maker
{
public:
// 初始化列表
// 注意:初始化列表只能写在构造函数
// 构造函数可以写参数 初始化直接写入参数
Maker(int a) :bmw(a)
{
cout << "Maker构造" << endl;
}
// 拷贝构造函数也要协商初始化列表
Maker(const Maker& m) :bmw(4)
{
// 注意 如果使用了初始化列表 那么所有的构造函数都要写
}
~Maker()
{
cout << "maker析构函数" << endl;
}
private:
BMW bmw;//成员对象
Buiker bui;
};
// 初始化列表是调用成员对象的指定构造函数
void test02()
{
Maker m(1);
}
int main()
{
test02();
return EXIT_SUCCESS;
}
#include<iostream>
using namespace std;
# define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
class BMW
{
public:
BMW(int a)
{
cout << "BMW() 构造" << a<<endl;
}
~BMW()
{
cout << "BMW析构函数" << endl;
}
};
class Buiker
{
public:
Buiker(int b)
{
cout << "Buiker() 构造" << endl;
}
~Buiker()
{
cout << "Buiker析构函数" << endl;
}
};
class Maker
{
public:
// 初始化列表
// 注意:初始化列表只能写在构造函数
// 构造函数可以写参数 初始化直接写入参数
Maker(int a) :bmw(a),bui(1)
{
cout << "Maker构造" << endl;
}
Maker(const Maker& m) :bmw(4),bui(1)
{
// 注意 如果使用了初始化列表 那么所有的构造函数都要写
}
~Maker()
{
cout << "maker析构函数" << endl;
}
private:
BMW bmw;//成员对象
Buiker bui;
};
// 初始化列表是调用成员对象的指定构造函数
void test02()
{
Maker m(1);
}
int main()
{
test02();
return EXIT_SUCCESS;
}
构造函数的参数填写所有参数,初始化列表写成参数
#include<iostream>
using namespace std;
# define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
class BMW
{
public:
BMW(int a)
{
cout << "BMW() 构造" << a<<endl;
}
~BMW()
{
cout << "BMW析构函数" << endl;
}
};
class Buiker
{
public:
Buiker(int b)
{
cout << "Buiker() 构造" <<b<< endl;
}
~Buiker()
{
cout << "Buiker析构函数" << endl;
}
};
class Maker
{
public:
// 初始化列表
// 注意:初始化列表只能写在构造函数
// 构造函数可以写参数 初始化直接写入参数
Maker(int a,int b) :bmw(a),bui(b)
{
cout << "Maker构造" << endl;
}
Maker(const Maker& m) :bmw(4),bui(1)
{
// 注意 如果使用了初始化列表 那么所有的构造函数都要写
}
~Maker()
{
cout << "maker析构函数" << endl;
}
private:
BMW bmw;//成员对象
Buiker bui;
};
// 初始化列表是调用成员对象的指定构造函数
void test02()
{
Maker m(1,2);
}
int main()
{
test02();
return EXIT_SUCCESS;
}
总结:
- 初始化列表是干什么用的,指定调用成员对象的某个构造函数
- 初始化列表只能写在构造函数旁边
- 如果使用了初始化列表 那么所有的构造函数都要写初始化列表
- 如果有多个对象需要指定调用某个构造函数,需要使用逗号隔开
- 可以使用对象的构造函数参数传递数值给成员对象的变量
十一、对象的深浅拷贝
11.1 默认的拷贝构造函数进行了简单的赋值操作
拷贝构造函数只是复制值 没有复制内存空间
#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
class Maker {
public:
Maker(int a, int b)
{
id = a;
age = b;
}
void print()
{
cout << "id = " << id << " age = " << age << endl;
}
private:
int id;
int age;
};
void test01()
{
Maker a(1, 1);
a.print();
Maker b(a);// 拷贝构造函数 浅复制
b.print();
}
int main()
{
test01();
return EXIT_SUCCESS;
}
浅拷贝的问题:同一块内存空间被释放两次
#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
class Maker {
public:
Maker(int a, int b)
{
id = a;
age = b;
}
void print()
{
cout << "id = " << id << " age = " << age << endl;
}
private:
int id;
int age;
};
class Student {
public:
Student(const char* name, int Age)
{
pName = (char*)malloc(strlen(name) + 1);// 申请堆区空间
strcpy(pName,name);// 复制内容
age = Age;
}
// 申请堆区空间 一定要释放
~Student()
{
if (pName != NULL)
{
free(pName);
pName = NULL;
}
}
void print()
{
cout << "名字 = " << pName << "年纪 = " << age << endl;
}
private:
char* pName;
int age;
};
void test01()
{
Maker a(1, 1);
a.print();
Maker b(a);// 拷贝构造函数 浅复制
b.print();
}
void test02()
{
Student a("xxx",1);
a.print();
// 复制的是地址
// 导致同一块内存地址空间被析构函数销毁两次
Student b(a);
b.print();
}
int main()
{
test02();
return EXIT_SUCCESS;
}
11.2 深拷贝
重写拷贝构造函数即可,本质上还是重新申请一块堆区空间
#include<iostream>
using namespace std;
#define _CRT_SECURE_NO_WARNINGS
#pragma warning(disable:4996)
class Maker {
public:
Maker(int a, int b)
{
id = a;
age = b;
}
void print()
{
cout << "id = " << id << " age = " << age << endl;
}
private:
int id;
int age;
};
class Student {
public:
Student(const char* name, int Age)
{
pName = (char*)malloc(strlen(name) + 1);// 申请堆区空间
strcpy(pName,name);// 复制内容
age = Age;
}
// 重载拷贝构造函数
Student(const Student& stu)
{
cout << "自己的拷贝构造函数" << endl;
// 重新申请一块内存空间
pName = (char*)malloc(sizeof(char) * (strlen(pName) + 1));
// 拷贝数据
strcpy(pName,stu.pName);
age = stu.age;
}
// 申请堆区空间 一定要释放
~Student()
{
if (pName != NULL)
{
free(pName);
pName = NULL;
}
}
void print()
{
cout << "名字 = " << pName << "年纪 = " << age << endl;
}
private:
char* pName;
int age;
};
void test01()
{
Maker a(1, 1);
a.print();
Maker b(a);// 拷贝构造函数 浅复制
b.print();
}
void test02()
{
Student a("xxx",1);
a.print();
// 复制的是地址
// 导致同一块内存地址空间被析构函数销毁两次
Student b(a);
b.print();
}
int main()
{
test02();
return EXIT_SUCCESS;
}