一、浅拷贝
我们看下以下代码
Test.h 文件
#pragma once
#include<iostream>
using namespace std;
class Student
{
public:
Student()
{
}
~Student()
{
if (m_Id != nullptr)
{
delete m_Id;
m_Id = nullptr;
}
}
Student(int id, string strName)
{
m_Id = new int[id];
m_strName = strName;
}
private:
int* m_Id;
string m_strName;
};
Test.cpp 文件
#include"Test.h"
int main()
{
Student s1(1, "zhangsan");
Student s2(s1);
cout << "helloworld" << endl;
return 0;
}
运行:
只是简单的构造了两个类对象,最后运行却崩溃了
崩溃点是析构函数
~Student()
{
if (m_Id != nullptr)
{
delete m_Id;
m_Id = nullptr;
}
}
那么我们把这段删除掉试一试
运行:
ok 问题解决了?这样只会造成内存泄漏,并不是不释放 = 解决问题
但是这不是问题的本质
直接原因:析构函数,多次释放同一块空间,导致崩溃
根本原因:默认拷贝构造函数
是的,类的默认拷贝构造函数
Student s1(1, "zhangsan");
Student s2(s1);
在类 Student 中 并没有定义拷贝构造函数,那么在 Student s2(s1); 时会调用类的默认成员函数:拷贝构造函数
而浅拷贝会导致一个结果
s1->m_Id
s2->m_Id
这两个类对象的成员变量 m_Id 的地址其实是一致的
打印各自的m_Id
void GetMemberAdd()
{
cout << m_Id << endl;
}
s1.GetMemberAdd();
s2.GetMemberAdd();
结果:
那么两个类对象调用析构函数,s1 先析构 delete m_Id 接着 s2 再次释放,那么就属于非法操作一块空间,最终导致崩溃。
有些朋友,可能说并没有出现这种情况,是的如果只是值拷贝,不涉及,指针拷贝,引用时深浅拷贝是一样的。
那么当涉及到指针、引用时则需要自己实现拷贝构造函数,实现深拷贝,避免造浅拷贝所出现的问题。
并且建议最好自己实现一下拷贝构造函数与类对象的 “=” 操作符重载
否则也是会出现以下情况
二、深拷贝
看了上述的问题,那么如何来解决浅拷贝的问题呢,这里就引入深拷贝的问题
我们直到出现问题的原因是:
s1->m_Id Address == s2->m_Id Address
那么我们在拷贝构造函数为每一个新的类对象的都动态创建一个新的 m_Id 那么就可以解决浅拷贝的问题了,看代码
Student(const Student& s)
{
int iTemp = *(s.m_Id);
m_Id = new int(iTemp);//动态申请新的空间,再赋值
m_strName = s.m_strName;
}
总结:深拷贝是开辟新的空间,再存储值,使用默认的拷贝构造,会将两个指针指向同一块空间(并没有开辟新的空间) 而是共用一块空间,因此在释放时会导致崩溃。
完整测试代码
Test.h
#pragma once
#include<iostream>
#include<string>
using namespace std;
class Student
{
public:
Student()
{
}
~Student()
{
if (m_Id != nullptr)
{
delete m_Id;
m_Id = nullptr;
}
}
Student(int id, string strName)
{
m_Id = new int(id);
m_strName = strName;
}
Student(const Student& s)
{
int iTemp = *(s.m_Id);
m_Id = new int(iTemp);
m_strName = s.m_strName;
}
void GetMemberAdd()
{
cout << m_Id << endl;
}
void Print()
{
cout << *m_Id << endl;
cout << m_strName << endl;
}
private:
int* m_Id;
string m_strName;
};
Test.cpp
#include"Test.h"
int main()
{
Student s1(1, "zhangsan");
Student s2(s1);
s1.Print();
s2.Print();
cout << "helloworld" << endl;
s1.GetMemberAdd();
s2.GetMemberAdd();
return 0;
}