天天看點

[C++系列] 51. String類的模拟實作及深淺拷貝詳解、寫時拷貝補充

1.經典的string類問題

在面試中,面試官總喜歡讓學生自己來模拟實作string類,最主要是實作String類的構造、拷貝構造、指派運算符重載以及析構函數。大家看下以下string類的實作是否有問題?

class String
{
public:
    String(const char* str = "")
    {
        // 構造string類對象時,如果傳遞nullptr指針,認為程式非法,此處斷言下
        if(nullptr == str)
        {
            assert(false);
            return;
        }
        
        _str = new char[strlen(str) + 1];
        strcpy(_str, str);
    }
    
    ~String()
    {
        if(_str)
        {
            delete[] _str;
            _str = nullptr;
        }
    }
    
private:
    char* _str;
};
 
// 測試
void TestString()
{
    String s1("hello bit!!!");
    String s2(s1);
}

           
[C++系列] 51. String類的模拟實作及深淺拷貝詳解、寫時拷貝補充

說明:上述String類沒有顯式定義其拷貝構造函數與指派運算符重載,此時編譯器會合成預設的,當用s1構

造s2時,編譯器會調用預設的拷貝構造。最終導緻的問題是,s1、s2共用同一塊記憶體空間,在釋放時同一塊

空間被釋放多次而引起程式崩潰,這種拷貝方式,稱為淺拷貝。

淺拷貝:也稱位拷貝,編譯器隻是将對象中的值拷貝過來。如果對象中管理資源,最後就會**導緻多個對象共享同一份資源,當一個對象銷毀時就會将該資源釋放掉,而此時另一些對象不知道該資源已經被釋放,以為還有效,是以 當繼續對資源進項操作時,就會發生發生了通路違規。**要解決淺拷貝問題,C++中引入了深拷

貝。

深拷貝 :如果一個類中涉及到資源的管理,其拷貝構造函數、指派運算符重載以及析構函數必須要顯式給出。一般情況都是按照深拷貝方式提供。

[C++系列] 51. String類的模拟實作及深淺拷貝詳解、寫時拷貝補充

寫時拷貝: 寫時拷貝就是一種拖延症,是在淺拷貝的基礎之上增加了引用計數的方式來實作的。

引用計數:用來記錄資源使用者的個數。在構造時,将資源的計數給成1,每增加一個對象使用該資源,就給

計數增加1,當某個對象被銷毀時,先給該計數減1,然後再檢查是否需要釋放資源,如果計數為1,說明該

對象時資源的最後一個使用者,将該資源釋放;否則就不能釋放,因為還有其他對象在使用該資源。

寫時拷貝

寫時拷貝在讀取時的缺陷

string的模拟實作代碼:

#pragma once
#include <string.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
#define DEFAULTCAPA 16
#define COUNTCAPA(size) (((size) / DEFAULTCAPA + 1) * DEFAULTCAPA)


class String
{
	char * m_data;
	size_t m_size;
	size_t m_capacity;
public:
	String(const char * str = "") :
		m_capacity(DEFAULTCAPA)
	{
		if (nullptr == str)
		{
			str = "";
		}

		m_size = strlen(str);
		m_capacity = COUNTCAPA(m_size);
		m_data = new char[m_capacity];
		strncpy(m_data, str, m_size);
	}

	String(String &s)
	{
		m_size = s.m_size;
		m_capacity = s.m_capacity;

		m_data = new char[m_capacity];
		strcpy(m_data, s.m_data);
	}

	String & operator = (const char * str)
	{
		m_size = strlen(str);
		reserve(m_size);

		strcpy(m_data, str);
		return *this;
	}

	String & operator = (String &s)
	{
		m_size = s.m_size;
		reserve(m_size);

		strcpy(m_data, s.m_data);
		return *this;
	}

	String(size_t n, char ch):
		m_size(n),
		m_capacity(COUNTCAPA(n))
	{
		m_data = new char[m_capacity];
		memset(m_data, ch, m_size);
	}

	~String()
	{
		if (m_data)
		{
			delete[]m_data;
			m_data = NULL;
		}
		m_size = m_capacity = 0;
	}

	size_t size()
	{
		return m_size;
	}

	size_t length()
	{
		return m_size;
	}

	size_t capacity()
	{
		return m_capacity - 1;
	}

	bool empty()
	{
		return m_size == 0;
	}

	void clear()
	{
		m_size = 0;
	}

	void reserve(size_t size)
	{
		if (size >= m_capacity)
		{
			m_capacity = COUNTCAPA(size);
			m_data = (char *)realloc(m_data, m_capacity);
		}
	}

	void resize(size_t size, char ch = '\0')
	{
		reserve(size);

		if (m_size < size)
		{
			memset(m_data + m_size, ch, size - m_size);
		}
		m_size = size;
	}

	char &operator[](int i)
	{
		return m_data[i];
	}

	typedef char * iterator;

	iterator begin()
	{
		return m_data;
	}

	iterator end()
	{
		return m_data + m_size;
	}

	void push_back(char ch)
	{
		reserve(m_size);

		m_data[m_size] = ch;
		m_size++;
	}

	void pop_back(char ch)
	{
		if (!empty())
		{ 
			m_size--;
		}
	}

	String & operator += (const char * str)
	{
		int tmp = m_size;
		m_size += strlen(str);
		reserve(m_size);

		strcpy(m_data + tmp, str);
		return *this;
	}

	String & operator += (String &s)
	{
		int tmp = m_size;
		m_size += s.m_size;
		reserve(m_size);

		strcpy(m_data + tmp, s.m_data);
		return *this;
	}

	/*作業:append 兩種,一種n個ch的,一種直接續字元串或容器的*/

	const char * c_str()
	{
		return m_data;
	}

	size_t find(char ch, int pos = 0)
	{
		if (pos < 0 || pos >= m_size)
		{
			return -1;
		}

		char * tmp = strchr(m_data + pos, ch);

		if (tmp)
		{
			return tmp - m_data;
		}
		return -1;
	}

	size_t find(const char *str, int pos = 0)
	{
		if (pos < 0 || pos >= m_size)
		{
			return -1;
		}

		char * tmp = strstr(m_data + pos, str);

		if (tmp)
		{
			return tmp - m_data;
		}
		return -1;
	}

	size_t find(const String &s, int pos = 0)
	{
		if (pos < 0 || pos >= m_size)
		{
			return -1;
		}

		char * tmp = strstr(m_data + pos, s.m_data);

		if (tmp)
		{
			return tmp - m_data;
		}
		return -1;
	}


	String substr(size_t start, size_t num)
	{
		String tmp;

		tmp.resize(num);

		strncpy(tmp.m_data, m_data + start, num);
		return tmp;
	}

	String operator + (const char * str) const;
	String operator + (const String &s) const;
	friend String operator +(const char * str, const String &s);

	bool operator >  (const char * str) const;
	bool operator <  (const char * str) const;
	bool operator >= (const char * str) const;
	bool operator <= (const char * str) const;
	bool operator == (const char * str) const;
	bool operator != (const char * str) 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;
	bool operator != (const String &s) const;

	friend bool operator >  (const char * str, const String &s);
	friend bool operator <  (const char * str, const String &s);
	friend bool operator >= (const char * str, const String &s);
	friend bool operator <= (const char * str, const String &s);
	friend bool operator == (const char * str, const String &s);
	friend bool operator != (const char * str, const String &s);

	friend ostream & operator << (ostream & os, const String &s);
	friend istream & operator >> (istream & is, String &s);
};

ostream & operator << (ostream & os, const String &s)
{
	int i;

	for (i = 0; i < s.m_size; i++)
	{
		os << s.m_data[i];
	}

	return os;
}

istream & operator >> (istream & is, String &s)
{
	char * tmp = new char[1024];
	is.getline(tmp, 1024);

	s.m_size = strlen(tmp);
	s.m_capacity = COUNTCAPA(s.m_size);
	strcpy(s.m_data, tmp);
	delete[]tmp;

	/*delete[]s.m_data;
	s.m_data = tmp;*/
	return is;
}


String String::operator + (const char * str) const
{
	String res;

	res.m_size = m_size + strlen(str);
	res.reserve(res.m_size);

	strncpy(res.m_data, m_data, m_size);
	strcpy(res.m_data + m_size, str);

	return res;
}

String String::operator + (const String &s) const
{
	String res;

	res.m_size = m_size + s.m_size;
	res.reserve(res.m_size);

	strncpy(res.m_data, m_data, m_size);
	strncpy(res.m_data + m_size, s.m_data, s.m_size);

	return res;
}

String operator +(const char * str, const String &s)
{
	String res;

	res.m_size = strlen(str) + s.m_size;
	res.reserve(res.m_size);

	strcpy(res.m_data, str);
	strncat(res.m_data, s.m_data, s.m_size);

	return res;
}

bool String::operator >  (const char * str) const
{
	return strncmp(m_data, str, m_size) > 0;
}

bool String::operator <  (const char * str) const
{
	int tmp = strncmp(m_data, str, m_size);
	if (!tmp && strlen(str) > m_size)
	{
		return true;
	}
	return tmp < 0;
}

bool String::operator >= (const char * str) const
{
	return !(*this < str);
}

bool String::operator <= (const char * str) const
{
	return !(*this > str);
}

bool String::operator == (const char * str) const
{
	int tmp = strncmp(m_data, str, m_size);
	if (!tmp && strlen(str) == m_size)
	{
		return true;
	}
	return false;
}

bool String::operator != (const char * str) const
{
	return !(*this == str);
}

bool String::operator >  (const String &s) const
{
	int i;
	int minsize = m_size < s.m_size ? m_size : s.m_size;
	for (i = 0; i < minsize; i++)
	{
		if (m_data[i] > s.m_data[i])
		{
			return true;
		}
		else if (m_data[i] < s.m_data[i])
		{
			return false;
		}
	}
	return m_size > s.m_size;
}

bool String::operator <  (const String &s) const
{
	int i;
	int minsize = m_size < s.m_size ? m_size : s.m_size;
	for (i = 0; i < minsize; i++)
	{
		if (m_data[i] < s.m_data[i])
		{
			return true;
		}
		else if (m_data[i] > s.m_data[i])
		{
			return false;
		}
	}
	return m_size < s.m_size;
}

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
{
	int i;
	int minsize = m_size < s.m_size ? m_size : s.m_size;
	for (i = 0; i < minsize; i++)
	{
		if (m_data[i] != s.m_data[i])
		{
			return false;
		}
	}
	return m_size == s.m_size;
}

bool String::operator != (const String &s) const
{
	return !(*this == s);
}


bool operator >  (const char * str, const String &s)
{
	int tmp = strncmp(str, s.m_data, s.m_size);
	if (!tmp && strlen(str) > s.m_size)
	{
		return true;
	}
	return tmp > 0;
}

bool operator <  (const char * str, const String &s)
{
	return strncmp(str, s.m_data, s.m_size) < 0;
}

bool operator >= (const char * str, const String &s)
{
	return !(str < s);
}

bool operator <= (const char * str, const String &s)
{
	return !(str > s);
}

bool operator == (const char * str, const String &s)
{
	int tmp = strncmp(s.m_data, str, s.m_size);
	if (!tmp && strlen(str) == s.m_size)
	{
		return true;
	}
	return false;
}
bool operator != (const char * str, const String &s)
{
	return !(str == s);
}
           

這個實作的比較詳細,其中最主要是實作String類的構造、拷貝構造、指派運算符重載以及析構函數。

繼續閱讀