8.面向物件 二 繼承與多型
友元
- 作用: 可以訪問類中私有成員 關鍵字friend
- 全域性函式做友元
/** *Created by Lex on 2020/11/23. */ #include <iostream> using namespace std; #include <string> class Build { friend void goodGay(Build *building); public: Build(){ m_SittingRoom="客廳"; m_BedRoom="臥室"; } string m_SittingRoom; private: string m_BedRoom; }; void goodGay(Build *building){ std::cout << "你的好友正在訪問您的=>"<<building->m_SittingRoom << std::endl; std::cout << "你的好友正在訪問您的=>"<<building->m_BedRoom << std::endl; } void test(){ Build build; goodGay(&build); } int main(int argc, char const *argv[]) { test(); return 0; }
-
類做友元
/** *Created by Lex on 2020/11/23. * 類做友元 */ #include <iostream> using namespace std; #include <string> class Building; class goodGay{ public: void visit(); goodGay(); private: Building * building; }; class Building{ friend class goodGay ; public: Building(); string m_sittingRoom; private: string m_bedRoom; }; //building的實現 Building::Building() { this->m_bedRoom="臥室"; this->m_sittingRoom="客廳"; } //GOOdGay的實現 goodGay::goodGay() { building = new Building; } void goodGay::visit() { std::cout << "你正在訪問=>>"<< building->m_sittingRoom << std::endl; std::cout << "你正在訪問=>>"<< building->m_bedRoom << std::endl; } void test(){ goodGay gay; gay.visit(); } int main(int argc, char const *argv[]) { test(); return 0; }
運算子過載
-
運算子過載主要針對於自定義型別的資料操作
/** *Created by Lex on 2020/11/23. 運算子+過載 */ #include <iostream> using namespace std; class Person{ public: int m_num; int m_number; }; void test(){ Person p1; p1.m_number=10; p1.m_num=20; Person p2; p2.m_number=10; p2.m_num=20; Person p3; p3=p1+p2;//報錯 } int main(int argc, char const *argv[]) { return 0; } //--------------------------------------------------------------------------------------------- //運算子遞增或者遞減過載 #include <iostream> using namespace std; class MyInteger { friend ostream& operator<<(ostream& cout, MyInteger myint); public: MyInteger(); ~MyInteger(); //前置遞增 MyInteger& operator--() { num--; return *this;//返回自身 } //後置遞增 MyInteger operator--(int) { MyInteger temp; temp = *this; num--; return temp; } private: int num; }; MyInteger::MyInteger() { num = 0; } MyInteger::~MyInteger() { } ostream& operator<<(ostream& cout, MyInteger myint) { cout << myint.num; return cout; } void test() { MyInteger mi; cout << mi-- << endl; cout << mi; } int main() { test(); return 0; } //-------------------------------------------------------------------------------------------- //賦值運算子過載 #include <iostream> using namespace std; class Person { public: Person(int age); ~Person(); int *myage; Person& operator=(Person& p) { if (myage!=NULL) { delete myage; myage = NULL; } myage = new int(*p.myage); return *this; } }; Person::Person(int age) { myage=new int(age); } Person::~Person() { if (myage!=NULL) { delete myage; myage = NULL; } } int main() { Person p1(18); Person p2(20); Person p3(45); p3 = p2 = p1; cout<<"p1的年齡:" << *p1.myage<<endl; cout << "p2的年齡:" << *p2.myage << endl; cout << "p3的年齡:" << *p3.myage << endl; return 0; }
-
全域性函式運算子過載
Person operator+(Person &person1,Person &person){ Person temp; temp.m_num=person.m_num+person1.m_num; temp.m_number=person.m_number+person1.m_number; return temp; } ostream & operator<<(ostream &out,Person &p){ out<<p.M_a<<p.m_b; return out }
-
成員函式運算子過載
//+過載 class Person{ public: Person operator+(Person &person){ Person temp; temp.m_num=person.m_num+ this->m_num; temp.m_number=person.m_number+ this->m_number; return temp; } int m_num; int m_number; }; //成員函式無法對<<過載,得不到預定結果 //關係運算符過載 bool operator==(Person &p){ if(this->myage==p.myage&&this->name==p.name){ return true; } return false; } bool operator!=(Person &p){ if(this->myage==p.myage&&this->name==p.name){ return false; } return true; }
-
繼承
/**
*Created by Lex on 2020/11/30.
*/
class Base{
public:
int m_a;
protected:
int m_b;
private:
int m_c;
};
class son: public Base{
public:
void func(){
m_a=1000;
m_b=1000;
// m_c=1000 //公有繼承私有屬性不可訪問
}
};
class son2: protected Base{
public:
void func(){
m_a=1000;
m_b=1000;
// m_c=1000 //保護繼承私有屬性不可訪問
}
};
class son3: private Base{
public:
void func(){
m_a=1000;
m_b=1000;
// m_c=1000 //保護繼承私有屬性不可訪問
}
};
void test(){
son son;
son.m_a=1000;//類外訪問不改變公有屬性的訪問許可權,無法訪問protected許可權的屬性
// son.m_b=100
son2 son2;//類外無法訪問子類屬性,保護繼承將公有屬性變為了protected許可權
son3 son3;//無法訪問子類屬性,私有繼承將公有屬性和保護屬性變為了私有屬性
}
私有屬性無法訪問,但已經被繼承下來了,只是被編譯器隱藏了
父類和子類的執行順序:
繼承中同名屬性或者函式如何訪問(包含靜態屬性函式):
子類中直接訪問,父類中加上父類作用域 => 父類名::屬性名或者函式名
多繼承語法(不推薦使用):對於二義性的解決方案:加上作用域
class son : public Base2,public Base1{
}
//菱形繼承 解決方案 虛繼承 virtual
多型
-
多型分為兩類:一類是靜態多型 類似函式過載,運算子過載 二是動態多型 :派生類和虛擬函式執行時多型
-
兩者區別:靜態多型是函式地址早繫結,即在編譯階段確定函式地址。 動態多型是函式地址晚繫結 ,即執行階段確定函式地址
-
實驗:animal類和cat類 cat繼承animal類 通過virtual關鍵字改變繫結時間
早繫結
/**
*Created by Lex on 2020/12/2.
*/
#include <iostream>
class Animal{
public:
//函式地址編譯時已經確定
void func(){
std::cout << "父類執行" << std::endl;
}
};
class Cat : public Animal{
public:
//函式地址編譯時已經確定
void func(){
std::cout << "子類執行" << std::endl;
}
};
void speak(Animal &animal){//函式地址編譯時已經確定
animal.func();
}
void test(){
Cat cat;
speak(cat);
}
int main(){
test();//呼叫父類的fun函式
return 0;
}
實現晚繫結
class Animal{
public:
//通過virtual關鍵字實現晚繫結
virtual void func(){
std::cout << "父類執行" << std::endl;
}
};
class Cat : public Animal{
public:
void func(){
std::cout << "子類執行" << std::endl;
}
};
void speak(Animal &animal){
animal.func();
}
void test(){
Cat cat;
speak(cat);
}
int main(){
test();
return 0;
}
通過圖中我們發現父類animal和子類cat的大小已經變為4 這是由於在函式前加上virtual關鍵字,編譯器會為這個函式新增vfptr 指標(指標的大小在32位系統下為4, 64位系統下為8 ;虛擬函式指標:指向虛擬函式表 vftable:記錄虛擬函式的地址)指向這個虛擬函式.子類中重寫了父類的func ,此時虛擬函式表中父類的vfptr被子類的vfptr覆蓋
- 純虛擬函式和抽象類
純虛擬函式語法:virtual 返回值型別 函式名(引數列表)=0
抽象類特點:無法例項化物件,子類必須重寫抽象類中的純虛擬函式
class Animal{
public:
//純虛擬函式
virtual void func()=0;
};
class Cat : public Animal{
public:
// 重寫抽象類的純虛擬函式
void func(){
std::cout << "子類執行" << std::endl;
}
};
void speak(Animal &animal){
animal.func();
}
void test(){
Cat cat;
speak(cat);
}
int main(){
test();
return 0;
}
- 虛析構和純虛析構
區別:共同點=>解決父類釋放子類物件 ,需要具體的函式實現
不同點=>純虛析構屬於抽象類,無法例項化
/**
*Created by Lex on 2020/12/2.
*/
#include <iostream>
using namespace std;
#include <cstring>
//抽象類的標誌:只有有一個純虛擬函式,就是抽象類
class Animal{
public:
//純虛擬函式
virtual void func()=0;
Animal(){
std::cout << "animal建構函式執行了" << std::endl;
}
~Animal(){
std::cout << "animal解構函式執行了" << std::endl;
}
};
class Cat : public Animal{
public:
// 重寫抽象類的純虛擬函式
void func(){
std::cout << "cat建構函式執行了" << std::endl;
std::cout << *cat_name<<"在唱歌" << std::endl;
}
string *cat_name;
Cat(string name){
cat_name= new string(name);
}
~Cat(){
if (cat_name!= nullptr){
std::cout <<"cat解構函式執行了" << std::endl;
delete cat_name;
cat_name= nullptr;
}
}
};
void speak(Animal &animal){
animal.func();
}
void test(){
Animal *animal =new Cat("jack");
animal->func();
delete animal;
}
int main(){
test();
return 0;
}
通過圖中可以發現子類解構函式沒有執行,子類在堆區建立的物件沒有釋放,對於這種現象可以使用虛析構和純虛析構解決
class Animal{
public:
//純虛擬函式
virtual void func()=0;
Animal(){
std::cout << "animal建構函式執行了" << std::endl;
}
virtual ~Animal(){
std::cout << "animal解構函式執行了" << std::endl;
}
};
class Animal{
public:
//純虛擬函式
virtual void func()=0;
Animal(){
std::cout << "animal建構函式執行了" << std::endl;
}
virtual ~Animal()=0;
};
Animal::~Animal(){
std::cout << "animal解構函式執行了" << std::endl;
}