繼續來學習構造函數,前面一篇學習了拷貝構造函數,至少了解了什麼是拷貝構造函數,這篇就來學習下拷貝構造的使用。什麼時候會使用到拷貝構造函數呢?大概可以分成三種場景:
- 使用一個已經創建完畢的對象來初始化一個新對象
- 值傳遞的方式給函數參數傳值
- 以值方式返回局部對象
使用一個已經創建完畢的對象來初始化一個新對象
這個很好理解,主要是前面已經花了經歷去構造一個對象,如果我們再來一個這樣的對象,我們很容易想到克隆,當前我們沒有提供克隆方法,就可以通過拷貝構造來初始化一個新對象。
還是基於前面的代碼,這裏我們把屬性權限改成public,這裏就不寫set和get方法,方便這裏少一點代碼
#include<iostream>
using namespace std;
class Point
{
public:
//構造函數
Point()
{
cout << "調用了構造函數"<< endl;
}
//有參構造
Point(int x, int y)
{
cout << "調用了有參構造函數" << endl;
X = x;
Y = y;
}
//拷貝構造
Point(const Point &p)
{
cout << "調用了拷貝構造函數" << endl;
//將p的屬性拷貝到當前類的初始化
X = p.X;
Y = p.Y;
}
//析構函數
~Point()
{
cout << "調用了析構函數" << endl;
}
public:
int X;
int Y;
};
void test01()
{
Point p1(10, 10);
Point p2(p1); // 拷貝構造
cout << "P2的X軸座標是:" << p2.X << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
運行結果
上面打印了p2的X座標是10,說明拷貝過來了,這種場景是我們使用拷貝構造函數的最多場景。
值傳遞的方式給函數參數傳值
先看下面代碼,再來解釋,這個有一點點繞,但是隻要記住一點就好理解。值傳遞是,先拷貝出一個值的副本,然後再傳遞給形參。對這句話理解很重要。
#include<iostream>
using namespace std;
class Point
{
public:
//構造函數
Point()
{
cout << "調用了構造函數"<< endl;
}
//有參構造
Point(int x, int y)
{
cout << "調用了有參構造函數" << endl;
X = x;
Y = y;
}
//拷貝構造
Point(const Point &p)
{
cout << "調用了拷貝構造函數" << endl;
//將p的屬性拷貝到當前類的初始化
X = p.X;
Y = p.Y;
}
//析構函數
~Point()
{
cout << "調用了析構函數" << endl;
}
public:
int X;
int Y;
};
void doSomething(Point p)
{
// do something
}
void test01()
{
Point p1;
doSomething(p1);
}
int main()
{
test01();
system("pause");
return 0;
}
運行結果:
藉助上面紅圈和箭頭,我們來分析下這個代碼的執行過程。
1)執行Point p1, 這個肯定調用了默認的無參構造函數,這個理解肯定沒問題
2)開始執行doSothing(p1),p1是上一行代碼定義的,這是一個實參,doSomething(People p)這個p是形參
3)doSothing(p1)中傳入的p1(51行代碼)是實參p1(50行代碼)的一個副本,因爲這裏是值傳遞。
4)這是是副本,就是一個拷貝過程,編譯器幫我們做了這個拷貝過程,所以執行了拷貝構造函數的調用
5)需要強調,假如doSomething函數中修改了X和Y的值,這個和50行代碼中p1的X和Y的值沒有任何影響,因爲兩個是完全不相同的兩個對象。
以值方式返回局部對象
也是直接來看代碼,然後進行分析
#include<iostream>
using namespace std;
class Point
{
public:
//構造函數
Point()
{
cout << "調用了構造函數"<< endl;
}
//有參構造
Point(int x, int y)
{
cout << "調用了有參構造函數" << endl;
X = x;
Y = y;
}
//拷貝構造
Point(const Point &p)
{
cout << "調用了拷貝構造函數" << endl;
//將p的屬性拷貝到當前類的初始化
X = p.X;
Y = p.Y;
}
//析構函數
~Point()
{
cout << "調用了析構函數" << endl;
}
public:
int X;
int Y;
};
Point doSomething()
{
Point p1;
return p1;
}
void test01()
{
Point p = doSomething();
}
int main()
{
test01();
system("pause");
return 0;
}
運行結果
分析:
主要看doSomething()函數,第一步,Point p1,調用的時候走的是默認無參構造函數。這個函數裏面定義的Point p1是一個局部變量,函數執行完畢就會自動銷燬。關鍵點就是在這裏,return p1 這個p1和Point p1不是同一個對象,這裏是以值返回,返回拿到的這個p1是45行代碼這個p1的一個拷貝副本,所以這個時候調用了拷貝構造。
我們可以通過打印內存地址來看看p1和外面接收的p的地址是不是一樣的