C++深淺拷貝和寫時拷貝圖文詳解

前言

之前我們在淺談6個成員函數中有提到深淺拷貝的問題,現在再回首掏一把。

一、深淺拷貝哪傢強?

先給出代碼理一理

#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include<assert.h>
using namespace std;

class String
{
	friend ostream& operator<<(ostream &out, const String &s);
public:
	String(const char* str = "")
	{
		m_data = new char[strlen(str) + 1];
		strcpy(m_data, str);
	}
	//String(const String& s)//qian拷貝
	//{
	//	m_data = s.m_data;
	//}
	String(const String& s)//深拷貝
	{
		m_data = new char[strlen(s.m_data) + 1];
		strcpy(m_data, s.m_data);
	}
	String& operator=(const String& s)
	{
		if (this != &s)
		{
			delete[]m_data;
			m_data = new char[strlen(s.m_data) + 1];
			strcpy(m_data, s.m_data);
		}
		return *this;
	}
	~String()
	{
		delete[]m_data;
		m_data = nullptr;
	}
private:
	char* m_data;
};

ostream& operator<<(ostream &out, const String &s)
{
	out << s.m_data;
	return out;
}

void main()
{
	String s1("abc");
	String s2 = s1;
	cout << "s1 = " << s1 << endl;
	cout << "s2 = " << s2 << endl;
}

而我們之前所說的淺拷貝崩潰是因為doublefree的問題,因此我們可以定義一個引用計數器,來記錄當前使用該值的對象數,如果數目大於1,則不釋放內存。

class String
{
	friend ostream& operator<<(ostream &out, const String &s);
public:
	String(const char* str = "")
	{
		m_data = new char[strlen(str) + 1];
		strcpy(m_data, str);
		m_count++;
	}
	String(const String& s)//淺拷貝
	{
		m_data = s.m_data;
		m_count++;
	}
	String& operator=(const String& s)
	{
		if (this != &s)
		{
			m_data = s.m_data;
			m_count++;
		}
		return *this;
	}
	~String()//淺賦值
	{
		if (--m_count == 0)
		{
			delete[]m_data;
			m_data = nullptr;
		}
	}
private:
	char* m_data;
	static int m_count;//引用計數器
};
int String::m_count = 0;

ostream& operator<<(ostream &out, const String &s)
{
	out << s.m_data;
	return out;
}

void main()
{
	String s1("abc");
	String s2 = s1;
	String s3;
	s3 = s2;
	cout << "s1 = " << s1 << endl;
	cout << "s2 = " << s2 << endl;
	cout << "s3 = " << s3 << endl;
}

可以看出,三個對象的m_data共享同一塊內存空間,節省瞭資源;

但是暴露出瞭很多的問題:站在對象的角度,其中一個對象改變m_data其他的對象也會隨之改變;其二若s3使用其他字符串初始化,但計數器還是三者共享。

倘若我們使用深拷貝方法,就不會出現這種問題。如果可以在不改變m_data前使用淺拷貝,在改變時使用深拷貝,暨同時實現深淺拷貝,那麼就兩全其美。

二、寫時拷貝

通過對上面問題的分析,我們需要實現:引用計數器管理不同的空間。

class String_rep
{
public:
	String_rep(const char* str = "") :m_count(0)
	{
		m_data = new char[strlen(str) + 1];
		strcpy(m_data, str);
		cout << "creat" << endl;
	}
	String_rep(const String_rep &rep) :m_count(0)
	{
		m_data = rep.m_data;
		increment();
	}
	String_rep & operator=(const String_rep &rep)
	{
		if (this != &rep)
		{
			m_data = rep.m_data;
			increment();
		}
		return *this;
	}
public:
	void increment()
	{m_count++;}
	void decrement()
	{m_count--;} 
private:
	char* m_data;
	int m_count;
};
class String
{
public:
	String(const char* str = "") :pn(new String_rep(str))
	{
		pn->increment();
	}
	~String()
	{
		cout << "Free" << endl;
	}
private:
	String_rep *pn;
};
void main()
{
	String s1("abc");
}

拷貝構造:s1和s2管理同一塊空間

定義s3,和s1、s2沒有關聯;

我們再完全理一遍:

此時已經解決我們之前提到過的第二個問題。

再來看第一個問題:

s1的改變影響瞭s2;

寫時拷貝:需要改變的時候深拷貝。

void to_upper()
	{
		String_rep *new_pn = new String_rep(pn->Getdata());//創建新空間
		pn->decrement();//原空間計數器減一
		pn = new_pn;//需要更改的對象的pn指向新空間
		pn->increment();//新空間的計數器加一
		char* p = pn->Getdata();
		while (*p != '\0')
		{
			if (*p >= 'a' && *p <= 'z')
				*p -= 32;
			p++;
		}

總結

到此這篇關於C++深淺拷貝和寫時拷貝的文章就介紹到這瞭,更多相關C++深淺拷貝 寫時拷貝內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!