淺拷貝:淺拷貝隻拷貝指針,但拷貝後兩個指針指向同一個記憶體空間,或者可以說,原對象和拷貝對象共用一個實體,任何一個對象的改變都會引起另一個的改變。當類成員不包括指針何引用時,淺拷貝并無問題;但對于指針與引用成員,當對象的生命周期結束後,淺拷貝會造成同一塊記憶體單元被釋放兩次,導緻記憶體洩漏。
深拷貝:不但對指針進行拷貝,而且對指針指向的内容進行拷貝,經深拷貝後的指針指向兩個不同位址。
調用拷貝構造函數後,淺拷貝依然還有聯系,深拷貝的兩個對象完全獨立。淺拷貝類似于檔案建立快捷方式,而深拷貝好比檔案複制。編譯器預設提供的預設拷貝構造函數是淺拷貝,深拷貝的構造函數需自己實作。
//淺拷貝
class String
{
public:
String(const char* pStr = "")//構造函數
:_pStr(new char[strlen(pStr)+1])
{
strcpy(_pStr,pStr);
}
String(const String& s)//拷貝構造函數
{
_pStr = s._pStr;
}
String& operator=(String& s)//指派運算符重載
{
if(_pStr != s._pStr)//判斷是不是自己給自己指派
{
_pStr = s._pStr;
}
return *this;
}
~String()//析構函數
{
if(NULL != _pStr)
{
delete []_pStr;
_pStr = NULL;
}
}
private:
char* _pStr;
};
void test()
{
String s1("abcd");
String s2(s1);
String s3 = s2;//調用拷貝構造函數(編譯器會s2直接初始化s3)
String s4;//s4對象已經存在了
s4 = s3;//編譯器會調用指派運算符重載将s3的值賦給s4
}
int main()
{
test();
return 0;
}

S1,S2,S3,S4,指針指向的是同一塊空間,根據棧幀的開辟原則,先對S4,進行析構,然後S4的空間被釋放,S4,接着被置空,接下來堆S3進行析構時,程式崩潰。
既然如此,我們采用深拷貝的方法來構造對象,使得每個對象都擁有獨立的記憶體空間,在記憶體釋放是不會發生多次析構的問題。
class String //深拷貝
{
public:
String(const char* pStr = "")//構造函數
:_pStr(new char[strlen(pStr)+1])
{
strcpy(_pStr,pStr);
}
String(const String& s)//拷貝構造
:_pStr(new char[strlen(s._pStr)+1])
{
strcpy(_pStr,s._pStr);
}
String& operator=(const String& s)//指派運算符重載
{
if(_pStr != s._pStr)//判斷自指派
{
char* temp = new char[strlen(s._pStr)+1];//先開辟一段新空間
strcpy(temp,s._pStr);//将原對象的值賦給新空間
delete []_pStr;//釋放目前對象
_pStr = temp;//将目前對象指向新開辟的空間
}
return *this;
}
~String()//析構
{
if(NULL != _pStr)
{
delete[]_pStr;
_pStr = NULL;
}
}
private:
char* _pStr;
};
void test()
{
String s1("abcd");
String s2(s1);
String s3 = s2;//調用拷貝構造函數(編譯器會s2直接初始化s3)
String s4;//s4對象已經存在了
s4 = s3;//編譯器會調用指派運算符重載将s3的值賦給s4
}
int main()
{
test();
system("pause");
return 0;
}
我們可以看到沒個對象都新開辟了一塊記憶體,在析構的時候不會因為析構多次二發生記憶體洩漏。
深拷貝的現代寫法,基本思想利用臨時對象來構造出需要拷貝的對象,然後将個對象的内容交換。(可以看做是一種剝奪)
//深拷貝(簡潔版2)
class String
{
public:
String(const char* pStr = "")//構造函數
:_pStr(new char[strlen(pStr)+1])
{
if(0 == *pStr)
{
*_pStr = '\0';
}
else
{
strcpy(_pStr,pStr);
}
}
String(String& s)//拷貝構造
:_pStr(NULL)//防止交換後temp指向随機空間,本函數調用結束導緻記憶體洩漏以緻崩潰
{
String temp(s._pStr);//如果不給出臨時變量,交換後s的值将為NULL
std::swap(_pStr,temp._pStr);
}
//1
String& operator=(const String &s)//指派運算符重載
{
if(_pStr != s._pStr)
{
String temp(s._pStr);//如果不給出臨時變量,交換後s的值将為NULL
std::swap(_pStr,temp._pStr);
}
return *this;
}
/* 2
String& operator=(const String& s)
{
if (this != &s)
{
String temp(s);
std::swap(_pStr, temp._pStr);
}
return *this;
}*/
/* 3
String& operator=(String temp)
{
std::swap(_pStr, temp._pStr);
return *this;
}*/
~String()//析構函數
{
if(NULL == _pStr)
{
return;
}
else
{
delete[]_pStr;
_pStr = NULL;
}
}
private:
char* _pStr;
};
void test()
{
String s1("abcd");
String s2(s1);
String s3 = s2;//調用拷貝構造函數(編譯器會s2直接初始化s3)
String s4;//s4對象已經存在了
s4 = s3;//編譯器會調用指派運算符重載将s3的值賦給s4
}
int main()
{
test();
system("pause");
return 0;
}
利用深拷貝實作string類的模拟。(完整版)
String.cpp
#include <iostream>
#include <assert.h>
#include <string.h>
#include "String.h"
using namespace std;
現代寫法 ----- 剝奪 -------利用構造函數建立,然後交換
String::String(const String& s)
:_size(s._size)
, _capacity(s._capacity)
, _str(new char[strlen(s._str) + 1])
{
strcpy(_str, s._str);
}
//String::String(const String& s)
//:_str(NULL)
//{
// //構造函數
// String tmp(s._str);
// //交換
// swap(_str, tmp._str);
//}
String& String::operator=(const String& s)
{
//if (this != &s)//自指派
//{
// delete[]_str; //釋放原有空間
// _str = new char[strlen(s._str) + 1];
// strcpy(_str, s._str);
// _size = s._size;
// _capacity = s._capacity;
//}
//return *this;
String tmp(s._str);
swap(_str, tmp._str);
return *this;
//swap(_str, s._str); //不為錯,額外的記憶體開銷
//return *this;
}
String::~String()
{
if (_str != NULL)
{
delete[]_str;
_str = NULL; //可置可不置空
_size = 0;
_capacity = 0;
}
}
const char* String::c_str()
{
return _str;
}
void String::Swap(String& s) //s1.Swap(s2);
{
assert(this != &s);
swap(_str, s._str);
swap(_size, s._size);
swap(_capacity, s._capacity);
}
void String::Expand(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[_capacity];
strcpy(tmp, _str);
delete[]_str;
_str = tmp;
_capacity = n;
}
}
void String::PushBack(char ch)
{
if (_size >= _capacity)
{
Expand(_capacity * 2);
}
_str[_size++] = ch;
_str[_size] = '\0';
}
void String::PushBack(const char* str)
{
assert(str);
size_t len = strlen(str);
if (_size + len > _capacity)
{
Expand(_size + len);
}
strcpy(_str + _size, str);
_size += len;
}
void String::PushFront(char ch)
{
if (_size + 1 > _capacity) //增容
{
Expand(_capacity * 2);
}
for (int i = _size + 1; i >= 0; --i) //搬移
{
_str[i + 1] = _str[i];
}
_str[0] = ch;
_size++;
}
void String::PushFront(const char* str)
{
assert(str);
size_t len = strlen(str);
if (len + _size > _capacity)
{
Expand(len + _size);
}
for (int i = _size; i >= 0; --i)
{
_str[i + len] = _str[i];
}
memcpy(_str, str, len);
_size += len;
}
void String::Insert(size_t pos, char ch)
{
assert(pos < _size);
if (_size + 1 > _capacity)
{
Expand(_capacity * 2);
}
for (int i = _size; i >= (int)pos; --i) //強轉pos防止發生無限循環
{
_str[i + 1] = _str[i];
}
_str[pos] = ch;
_size++;
}
void String::Insert(size_t pos, const char* str)
{
assert(str);
assert(pos <= _size);
size_t len = strlen(str);
if (len + _size > _capacity)
{
Expand(_size + len);
}
for (int i = _size; i >= (int)pos; --i)
{
_str[i + len] = _str[i];
}
while (*str != '\0')
{
_str[pos++] = *(str++);
}
_size += len;
}
void String::Erase(size_t pos, size_t n)
{
//1.pos
assert(pos < _size);
//pos+n
if (pos + n >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos, _str + pos + n);
_size -= n;
}
}
size_t String::Find(char ch, size_t pos)
{
for (size_t i = pos; i < _size; i++)
{
if (ch == _str[i]) return i;
}
return -1;
}
size_t String::Find(const char* str, size_t pos)
{
assert(str != NULL);
assert(pos < _size);
assert(_size > strlen(str));
const char* src = _str + pos;
while (*src != '\0')//标記母串的查找位置
{
const char* match = str;
const char* cur = src;
while (*match != '\0' && *match == *cur)//子串沒有走到結尾
//子串的目前字元不等于母串
{
match++;
cur++;
}
if (*match == '\0')
{
return src - _str;
}
src++;
}
return -1;
}
void String::Replace(size_t pos, int len, const char* sub2)
{
//pos > _size == _size < _size
//len > strlen(sub2) pos + len > size
//sub2
}
bool String::operator>(const String& s)const
{
size_t i = 0;
while (_str[i] != '\0' && s._str[i] != '\0')
{
if (_str[i] == s._str[i])
i++;
else if (_str[i] > s._str[i])
return true;
else
return false;
}
if (_str[i] != '\0')
return true;
else
return false;
}
bool String::operator>=(const String& s)const
{
return *this > s || *this == s;
}
bool String::operator<(const String& s)const
{
return !(*this >= s);
}
bool String::operator<=(const String& s)const
{
return !(*this > s);
}
bool String::operator==(const String& s)const
{
size_t i = 0;
while (_str[i] != '\0' && s._str[i] != '\0')
{
if (_str[i] == s._str[i])
i++;
else
return false;
}
if (_str[i] == '\0' && s._str[i] == '\0')
return true;
else
return false;
}
bool String::operator!=(const String& s)const
{
return !(*this == s);
}
String String::operator+(char ch)
{
String tmp(*this); //構造
tmp.PushBack(ch);
return tmp;
}
String& String::operator+=(char ch)
{
PushBack(ch);
return *this;
}
String String::operator+(const char* str)
{
String tmp(*this);
tmp.PushBack(str);
return tmp;
}
String& String::operator+=(const char* str)
{
PushBack(str);
return *this;
}
void TestString()
{
String s1("change world");
cout << s1.Find("wor")<<endl;
}
int main()
{
TestString();
return 0;
}
String.h
#pragma
#include <string.h>
#define NULL 0
class String
{
public:
String(const char* str = "")
:_str(new char[strlen(str) + 1])
, _size(strlen(str))
, _capacity(strlen(str)) //size和capacity保持一緻'\0'不算給_capacity
{
strcpy(_str, str);
}
String(const String& s);
String& operator=(const String& s);
~String();
const char* c_str();
void Swap(String& s);
void Expand(size_t n);
void PushBack(char ch);
void PushBack(const char* str);
void PushFront(char ch);
void PushFront(const char* str);
void Insert(size_t pos, char ch);
void Insert(size_t pos, const char* str);
void Erase(size_t pos, size_t n = 1);
void Replace(size_t pos, int len, const char* sub2);
size_t Find(char ch, size_t pos);
size_t Find(const char* str, size_t pos = 0);
String operator+(char ch);
String& operator+=(char ch);
String operator+(const char* str);
String& operator+=(const char* str);
bool operator>(const String& s)const;
bool operator>=(const String& s)const;
bool operator<(const String& s)const;
bool operator<=(const String& s)const;
bool operator==(const String& s)const;
bool operator!=(const String& s)const;
private:
size_t _size;
size_t _capacity;
char* _str;
};