C++的構造函數的作用:初始化類對象的數據成員。
即類的對象被創建的時候,編譯系統對該對象分配內存空間,并自動調用構造函數,完成類成員的初始化。
構造函數的特點:以類名作為函數名,無返回類型。
常見的構造函數有三種寫法:
- 無參構造函數
- 一般構造函數
- 復制構造函數
C++的構造函數可以有多個,創建對象時編譯器會根據傳入的參數不同調用不同的構造函數。
1、無參構造函數
如果創建一個類,沒有寫任何構造函數,則系統會自動生成默認的無參構造函數,且此函數為空。
默認構造函數(default constructor)就是在沒有顯式提供初始化式時調用的構造函數。如果定義某個類的變量時沒有提供初始化時就會使用默認構造函數。
但只要有下面某一種構造函數,系統就不會再自動生成這樣一個默認的構造函數。如果希望有一個這樣的無參構造函數,則需要顯示地寫出來。
#include <IOStream>
using namespace std;
class Student {
public:
int m_age;
int m_score;
// 1. 無參構造函數
Student() {
m_age = 10;
m_score = 99;
cout << "1. 無參構造函數" << endl;
}
};
2、一般構造函數
一般構造函數有兩種寫法:
- 初始化列表方式:以一個冒號開始,接著是以逗號分隔的數據成員列表,每個數據成員后面跟一個放在括號中的初始化值。
- 內部賦值方式:正常函數的賦值。
#include <iostream>
using namespace std;
class Student {
public:
int m_age;
int m_score;
// 2. 一般構造函數
// 初始化列表方式
Student(int age, int score) :
m_age(age),
m_score(score)
{}
// 內部賦值方式
Student(int age, int score) {
m_age = age;
m_score = score;
}
};
C++規定,對象的成員變量的初始化動作發生在進入構造函數本體之前。也就是說采用初始化列表的話,構造函數本體實際上不需要有任何操作,因此效率更高。
一般構造函數可以有多種參數形式,即一個類可以有多個一般構造函數,前提是參數的個數或者類型不同(C++的函數重載機制)。
C++覆蓋和重載的區別
#include <iostream>
using namespace std;
class Student {
public:
int m_age;
int m_score;
// 2. 一般構造函數
Student(int age, int score) {
m_age = age;
m_score = score;
cout << "2.1 一般構造函數" << endl;
}
Student(int age) {
m_age = age;
cout << "2.2 一般構造函數" << endl;
}
};
3、復制構造函數
復制構造函數,也稱為拷貝構造函數。
復制構造函數參數為類對象本身的引用,根據一個已存在的對象復制出一個新的對象,一般在函數中會將已存在對象的數據成員的值復制一份到新創建的對象中。
#include <iostream>
using namespace std;
class Student {
public:
int m_age;
int m_score;
// 3. 復制構造函數
Student(Student& s) {
m_age = s.m_age;
m_score = s.m_score;
cout << "3. 復制構造函數" << endl;
}
};
注意:若沒有顯示定義復制構造函數,則系統會默認創建一個復制構造函數,當類中有指針成員時,由系統默認創建的復制構造函數會存在“淺拷貝”的風險,因此必須顯示定義復制構造函數。
- 淺拷貝指的是在對對象復制時,只對對象中的數據成員進行簡單的賦值,若存在動態成員,就是增加一個指針,指向原來已經存在的內存。這樣就造成兩個指針指向了堆里的同一個空間。當這兩個對象生命周期結束時,析構函數會被調用兩次,同一個空間被兩次free。
- 深拷貝就是對于對象中的動態成員,不是簡單的賦值,而是重新分配空間。

構造函數的調用示例如下:
int main()
{
Student stu1; // 調用無參構造函數
Student stu21(21, 20); // 調用一般構造函數
Student stu22(22); // 調用一般構造函數
Student stu3(stu1); // 調用復制構造函數
return 0;
}