1. 程式人生 > >C++11右值引用實際使用示例

C++11右值引用實際使用示例

class CXyString
{
	/* 用於交換物件內容 */
	friend void swapObj(CXyString & o1, CXyString & o2);
	//也可以直接在這裡實現這個函式


private:
	char * m_buff;
	size_t m_cap;
	size_t m_size;

public:
	/* 0 無參建構函式 */
	CXyString() : m_buff(nullptr), m_size(0), m_cap(0)
	{
		printf("[CREATE EMPTY] %p.\n", this);
	}

	/* 1 建構函式 */
	CXyString(const char * str, size_t len = 0) : CXyString()
	{
		printf("created, set data\n");
		if (str)
			append(str, len);
	}

	/* 2 左值引用建構函式,這裡不可以寫成傳值型別的引數,會導致呼叫過載不明確 */
	CXyString(const CXyString & o) : CXyString(o.data(), o.size())
	{
		printf("deep copy completed!!!\n");
	}

	/* 3 右值引用建構函式 */
	CXyString(CXyString && o) : CXyString()	//一定要呼叫無參建構函式以清空成員變數,不然交換完之後釋放右值時會導致釋放m_buff所指向的無效地址(vs中debug狀態下為0xcccccccc)
	{
		printf("created, move data.\n");
		swapObj(*this, o);	//交換資料內容
	}


	/* 左值版本的賦值 */
	CXyString & operator=(const CXyString & o)	//值傳遞,以達到傳參前自動呼叫複製建構函式的目的
	{
		printf("entered operator=\n");
		if (&o == this)
			return *this;

		CXyString tmp(o);	//複製一個新的,再交換。因為左值引用引數不能影響引數原來的內容
		swapObj(*this, tmp);
		printf("leaving operator=\n");
		return *this;
	}
	
	/* 右值版本的賦值 */
	CXyString & operator=(CXyString && o)
	{
		printf("entered operator=&&\n");		
		if (&o == this)
			return *this;

		swapObj(*this, o);	//直接交換,右值引數是可以修改的,因為沒有任何人能引用到傳進來的右值引數(其實是個臨時變數,用完即棄的那種)				
		printf("leaving operator=&&\n");
		return *this;
	}

	/* 連線字串,傳入記憶體地址 */
	CXyString operator+(const char * str)
	{
		printf("entered operator+\n");
		//if (str == nullptr)
		//	return CXyString(*this);	// return std::move(CXyString(*this));	//會被RVO,vs中測試直接return的可以不move,不會再複製一份

		CXyString & ret = CXyString(*this);	//ret可以是引用也可以為非引用,即使為非引用也不會再複製一份給ret
		ret.append(str);
		printf("leaving operator+\n");
		//避免深複製,std::move返回&&右值引用,接著return會呼叫本類的右值引用版建構函式來轉移m_buff中的資料而非複製。因為ret引數在本函式結束時會被釋放
		return std::move(ret);
	}

	/* 連線字串,傳入物件 */
	CXyString operator+(const CXyString & o)
	{
		return operator+(o.data());
	}

	/* 析構 */
	virtual ~CXyString()
	{
		printf("[DELETE] %p!\n", this);
		clear();
	}

	const char * data() const
	{
		return m_buff;
	}

	const char * c_str() const
	{
		return m_buff;
	}

	size_t size() const
	{
		return m_size;
	}

	size_t length() const
	{
		return m_size;
	}

	size_t cap() const
	{
		return m_cap;
	}

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

	void clear()
	{
		if (m_buff) {
			delete m_buff;
			m_buff = nullptr;
			m_size = 0;
		}
	}

	void reserve(size_t n)
	{
		if (n + 1 <= m_cap)
			return;

		char * tmpBuff = new char[n + 1];
		tmpBuff[0] = '\0';
		if (m_size > 0) {
			memcpy(tmpBuff, m_buff, m_size <= n ? m_size : n);
			tmpBuff[n] = '\0';
			delete m_buff;
		}
		
		m_buff = tmpBuff;
		m_cap = n + 1;
	}

	void append(const char * str, size_t n = 0)
	{
		if (n == 0)
			n = strlen(str);

		if (n == 0)
			return;

		if (m_cap < m_size + n + 1) {
			reserve(m_size + n + 1);
		}

		memcpy(m_buff + m_size, str, n);
		m_buff[m_size + n] = '\0';
		m_size += n;
	}

	void append(const CXyString & o)
	{
		append(o.data(), o.size());
	}
};

/* 用於交換物件內容 */
static void swapObj(CXyString & o1, CXyString & o2)	//類外friend函式,也可以在類內直接實現
{
	std::swap(o1.m_buff, o2.m_buff);    //std::swap用一來交換兩個變數
	std::swap(o1.m_cap, o2.m_cap);
	std::swap(o1.m_size, o2.m_size);
}