對於通常的對象,如:
ios
int a = b;ide
int c = 5;函數
它們之間的賦值,複製很簡單,到對於類對象來講,其內部存在各類成員變量,他的複製,賦值就不是如此的簡單,若是處理不當,就會出現各類問題。this
咱們首先來看一下下面的代碼:
spa
#include<iostream>3d
using namespace std;指針
#include<string.h>對象
class Stringblog
{內存
public:
String(const char* ptr)
:_ptr(new char[strlen(ptr) + 1])
{
strcpy(_ptr, ptr);
cout << "String" << endl;
cout << _ptr << endl;
}
String(const String& ptr)
:_ptr(ptr._ptr)
{}
String& operator=(const String& ptr)
{
if (this != &ptr)
{
_ptr = ptr._ptr;
}
return *this;
}
~String()
{
cout << "~String" << endl;
delete[] _ptr;
}
private:
char * _ptr;
};
void Test()
{
String s("hello");
String s2(s);
}
int main()
{
Test();
return 0;
}
能夠看到,這裏對同一對象析構了兩次,咱們經過下面的圖來對這個現象進行解釋:
在進行對象複製後,事實上s、s2裏的成員指針_ptr都指向了一塊內存空間(即內存空間共享了),在s1析構時,delete了成員指針_ptr所指向的內存空間,而s2析構時一樣指向(此時已變成野指針)而且要釋放這片已經被s1析構函數釋放的內存空間,這就讓一樣一片內存空間釋放了兩次 ,從而出錯。而淺拷貝還存在着一個問題,由於一片空間被兩個不一樣的子對象共享了,只要其中的一個子對象改變了其中的值,那另外一個對象的值也跟着改變了。
在這裏呢咱們找到了深拷貝來解決這個問題。
下面是深拷貝的拷貝構造函數代碼:
String(const String& ptr)
:_ptr(new char[strlen(ptr._ptr) + 1])
{
strcpy(_ptr, ptr._ptr);
}
這裏經過從新開闢一段空間來解決淺拷貝中析構兩次的狀況。
在淺拷貝中還存在其餘的一些問題,好比賦值運算符重載,他也是s,s1,兩個對象指向同一塊內存空間。在析構時一樣會出現問題。還有析構函數,若是這塊空間爲NULL,又怎麼能進行析構呢!
咱們給出深拷貝的函數代碼:
class String
{
public:
String(const char* ptr)
:_ptr(new char[strlen(ptr) + 1])
{
strcpy(_ptr, ptr);
}
String(const String& ptr)
:_ptr(new char[strlen(ptr._ptr) + 1])
{
strcpy(_ptr, ptr._ptr);
}
String& operator=(const String& ptr)
{
if (this != &ptr)
{
delete[] _ptr;
char* tmp = new char[strlen(ptr._ptr) + 1];
strcpy(tmp, ptr._ptr);
_ptr = tmp;
}
return *this;
}
~String()
{
if (_ptr)
{
delete[] _ptr;
}
}
private:
char* _ptr;
};
void Test()
{
String s("hello");
String s2(s);
String s3("11111");
s3 = s;
}
int main()
{
Test();
return 0;
}
經過深拷貝咱們就能解決淺拷貝的問題。
對深拷貝中的賦值運算符重載咱們作如下說明:
1.進入賦值運算符重載,咱們首先須要判斷該對象是否是本身給本身賦值,本身給本身賦值從賦值上來講也沒錯,可是在調用析構函數時又怎該去析構呢?不是又給同一對象析構兩次嗎?
2.賦值運算符重載的深層理解
String& operator=(const String& ptr)
s3 = s;
賦值運算符重載傳參是實際上是這樣的:s3.operator=(&s3,s),&s3其實就是this指針,在實現賦值運算符重載的過程當中,首先析構掉s3本來空間的內容,而後開闢一段新空間,把s的內容複製到新空間中,再把新空間的內容給給s3,完成賦值。