文章目录
- 一、默认拷贝构造函数是浅拷贝
- 1、默认拷贝构造函数
- 2、默认拷贝构造函数是浅拷贝机制
- 二、代码示例 - 浅拷贝造成的问题
一、默认拷贝构造函数是浅拷贝
1、默认拷贝构造函数
如果 C++ 类中 没有定义拷贝构造函数 , C++ 编译器会自动为该类提供一个 " 默认的拷贝构造函数 " , 在函数中对成员变量进行简单的复制操作 ;
" 默认拷贝构造函数 " 用于创建一个新对象作为现有对象的副本 , 其作用是将 现有对象 的成员变量 复制到 新对象中 ;
创建一个类对象 并将其 赋值给 另一个类对象时 , 会自动调用 默认拷贝构造函数 ;
2、默认拷贝构造函数是浅拷贝机制
C++ 编译器 为 类 自动生成的 默认拷贝构造函数 是 浅拷贝 , 只能拷贝 顶层的 成员变量值 , 如果成员变量 是 引用 或 指针 , 其指向的 类 或 内存空间 中的数据 , 是无法拷贝的 ;
如果 没有定义 拷贝构造函数 , 就会触发上述机制 ;
出现如下代码调用时 , 先 调用 有参构造函数 创建了一个 原始对象 s ,
然后 将 s 对象的值 赋值给 s2 对象 , 此时调用的是 拷贝构造函数 ,
由于没有定义 拷贝构造函数 , 使用的事 C++ 编译器的 默认拷贝构造函数 , 进行的拷贝 是 浅拷贝 ;
其中的 字符串指针 , 只拷贝了指针的值 , 没有拷贝字符串的具体内容 ;
// 调用有参构造函数 , 创建 Student 实例对象
Student s(18, "Tom");
// 声明 Student 对象 s2 , 并使用 s 为 s2 赋值
// 该操作会调用 默认的拷贝构造函数
// C++ 编译器提供的拷贝构造函数 只能进行浅拷贝
Student s2 = s;
二、代码示例 - 浅拷贝造成的问题
下面代码中 ,
定义的 Student 类 中 , 定义了 有参构造函数 和 析构函数 ,
没有定义拷贝构造函数 , 因此 C++ 编译器为其生成了 默认拷贝构造函数 ,
默认拷贝构造函数 是 浅拷贝 ;
分析下面 创建两个 Student 对象 的代码 :
// 调用有参构造函数 , 创建 Student 实例对象
Student s(18, "Tom");
// 打印 Student 实例对象成员变量值
s.toString();
// 声明 Student 对象 s2 , 并使用 s 为 s2 赋值
// 该操作会调用 默认的拷贝构造函数
// C++ 编译器提供的拷贝构造函数 只能进行浅拷贝
Student s2 = s;
s2.toString();
Student s(18, "Tom")
是调用有参参构造函数 , 创建 Student 实例对象 , 并调用 s.toString()
打印上述对象 , 打印结果为 :
m_age = 18 , m_name = Tom
Student s2 = s
代码中 , 声明 Student 对象 s2 , 并使用 s 为 s2 赋值 , 该操作会调用 默认的拷贝构造函数 , C++ 编译器提供的拷贝构造函数 只能进行浅拷贝 , 因此打印的值是一样的 ;
m_age = 18 , m_name = Tom
分析修改 拷贝对象 代码 :
// 修改 s2 对象
strcpy(s2.m_name, "Jey");
s.toString();
s2.toString();
strcpy(s2.m_name, "Jey")
代码中 , 修改了 拷贝对象 指针指向的内容 , 将 “Tom” 改为了 “Jey” , 修改了指针指向的内容之后 , 拷贝对象 和 原始对象 的 m_name 成员值都变成了 “Jey” ;
拷贝对象 和 原始对象 都使用了相同的指针 , 那么在析构时就需要注意 , 不能重复 free 掉相同的指针 , 否则就会报错 ;
代码示例 :
#define _CRT_SECURE_NO_WARNINGS
#include "iostream"
using namespace std;
class Student
{
public:
// 有参构造函数
Student(int age, const char* name)
{
// 获取字符串长度
int len = strlen(name);
// 为 m_name 成员分配内存
// 注意还要为字符串结尾的 '\0' 字符分配内存
m_name = (char*)malloc(len + 1);
// 拷贝字符串
// C++ 中使用该函数需要
// 添加 #define _CRT_SECURE_NO_WARNINGS 宏定义
if (m_name != NULL)
{
strcpy(m_name, name);
}
// 为 m_age 成员设置初始值
m_age = age;
cout << "调用有参构造函数" << endl;
}
~Student()
{
// 销毁 name 指向的堆内存空间
if (m_name != NULL)
{
free(m_name);
m_name = NULL;
}
cout << "调用析构函数" << endl;
}
// 该类没有定义拷贝构造函数 , C++ 编译器会自动生成默认的拷贝构造函数
// 打印类成员变量
void toString()
{
cout << "m_age = " << m_age << " , m_name = " << m_name << endl;
}
public:
int m_age;
char* m_name;
};
int main()
{
// 调用有参构造函数 , 创建 Student 实例对象
Student s(18, "Tom");
// 打印 Student 实例对象成员变量值
s.toString();
// 声明 Student 对象 s2 , 并使用 s 为 s2 赋值
// 该操作会调用 默认的拷贝构造函数
// C++ 编译器提供的拷贝构造函数 只能进行浅拷贝
Student s2 = s;
s2.toString();
// 修改 s2 对象
strcpy(s2.m_name, "Jey");
s.toString();
s2.toString();
// 执行时没有问题 , 两个对象都可以正常访问
// 但是由于拷贝时 执行的是浅拷贝
// 浅拷贝 字符串指针时 , 直接将指针进行拷贝 , 没有拷贝具体的值
// s 和 s2 的 m_name 成员是同一个指针
// 如果析构时 , 先析构 s2 , 将指针释放了
// 之后再析构 s 时 发现 继续释放 被释放的指针 , 报错了
// 控制台暂停 , 按任意键继续向后执行
system("pause");
return 0;
}
执行结果 : 执行后打印如下内容 ,
调用有参构造函数
m_age = 18 , m_name = Tom
m_age = 18 , m_name = Tom
m_age = 18 , m_name = Jey
m_age = 18 , m_name = Jey
请按任意键继续. . .
按下任意键 , 继续向后执行 , 调用完第一个析构函数后 , 再次尝试调用第二个析构函数 , 报错了 ;