深拷贝用来解决浅拷贝造成的两次析构问题,因为浅拷贝时,两个指针指向同一块内存空间,析构时,一块内存空间释放两次,系统会报错。因此,我们需要重新开辟一块内存空间,使两个指针指向不同的空间,以此来解决问题。
下面用一个String的例子来验证深拷贝。
#include <iostream>
#include <cstring>
using namespace std;
class String
{
public:
//利用列表初始化为mPstr在堆上创建一个内存空间
String(const char *s = " ") : mPstr(new char[strlen(s) + 1])
{
strcpy(mPstr,s);
}
//默认拷贝构造函数中,s1和s2指向同一块内存空间,当析构时,先析构s2,内存空间已经销毁,再销毁一次s1,两次析构同一个空间,报错。
//因此,需要重新开辟一块内存空间,让s2指向新空间,析构时,各删各的
String(const String &other) : mPstr(new char[strlen(other.mPstr) + 1])
{
strcpy(mPstr, other.mPstr);
}
~String()
{
delete []mPstr;
}
void show() const
{
cout << mPstr << endl;
}
void append(const String &other) //实现一个追加功能
{
char *p = new char[strlen(this->mPstr) + strlen(other.mPstr) + 1];
strcpy(p, this->mPstr);
strcat(p, other.mPstr);
delete []this->mPstr;
mPstr = p;
//因为p是局部变量,所以不需要手动销毁
}
void assign(const String &other) //实现一个覆盖功能
{
if(this != &other) //如果覆盖的字符是本身,就不覆盖
{
char *p = new char[strlen(other.mPstr) + 1];
strcpy(p, other.mPstr);
delete []this->mPstr;
mPstr = p;
}
}
private:
char *mPstr;
};
int main(void)
{
String s1("Hello");
s1.show();
String s2(s1);
s2.show();
system("pause");
return 0;
}
再来一个链表的例子
#include <iostream>
#include <cstring>
using namespace std;
//链表
struct Node //定义结点,实际上还是一个类,所以可以使用列表初始化
{
Node(int value, Node *pNext) : data(value), next(pNext){}
int data;
Node *next;
};
class List
{
public:
List();
//默认拷贝构造函数,会导致浅拷贝的问题,在析构时,删除同一块内存空间,报错,因此将新的pHead置空,复制数据,重新插入
List(const List &other);
~List();
void push_front(int value); //在头部插入数据
void push_back(int value); //在尾部插入数据
void pop_front(); //删除头部数据
void pop_back(); //删除尾部数据
void clear(); //清空数据,用于析构
void show() const; //打印链表数据
size_t size() const; //记录结点个数
bool isEmpty() const; //判空函数
private:
Node *pHead;
};
int main(void)
{
List l;
l.push_front(1);
l.push_front(2);
l.push_front(3);
l.show();
cout << "***********" << endl;
List l2(l);
l2.show();
system("pause");
return 0;
}
List::List() : pHead(nullptr)
{
}
List::List(const List &other) : pHead(nullptr)
{
Node *p = other.pHead;
while(p)
{
this->push_back(p->data);
p = p->next;
}
}
List::~List()
{
clear();
}
void List::push_front(int value)
{
Node *pNew = new Node(value, pHead);
pHead = pNew;
}
bool List::isEmpty() const
{
return nullptr == pHead;
}
void List::push_back(int value)
{
if(isEmpty())
{
push_front(value);
}
else
{
Node *pNew = new Node(value, nullptr);
Node *p = pHead;
while(p->next)
{
p = p->next;
}
p->next = pNew;
}
}
void List::pop_front()
{
if(!isEmpty())
{
Node *p = pHead;
pHead = p->next;
delete p;
}
}
void List::pop_back()
{
if(!isEmpty())
{
size_t n = size();
if(n == 1)
{
pop_front();
}
if(n >= 2)
{
Node *p = pHead;
while(p->next->next != 0)
{
p = p->next;
}
delete p->next;
p->next = nullptr;
}
}
}
void List::show() const
{
Node *p = pHead;
while(p)
{
cout << p->data << endl;
p = p->next;
}
}
void List::clear()
{
while(!isEmpty())
{
pop_front();
}
}
size_t List::size() const
{
Node *p = pHead;
size_t counter = 0;
while(p)
{
++counter;
p = p->next;
}
return counter;
}