第三章 類
第二節 構造函數詳解, explicit, 初始化列表
1. 構造函數
-
在類中,有一種特殊的成員函數,他的名字和類名相同,我們在創建類的對象的時候,這個特殊的成員函數就會被系統自動調用,這個成員函數就叫做"構造函數",因爲構造函數會被系統自動調用,我們可以理解構造函數的目的就是初始化類對象的數據成員
-
特點
-
構造函數沒有返回值(沒有void),這也是構造函數的特殊之處
-
不可以手工調用構造函數,否則編譯就會出錯
-
正常情況下,構造函數應該被聲明爲public,因爲我們創建一個對象時,系統要替我們調用public函數,
因爲類缺省的成員是私有成員,必須說明構造函數爲public -
構造函數中如果有多個參數,則我們創建對象的時候也要帶上這些參數
- Time.h
//
// Created by felaim on 2020/5/26.
//
#ifndef LESSION_PART_TWO_TIME_H
#define LESSION_PART_TWO_TIME_H
class Time {
private:
// 私有成員變量不能在外部直接用成員變量名使用
// 可以自己的成員函數調用
int Millisecond;//毫秒
void initMillTime(int mls);
public:
int Hour;
int Minute;
int Second;
// 成員函數
void initTime(int tmphour, int tmpmin, int tmpsec);
public:
//構造函數
Time(int tmphour, int tmpmin, int tmpsec);
};
#endif //LESSION_PART_TWO_TIME_H
- Time.cpp
//
// Created by felaim on 2020/5/26.
//
#include "Time.h"
// 成員函數initTime的實現
// ::叫做作用域運算符,表示initTime屬於Time類
void Time::initTime(int tmphour, int tmpmin, int tmpsec) {
//成員函數中可以直接使用成員變量名
// 哪個對象調用的成員函數,這些成員變量就屬於對應的對象
Hour = tmphour;
Minute = tmpmin;
Second = tmpsec;
initMillTime(25);
}
void Time::initMillTime(int mls) {
Millisecond = mls; //成員函數訪問成員變量
}
// 構造函數的實現
Time::Time(int tmphour, int tmpmin, int tmpsec) {
Hour = tmphour;
Minute = tmpmin;
Second = tmpsec;
initMillTime(0);
}
- main.cpp
#include <iostream>
#include "Time.h"
using namespace std;
int main() {
Time myTime(11, 44, 43);
cout << "myTime: " << myTime.Hour << ":" << myTime.Minute << ":" << myTime.Second << endl;
Time myTime1 = Time(1, 2, 3);
Time myTime2{1, 2, 3};
Time myTime3 ={1, 2, 3};
Time myTime4 = Time{1, 2, 3};
return 0;
}
2. 多個構造函數
-
一個類中可以有多個構造函數,就可以爲類對象的創建提供多種初始化方法,
但是多個構造函數要有所區別,不能完全相同 -
Time.h
//
// Created by felaim on 2020/5/26.
//
#ifndef LESSION_PART_TWO_TIME_H
#define LESSION_PART_TWO_TIME_H
class Time {
private:
// 私有成員變量不能在外部直接用成員變量名使用
// 可以自己的成員函數調用
int Millisecond;//毫秒
void initMillTime(int mls);
public:
int Hour;
int Minute;
int Second;
// 成員函數
void initTime(int tmphour, int tmpmin, int tmpsec);
public:
//構造函數
Time(int tmphour, int tmpmin, int tmpsec);
//不加參數
Time();
};
#endif //LESSION_PART_TWO_TIME_H
- Time.cpp
//
// Created by felaim on 2020/5/26.
//
#include "Time.h"
// 成員函數initTime的實現
// ::叫做作用域運算符,表示initTime屬於Time類
void Time::initTime(int tmphour, int tmpmin, int tmpsec) {
//成員函數中可以直接使用成員變量名
// 哪個對象調用的成員函數,這些成員變量就屬於對應的對象
Hour = tmphour;
Minute = tmpmin;
Second = tmpsec;
initMillTime(25);
}
void Time::initMillTime(int mls) {
Millisecond = mls; //成員函數訪問成員變量
}
// 構造函數的實現
Time::Time(int tmphour, int tmpmin, int tmpsec) {
Hour = tmphour;
Minute = tmpmin;
Second = tmpsec;
initMillTime(0);
}
Time::Time(){
Hour = 12;
Minute = 59;
Second = 59;
}
- main.cpp
#include <iostream>
#include "Time.h"
using namespace std;
int main() {
Time myTime(11, 44, 43);
cout << "myTime: " << myTime.Hour << ":" << myTime.Minute << ":" << myTime.Second << endl;
//使用第二個構造函數
Time myTime1;
cout << "myTime1: " << myTime1.Hour << ":" << myTime1.Minute << ":" << myTime1.Second << endl;
// 對象拷貝
Time myTime2;
// myTime3 並沒有調用傳統意義上的構造函數,而是調用的是拷貝構造函數,後續會講
Time myTime3 = myTime2;
return 0;
}
3. 函數默認參數
- 規定
-
默認值只能放在函數聲明中,除非該函數沒有函數聲明
-
在具有多個參數的函數中指定默認值時,默認參數必須出現在不默認參數的右邊,一旦某個默認參數開始指定默認值,它右邊的所有參數必須指定默認值
- Time.h
//
// Created by felaim on 2020/5/26.
//
#ifndef LESSION_PART_TWO_TIME_H
#define LESSION_PART_TWO_TIME_H
class Time {
private:
// 私有成員變量不能在外部直接用成員變量名使用
// 可以自己的成員函數調用
int Millisecond;//毫秒
void initMillTime(int mls);
public:
int Hour;
int Minute;
int Second;
// 成員函數
void initTime(int tmphour, int tmpmin, int tmpsec);
public:
//構造函數
Time(int tmphour, int tmpmin, int tmpsec=22);
//不加參數
Time();
};
#endif //LESSION_PART_TWO_TIME_H
- Time.cpp
//
// Created by felaim on 2020/5/26.
//
#include "Time.h"
// 成員函數initTime的實現
// ::叫做作用域運算符,表示initTime屬於Time類
void Time::initTime(int tmphour, int tmpmin, int tmpsec) {
//成員函數中可以直接使用成員變量名
// 哪個對象調用的成員函數,這些成員變量就屬於對應的對象
Hour = tmphour;
Minute = tmpmin;
Second = tmpsec;
initMillTime(25);
}
void Time::initMillTime(int mls) {
Millisecond = mls; //成員函數訪問成員變量
}
// 構造函數的實現
Time::Time(int tmphour, int tmpmin, int tmpsec) {
Hour = tmphour;
Minute = tmpmin;
Second = tmpsec;
initMillTime(0);
}
Time::Time(){
Hour = 12;
Minute = 59;
Second = 59;
}
- main.cpp
#include <iostream>
#include "Time.h"
using namespace std;
int main() {
Time myTime(11, 44, 43);
cout << "myTime: " << myTime.Hour << ":" << myTime.Minute << ":" << myTime.Second << endl;
//使用第二個構造函數
Time myTime1;
cout << "myTime1: " << myTime1.Hour << ":" << myTime1.Minute << ":" << myTime1.Second << endl;
// 有了默認參數過後,第三個參數可以不給對應值,調用三個參數的構造函數
Time myTime2(12,3);
cout << "myTime2: " << myTime2.Hour << ":" << myTime2.Minute << ":" << myTime2.Second << endl;
return 0;
}
4. 隱式轉換和explicit
隱式轉換
- Time.h
//
// Created by felaim on 2020/5/26.
//
#ifndef LESSION_PART_TWO_TIME_H
#define LESSION_PART_TWO_TIME_H
class Time {
private:
// 私有成員變量不能在外部直接用成員變量名使用
// 可以自己的成員函數調用
int Millisecond;//毫秒
void initMillTime(int mls);
public:
int Hour;
int Minute;
int Second;
// 成員函數
void initTime(int tmphour, int tmpmin, int tmpsec);
public:
//構造函數
Time(int tmphour, int tmpmin, int tmpsec=22);
//不加參數
Time();
//單參數的構造函數
Time(int tmphour);
};
#endif //LESSION_PART_TWO_TIME_H
- Time.cpp
//
// Created by felaim on 2020/5/26.
//
#include "Time.h"
// 成員函數initTime的實現
// ::叫做作用域運算符,表示initTime屬於Time類
void Time::initTime(int tmphour, int tmpmin, int tmpsec) {
//成員函數中可以直接使用成員變量名
// 哪個對象調用的成員函數,這些成員變量就屬於對應的對象
Hour = tmphour;
Minute = tmpmin;
Second = tmpsec;
initMillTime(25);
}
void Time::initMillTime(int mls) {
Millisecond = mls; //成員函數訪問成員變量
}
// 構造函數的實現
Time::Time(int tmphour, int tmpmin, int tmpsec) {
Hour = tmphour;
Minute = tmpmin;
Second = tmpsec;
initMillTime(0);
}
Time::Time(){
Hour = 12;
Minute = 59;
Second = 59;
}
Time::Time(int tmphour){
Hour = tmphour;
Minute = 59;
Second = 59;
}
- main.cpp
#include <iostream>
#include "Time.h"
using namespace std;
void func(Time time1) {
cout << "time1: " << time1.Hour << ":" << time1.Minute << ":" << time1.Second << endl;
return;
}
int main() {
//可以理解
Time myTime = {12};
//編譯系統把數字轉換成了Time類型的轉換,存在臨時對象隱式轉換
Time myTime1 = 1;
cout << "myTime1: " << myTime1.Hour << ":" << myTime1.Minute << ":" << myTime1.Second << endl;
Time myTime2 = (1, 3, 4, 5, 6);
cout << "myTime2: " << myTime2.Hour << ":" << myTime2.Minute << ":" << myTime2.Second << endl;
// 4被轉換成了Time臨時的對象,調用了單參數的構造函數
func(4);
return 0;
}
explicit
-
能否強制系統,構造函數不能做隱式類型轉換?
如果構造函數聲明中explict,則這個構造函數只能用於初始化和顯式類型轉換 -
修改Time.h中的單參數函數聲明
//單參數的構造函數
explicit Time(int tmphour);
- main.cpp中測試,不允許隱式類型轉換
#include <iostream>
#include "Time.h"
using namespace std;
void func(Time time1) {
cout << "time1: " << time1.Hour << ":" << time1.Minute << ":" << time1.Second << endl;
return;
}
int main() {
// 添加explict仍然可行
Time myTime{12};
cout << "myTime: " << myTime.Hour << ":" << myTime.Minute << ":" << myTime.Second << endl;
// 添加explict不可行
//Time myTime1 = 12;
//顯式類型轉換後可行
Time myTime1 = Time(8);
cout << "myTime1: " << myTime1.Hour << ":" << myTime1.Minute << ":" << myTime1.Second << endl;
// 4被轉換成了Time臨時的對象,調用了單參數的構造函數
// 進行顯式類型轉換
func(Time(4));
return 0;
}
- 對於單參數的構造函數,一般都聲明稱爲explicit,使得類構造更加合理一些
5. 構造函數初始化列表
- 將Time.cpp中原來三參數的構造函數寫成如下的形式,即使用了構造函數初始化列表
Time::Time(int tmphour, int tmpmin, int tmpsec)
: Hour(tmphour), Minute(tmpmin), Second(tmpsec), Millisecond(0) {}
-
這種寫法顯得比較高大上,顯得專業
-
效率上會更高一些