1. 程式人生 > >【C++】拷貝建構函式

【C++】拷貝建構函式

目錄

拷貝建構函式

拷貝建構函式的呼叫

預設的拷貝建構函式

深度拷貝 


看完類的建構函式後,再來了解下拷貝建構函式。

拷貝建構函式

拷貝建構函式是一種特殊的建構函式。

(1)它是建構函式,所以函式名就是類名,沒有返回值;

(2)它是特殊的建構函式,引數形式是固定的。

拷貝建構函式的呼叫

拷貝建構函式從來都不會顯式呼叫,而是由編譯器隱式呼叫。

(1)定義物件

Object a;

Object b(a); //或寫成 Object b = a;

(2)動態建立物件

Object a;

Object* p = new Object(a);

(3)函式的傳值呼叫

void Test(Object obj)

#include "pch.h"
#include <iostream>
#include "stdio.h"

class Object
{
public:
	Object(int a, int b)
	{
		this->a = a;
		this->b = b;
	}
	Object(const Object& other)
	{
		printf("in copy constructor... \n");
		this->a = other.a;
		this->b = other.b;
	}
private:
	int a;
	int b;
};

void Test(Object obj) //這裡編譯器其實進行了obj = x;
{
	
}

int main()
{
	Object x(1, 2);
	Object y(x); //與x的地址不相同
	Object* p = new Object(x); //與x,y的地址都不相同
	Test(x);
	
	printf("%p\n", p);
	printf("%p\n", &x);
	printf("%p\n", &y);

	delete p;
	return 0;
}

注意

(1)構造和賦值的區別

構造:
Object a;
Object b(a); // 或寫作Object b = a;
賦值:
Object a(1,2);
Object b;
b = a;

(2)可以訪問同類物件的private成員

在拷貝建構函式,可以訪問引數物件的任意成員。因為他們是同類,所以訪問不受限制。

Object(const Object& other)
{
    this->a = other.a;
}

預設的拷貝建構函式

當你自己沒有寫拷貝建構函式時,編譯器會預設提供一個拷貝建構函式,預設的拷貝動作:將每一個成員逐個拷貝。

所以除非必要,請不要自己加拷貝建構函式。

如果一定要自己寫拷貝建構函式,請仔細檢查:

(1)所有的成員變數,要依次拷貝所有成員變數,不要遺漏;

(2)呼叫父類的拷貝建構函式。

#include "pch.h"
#include <iostream>
#include "stdio.h"

class Base
{
public:
	int test_base;
};

class Object : public Base
{
public:
	Object()
	{
		a = b = 1;
	}
	Object(int a, int b)
	{
		this->a = a;
		this->b = b;
	}
	Object(const Object& other) : Base(other) //如果不加Base(other),那麼父類的成員就不會被拷貝
	{
		printf("in copy constructor... \n");
		this->a = other.a;
		this->b = other.b;
	}
private:
	int a;
	int b;
};

int main()
{
	Object x(1, 2);
	x.test_base = 10;
	Object y(x); 

	return 0;
}

既然編譯器會預設幫我們加拷貝建構函式,那麼為什麼還要自己加呢?

只有一種情況,那就是需要深度拷貝的時候才會自己新增拷貝建構函式。

深度拷貝 

深度拷貝其實就是我不想將指標直接copy過來,而是用一個新的指標指向copy過來的資料。。

#include "pch.h"
#include <iostream>
#include "stdio.h"
#include "string.h"

class Text
{
public:
	Text(const char* str)
	{
		m_size = strlen(str) + 1;
		m_buf = new char[m_size];
		strcpy(m_buf, str);
	}
	~Text()
	{
		delete [] m_buf;
	}

	const char* GetText()
	{
		return m_buf;
	}
private:
	int m_size;
	char* m_buf;
};


int main()
{
	Text t1("hello,world");
	Text t2(t1);

	return 0;
}

上面這段程式碼,在vs2017下居然能跑通,除錯過程中發現t1和t2的m_buf是同一塊地址,但是程式好像不能正常結束,這使得我非常困惑。但我依舊認為它是不合理的。具體原因如下:

物件建立:

(1)物件t1.m_buf,指向一塊記憶體;

(2)物件t2拷貝了t1,t2.m_buf指向了同一塊記憶體。

物件析構:

(1)物件t1析構,delete [] m_buf;

(2)物件t2析構,delete [] m_buf;出錯,此塊記憶體已經被delete了。

究其根本,應該拷貝其資料,不應該拷貝他的指標。

修改方式一:

#include "pch.h"
#include <iostream>
#include "stdio.h"
#include "string.h"

class Text
{
public:
	Text(const char* str)
	{
		m_size = strlen(str) + 1;
		m_buf = new char[m_size];
		strcpy(m_buf, str);
	}

    //後加的拷貝建構函式
	Text(const Text& other)
	{
		m_size = other.m_size;
		m_buf = new char[m_size];
		strcpy(m_buf, other.m_buf);
	}

	~Text()
	{
		delete [] m_buf;
	}

	const char* GetText()
	{
		return m_buf;
	}
private:
	int m_size;
	char* m_buf;
};


int main()
{
	Text t1("hello,world");
	Text t2(t1);

	printf("%p \n", &t1);
	printf("%p \n", &t2);

	return 0;
}

修改方式二:(這種方式有點流氓,直接不讓你用拷貝建構函式,然後你只能自己慢慢賦值把)

#include "pch.h"
#include <iostream>
#include "stdio.h"
#include "string.h"

class Text
{
public:
	Text(const char* str)
	{
		m_size = strlen(str) + 1;
		m_buf = new char[m_size];
		strcpy(m_buf, str);
	}

	~Text()
	{
		delete [] m_buf;
	}

	const char* GetText()
	{
		return m_buf;
	}
private:
	Text(const Text& other)
	{}
private:
	int m_size;
	char* m_buf;
};


int main()
{
	Text t1("hello,world");

	printf("%p \n", &t1);

	return 0;
}