一、對象的拷貝
拷貝和復制是一個意思,對計算機來說就是利用已經存在的數據創建出一份新的和原有數據一模一樣的數據。在C++中,拷貝是指用已經存在的對象創建出一個新的對象。嚴格來說,對象的創建包括兩個階段,首先要分配內存空間,然后再進行初始化。
- 分配內存空間就是在,就是在堆、棧或者全局數據區留出足夠的字節空間,它所包含的數據一般是零或者隨機值,沒有什么實際意義。
- 初始化就是首次對留出的這段字節空間賦值,讓里面的數據具有實際意義。這里一定得是首次賦值,再次賦值就不是初始化了。
這里的拷貝是在初始化階段進行的,就是用其它的對象的數據來初始化新對象的內存。
二、以拷貝方式來初始化對象的例子
#include<iostream>
#include<sttring>
using namespace std;
void func(string str)
{
cout<<str<<endl;
}
int main()
{
string str1 = "Hello String";
string str2(str1);
string str3 = str1;
string str4 = str1 + " " +str2;
func(str1);
cout<<str1<<endl<<str2<<endl<<str3<<endl<<str4<<endl;
return 0;
}
/*
運行結果:
Hello String
Hello String
Hello String
Hello String
Hello String Hello String
*/
上述例子中,str1,str3,str3,str4以及 func() 的形參 str, 都是使用拷貝的方式來初始化的。
- 對於 str1,表面上看起來是將一個字符串直接賦值給了 str1,實際上在內部進行了類型轉換,將 const char * 類型轉換為 string 類型后才賦值的。
- 對於 func() 的形參 str,其實在定義時就為它分配了內存,但是此時並沒有初始化,只有等到調用 func() 時,才會將其它對象的數據拷貝給 str 以完成初始化。
當以拷貝的方式初始化一個對象時,會調用一個特殊的構造函數,就是拷貝構造函數。
三、拷貝構造函數的定義及使用
#include<iostream>
#include<string>
using namespace std;
class Student
{
public:
Student(string name="" ,int age=0); //普通構造函數
Student(const Student &stu); //拷貝構造函數(聲明)
~Student();
void display();
private:
string m_name;
int m_age;
};
Student::Student(string name, int age )
{
m_name = name;
m_age = age;
}
Student::Student(const Student &stu) //拷貝構造函數(定義)
{
this->m_name = stu.m_name;
this->m_age = stu.m_age;
cout << "Copy constructor was called." << endl;
}
Student::~Student()
{
}
void Student::display()
{
cout << m_name << " " << m_age << endl;
}
int main()
{
Student stu1("Student", 13);
Student stu2 = stu1; //調用拷貝構造函數
Student stu3(stu1); //調用拷貝構造函數
stu1.display();
stu2.display();
stu3.display();
return 0;
}
/*
輸出:
Copy constructor was called.
Copy constructor was called.
Student 13
Student 13
Student 13
*/
1. 拷貝構造函數的參數為什么是當前類的引用?
如果拷貝構造函數的參數不是當前類的引用,而是當前類的對象,那么在調用拷貝構造函數時,會將另外一個對象直接傳遞給形參,這本身就是一次拷貝,會再次調用拷貝構造函數,然后又將一個對象直接傳遞給了形參,將繼續調用拷貝構造函數……這個過程會一直持續下去,沒有盡頭,陷入死循環。
2. 為什么是const 引用?
拷貝構造函數的目的是用其它對象的數據來初始化當前對象,並沒有期望更改其它對象的數據,添加 const 限制后,這個含義更加明確了。
另外一個原因是,添加 const 限制后,可以將 const 對象和非 const 對象傳遞給形參了,因為非 const 類型可以轉換為 const 類型。如果沒有 const 限制,就不能將 const 對象傳遞給形參,因為 const 類型不能轉換為非 const 類型,這就意味着,不能使用 const 對象來初始化當前對象了。
四、默認拷貝構造函數
如果程序員沒有顯式地定義拷貝構造函數,那么編譯器會自動生成一個默認的拷貝構造函數。這個默認的拷貝構造函數很簡單,就是使用“老對象”的成員變量對“新對象”的成員變量進行一一賦值,和上面 Student 類的拷貝構造函數非常類似。
對於簡單的類,默認拷貝構造函數一般是夠用的,我們也沒有必要再顯式地定義一個功能類似的拷貝構造函數。但是當類持有其它資源時,如動態分配的內存、打開的文件、指向其他數據的指針、網絡連接等,默認拷貝構造函數就不能拷貝這些資源,我們必須顯式地定義拷貝構造函數,以完整地拷貝對象的所有數據 。