初始化列表
特點(diǎn)
- 一種便捷的初始化成員變量的方式
- 只能在構(gòu)造函數(shù)中
Person(int age, int height): m_age(age), m_height(height) { }
初始化列表與默認(rèn)參數(shù)配合使用
Person(int age = 0, int height = 0): m_age(age), m_height(height) { }
如果函數(shù)的聲明和實(shí)現(xiàn)是分離的
- 初始化列表只能寫(xiě)在函數(shù)的實(shí)現(xiàn)中
- 默認(rèn)參數(shù)只能寫(xiě)在函數(shù)的聲明中
父類(lèi)的構(gòu)造函數(shù)
- 子類(lèi)的構(gòu)造函數(shù)默認(rèn)會(huì)調(diào)用父類(lèi)的無(wú)參構(gòu)造函數(shù)
- 如果子類(lèi)的構(gòu)造函數(shù)顯示地調(diào)用了父類(lèi)的有慘構(gòu)造函數(shù)呆细,就不會(huì)再去默認(rèn)調(diào)用父類(lèi)的無(wú)參構(gòu)造函數(shù)
- 如果父類(lèi)缺少無(wú)參構(gòu)造函數(shù)坟比,子類(lèi)的構(gòu)造函數(shù)必須顯式調(diào)用父類(lèi)的有參構(gòu)造函數(shù)
繼承體系下的構(gòu)造函數(shù)示例
struct Person {
int m_age;
Person(): Person(0) {}
Person(int age): m_age(age) { }
};
struct Student: Person {
int m_no;
Student(): Student(0, 0) {
}
Student(int age, int no): Person(age), m_no(no) {
}
};
父類(lèi)指針果元、子類(lèi)指針
- 父類(lèi)指針可以指向子類(lèi)對(duì)象,是安全的(繼承方式必須是public)
- 子類(lèi)指針指父類(lèi)對(duì)象是不安全的
class Person {
public:
int m_age;
};
class Student : public Person {
public:
int m_score;
};
void test() {
Person *person = new Student();
// 可以訪問(wèn)m_score甚脉, m_age; 安全
person->m_age = 10;
Student *stu = (Student*)new Person();
// 訪問(wèn)不到 m_score, 不安全
stu->m_score = 10;
}
多態(tài)
- 默認(rèn)情況下,編譯器只會(huì)根據(jù)指針類(lèi)型調(diào)用對(duì)應(yīng)的函數(shù)婶希,不存在多態(tài)
特性
- 同一個(gè)操作作用于不同的對(duì)象耕腾,可以有不同的解釋见剩,產(chǎn)生不同的執(zhí)行結(jié)果
- 在運(yùn)行時(shí),可以識(shí)別出真正的對(duì)象類(lèi)型扫俺,調(diào)用對(duì)應(yīng)子類(lèi)中的函數(shù)
多態(tài)的要素
- 有繼承關(guān)系
- 子類(lèi)重寫(xiě)父類(lèi)的成員函數(shù)(override)
- 父類(lèi)指針指向子類(lèi)對(duì)象
- 利用父類(lèi)調(diào)用重寫(xiě)的成員函數(shù)
多態(tài)分為兩類(lèi)
- 靜態(tài)多態(tài):函數(shù)重載和運(yùn)算符重載屬于靜態(tài)多態(tài)苍苞,復(fù)用函數(shù)名
- 動(dòng)態(tài)多態(tài):派生類(lèi)和虛函數(shù)實(shí)現(xiàn)運(yùn)行時(shí)多態(tài)
靜態(tài)多態(tài)個(gè)動(dòng)態(tài)多態(tài)的區(qū)別
- 靜態(tài)多態(tài)的函數(shù)地址早綁定-編譯階段確定函數(shù)地址
- 動(dòng)態(tài)多態(tài)的函數(shù)地址晚綁定-運(yùn)行階段確定函數(shù)地址
class Animal {
public:
virtual void run() {
cout << "Animal::run()" << endl;
}
};
class Dog: public Animal {
public:
void run() override {
cout << "Dog::run()" << endl;
}
};
class ErHa: public Dog {
public:
void run() override {
cout << "ErHa::run()" << endl;
}
};
void test() {
Dog *dog = new Dog();
dog->run();
Animal * animal0 = new Dog();
animal0->run();
Animal * animal1 = new ErHa();
animal1->run();
}
虛函數(shù)
- C++中的多態(tài)通過(guò)虛函數(shù)(virtual function)來(lái)實(shí)現(xiàn)
- 虛函數(shù):被virtual修飾的成員函數(shù)
- 只要在父類(lèi)中聲明為虛函數(shù),子類(lèi)中重寫(xiě)的函數(shù)也自動(dòng)變成虛函數(shù)(也就是說(shuō)子類(lèi)可以省略virtual關(guān)鍵字)
虛表
- 虛函數(shù)的實(shí)現(xiàn)原理是虛表牵舵,這個(gè)虛表里面存儲(chǔ)著最終需要調(diào)用的函數(shù)地址柒啤,這個(gè)虛表也叫虛函數(shù)表
class Animal {
public:
virtual void run() {
cout << "Animal::run()" << endl;
}
virtual void speak() {
cout <<"Animal::speak()" << endl;
}
};
class Cat: public Animal {
public:
int m_life;
Cat(): m_life(0) {}
void run() override {
cout << "Cat::run()" << endl;
}
void speak() override {
// 先執(zhí)行父類(lèi)的成員函數(shù)
Animal::speak();倦挂、
// 再執(zhí)行自己的一些操作
cout << "Cat::Speak()" << endl;
}
};
虛表的x86環(huán)境圖
- 由上圖可知,所有的Cat對(duì)象只對(duì)應(yīng)一份虛表担巩。
- 具有虛函數(shù)的類(lèi)方援,在對(duì)象創(chuàng)建時(shí),會(huì)在其內(nèi)存布局的時(shí)候涛癌,拿出4個(gè)字節(jié)的內(nèi)存犯戏,用來(lái)存儲(chǔ)該對(duì)象對(duì)應(yīng)的函數(shù)虛表地址(vfptr: 虛函數(shù)指針),x86環(huán)境中拳话,一個(gè)虛函數(shù)地址占用4個(gè)字節(jié)先匪。
虛表的匯編分析
// 調(diào)用Cat::run
// 取出cat指針變量里面存儲(chǔ)的地址值
// eax里面存放的是cat對(duì)象的地址
mov eax, dword ptr [cat]
// 取出Cat對(duì)象的前面4個(gè)字節(jié)給edx
// edx里面存儲(chǔ)的是虛表的地址
mov edx, dword ptr [eax]
// 取出虛表中的前面4個(gè)字節(jié)給eax
// eax存放的就是Cat::run的函數(shù)地址
mov eax, dword ptr [edx]
call eax
// 調(diào)用Cat::speak
// 取出cat指針變量里面存儲(chǔ)的地址值
// eax里面存放的是cat對(duì)象的地址
mov eax, dword ptr [cat]
// 取出Cat對(duì)象的前面4個(gè)字節(jié)給edx
// edx里面存儲(chǔ)的是虛表的地址
mov edx, dword ptr [eax]
// 取出虛表中的后面4個(gè)字節(jié)給eax
// eax存放的就是Cat::speak的函數(shù)地址
mov eax, dword ptr [edx]
call eax
虛析構(gòu)函數(shù)
- 含有虛函數(shù)的類(lèi),應(yīng)該將析構(gòu)函數(shù)聲明為虛函數(shù)(虛析構(gòu)函數(shù))
- delete 父類(lèi)指針時(shí)弃衍,才會(huì)調(diào)用子類(lèi)的析構(gòu)函數(shù)呀非,保證析構(gòu)的完整性
- 虛析構(gòu)或純純虛析構(gòu)就是用來(lái)解決父類(lèi)指針釋放子類(lèi)對(duì)象
- 如果子類(lèi)中沒(méi)有堆區(qū)數(shù)據(jù),可以不寫(xiě)為虛析構(gòu)或純虛析構(gòu)
- 擁有純虛析構(gòu)函數(shù)的類(lèi)屬于抽象類(lèi)
class Animal {
public:
virtual void run() {
cout << "Animal::run()" << endl;
}
virtual void speak() {
cout <<"Animal::speak()" << endl;
}
// 虛析構(gòu)
virtual ~Animal() {
}
// 純虛析構(gòu)
virtual ~Animal() = 0;
};
class Cat: public Animal {
public:
int m_life;
Cat(): m_life(0) {}
void run() override {
cout << "Cat::run()" << endl;
}
void speak() override {
// 先執(zhí)行父類(lèi)的成員函數(shù)
Animal::speak();
// 再執(zhí)行自己的一些操作
cout << "Cat::Speak()" << endl;
}
~Cat() {
cout << "Cat::~Cat()" << endl;
}
};
純虛函數(shù)
- 定義:沒(méi)有函數(shù)體且初始化為0的虛函數(shù)镜盯,用來(lái)定義接口規(guī)范
- 抽象類(lèi)
- 含有虛函數(shù)的類(lèi)岸裙,不可以實(shí)例化
- 抽象類(lèi)也可以包含非純虛函數(shù)
- 如果父類(lèi)是抽象類(lèi),子類(lèi)沒(méi)有完全實(shí)現(xiàn)純虛函數(shù)速缆,那么這個(gè)子類(lèi)依然是抽象類(lèi)
class Animal {
public:
virtual void run() = 0;
virtual void speak() = 0;
virtual ~Animal() { }
};