【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;
}