浅拷贝只是增加了一个指针指向已存在的内存地址,
深拷贝是增加了一个指针并且申请了一个新的内存,使这个增加的指针指向这个新的内存,
拷贝后两者是同一个地址,则是浅拷贝,否则是深拷贝。
可以结合值传递和引用传递来理解。
一般的赋值操作是深度拷贝:
int a = 5;
int b = a;
因为这里的b是单独分配的一个地址空间,a和b地址不同。
简单的指针指向,则是浅拷贝:
//浅拷贝
int a = 8;
int *p;
p = &a;
char* str1 = "HelloWorld";
char* str2 = str1;
将上面的浅拷贝改为深度拷贝后:
//深度拷贝
int a = 8;
int *p = new int;
*p = a;
char* str1 = "HelloWorld";
int len = strlen(str1);
char *str2 = new char[len];
memcpy(str2, str1, len);
看个例子,我们在定义一个类的时候,当类中没有定义构造函数时,编译器会提供一个默认的无参构造,还有一个拷贝构造函数,这个默认的拷贝构造函数就是浅拷贝。
这样会出现一个情况,两个对象同时指向同一内存,当一个对象析构后,另一对象再析构就会出错。
因此就需要自定义一个拷贝构造函数,将其改为深拷贝。
#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
template <class T>
class MyArray
{
public:
MyArray(int capacity)
{
cout << "有参构造调用..." << endl;
this->m_Capacity = capacity;
this->m_Size = 0;
this->pAddr = new T[capacity];
}
//重载 拷贝构造函数
MyArray(const MyArray& arr)
{
cout << "拷贝构造调用..." << endl;
this->m_Capacity = arr.m_Capacity;
this->m_Size = arr.m_Size;
//假如使用编译器提供的拷贝构造,两个对象同时指向同一内存,当一个对象析构后,另一对象再析构就会出错
//需要进行深拷贝.在堆区重新开辟一块内存,和传进来的内存一样大,然后复制内存
this->pAddr = new T[arr.m_Capacity];
//数据拷贝
for (int i = 0; i < arr.m_Size; i++)
{
this->pAddr[i] = arr.pAddr[i];
}
}
~MyArray()
{
cout << "析构函数调用..." << endl;
if (this->pAddr)
{
delete[] this->pAddr;
this->pAddr = nullptr;
}
}
private:
T* pAddr;
int m_Capacity;
int m_Size;
};
#include <iostream>
#include "MyArrayDemo.hpp"
void test01()
{
//调用有参构造函数,会为指针分配内存
MyArray<int> arr1(10);
//调用拷贝构造函数,自定义深拷贝,同样为指针分配内存
//假如不重写拷贝构造函数的话,不会为指针分配空间,就是浅拷贝了
MyArray<int> arr2(arr1);
//MyArray<int> arr2{arr1};//这里用花括号也可以的
//拷贝构造是产生一个新的对象,赋值是对两个已经存在的对象
//只有2个操作数都是已经定义过的变量时,才会调用赋值运算符重载
//下面这行同样是调用拷贝构造函数
MyArray<int> arr3 = arr1;
}
int main(int argc, char* argv[])
{
test01();
return 0;
}
当使用默认拷贝构造函数时(注释掉重载的拷贝构造函数),运行出错。可以看到出错位置在第二次析构函数调用的地方。
重载拷贝构造函数并改为深度拷贝后再运行一次
可以正常析构。
貌似只有存在指针的时候,需要深度拷贝。
只有在需要深度拷贝的时候才需要重载拷贝构造函数。
#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
template <class T>
class MyArray
{
public:
MyArray(int capacity)
{
cout << "有参构造调用..." << endl;
this->m_Capacity = capacity;
this->m_Size = 0;
this->pAddr = new T[capacity];
}
//拷贝构造函数
MyArray(const MyArray& arr)
{
cout << "拷贝构造调用..." << endl;
this->m_Capacity = arr.m_Capacity;
this->m_Size = arr.m_Size;
#if 0
//拷贝构造->1
//假如使用编译器提供的拷贝构造,两个对象同时指向同一内存,当一个对象析构后,另一对象再析构就会出错
this->pAddr = arr.pAddr;//这样拷贝对指针是不可取的
#elif 1
//拷贝构造->2
//需要进行深拷贝.在堆区重新开辟一块内存,和传进来的内存一样大,然后复制内存
this->pAddr = new T[arr.m_Capacity];
//数据拷贝
for (int i = 0; i < arr.m_Size; i++)
{
//memcpy(this->pAddr[i], arr.pAddr[i], arr.m_Capacity);
this->pAddr[i] = arr.pAddr[i];
}
#endif
}
MyArray& operator= (const MyArray& arr)
{
if (this->pAddr != nullptr)
{
delete[] this->pAddr;
this->pAddr = nullptr;
this->m_Capacity = 0;
this->m_Size = 0;
}
this->m_Capacity = arr.m_Capacity;
this->m_Size = arr.m_Size;
this->pAddr = new T[arr.m_Capacity];
for (int i = 0; i < arr.m_Size; i++)
{
this->pAddr[i] = arr.pAddr[i];
}
return *this;
}
void Push_Back(const T& val)
{
if (this->m_Size==this->m_Capacity)
{
cout << "容量已满" << endl;
return;
}
this->pAddr[this->m_Size] = val;
this->m_Size++;
}
void Pop_Back()
{
if (this->m_Size == 0)
{
cout << "容器为空" << endl;
return;
}
//逻辑删除,只要把m_Size减一,用户就访问不到了
this->m_Size--;
}
~MyArray()
{
cout << "析构函数调用..." << endl;
if (this->pAddr)
{
delete[] this->pAddr;
this->pAddr = nullptr;
}
cout << "析构结束!" << endl;
}
private:
T* pAddr;
int m_Capacity;
int m_Size;
};