/*
設計學生這個類
延用人這個類設計好的東西
*/
/*
人 N多的成員方法
學生
人N多的成員方法
學生也有人N
*/
#include<iostream>
#include<string>
class People
{
public:
People(std::string name = " ", bool sex = true,
int age = 0)
:mname(name), msex(sex), mage(age)
{}
protected:
std::string mname;
bool msex;
int mage;
};
class Student : public People
{
public:
Student(std::string name = " ", bool sex = true,
int age = 0,
std::string id = " ")
{
mid = id;
}
private:
std::string mid;
};
//class Student
//{
//public:
// Student(std::string name, bool sex, int age,
// std::string id)
// :mname(name), msex(sex), mage(age),
// mid(id){}
//private:
// std::string mname;
// bool msex;
// int mage;
// std::string mid;
//};
class Base
{
public:
Base(int a = 0)
:ma(a)
{}
void Show()
{
std::cout << "Base::ma : " << ma << std::endl;
}
static void Print()
{
std::cout << "hello world!" << std::endl;
}
public:
int ma;
static int a;
};
int Base::a = 10;
class Derive :public Base
{
public:
Derive(int b)
:mb(b)
{}
public:
int mb;
};
int main()
{
Derive d(20);
d.mb = 30;
d.Show();
Derive::Print();
std::cout << "Base size : " << sizeof(Base) << std::endl;
std::cout << "Derive size : " << sizeof(Derive) << std::endl;
return 0;
}
如果能繼承構造/解構:基礎類別的建構函式在派生類中,相當於普通函數,但是普通函數必須有返回值,因此不成立,所以派生類不能繼承基礎類別的構造和解構
1.普通的成員變數
2.靜態的成員變數
3.普通的成員方法
4.靜態的成員方法
5.作用域
派生類繼承了基礎類別除構造解構以外的所有成員
/*
基礎類別沒有給出預設的建構函式
派生類中指定對應的構造方式和實參
*/
class Base
{
public:
Base(int a)
:ma(a)
{
std::cout << "Base::Base()" << std::endl;
}
~Base()
{
std::cout << "Base::~Base()" << std::endl;
}
protected:
int ma;
};
//類標誌 派生類類名 : 繼承方式 基礎類別類名
/*
存取限定符
public
protected
private
繼承方式
public 公有繼承
protected 保護繼承
private 私有繼承
*/
class Derive : public Base
{
public:
Derive(int b)
:mb(b), Base(b)
{
std::cout << "Derive::Derive()" << std::endl;
}
~Derive()
{
std::cout << "Derive::~Derive()" << std::endl;
}
protected:
int mb;
};
int main()
{
Derive d(10);
return 0;
}
基礎類別構造優先於派生類構造
先構造的後解構
public: 任意位置
protected: 本類和子類
private: 本類
public:公有繼承
protected:保護繼承
private:私有繼承
繼承方式 ↓ | public | protected | private |
---|---|---|---|
public | public | protected | 不可存取 |
protected | protected | protected | 不可存取 |
private | private | private | 不可存取 |
class Base
{
public:
Base(){}
public:
int ma;
protected:
int mb;
private://本類類中存取
int mc;
};
class Derive : private Base
{
public:
void Show()
{
std::cout << mb << std::endl;
}
};
class Derive2 : public Derive
{
public:
void Show()
{
//std::cout << mb << std::endl;
}
};
int main()
{
//std::cout << sizeof(Derive) << std::endl;
Derive d;
Derive2 d2;
//std::cout << d.mb << std::endl; ---->類外存取
d2.Show();
d.Show();
return 0;
}
三要素 :
1.同名
2.不同參
3.同作用域
派生類中同名的函數隱藏了基礎類別中所有的同名函數
1.同名
2.不同作用域(繼承層次)
class Base
{
public:
Base(int a = 0)
:ma(a)
{}
void Show()
{
std::cout << "Base::ma " << ma << std::endl;
}
void Show(int arg)
{
int tmp = ma + arg;
std::cout << "Base:: Sum " << tmp << std::endl;
}
protected:
int ma;
};
class Derive : public Base
{
public:
Derive(int b)
:mb(b)
{}
void Show()
{
std::cout << "Derive::mb " << mb << std::endl;
}
private:
int mb;
};
int main()
{
Base* pb = new Derive(10);//基礎類別指針 指向派生類物件
Derive* pd = new Base(10);//派生類指針 指向基礎類別物件
return 0;
}
int main()
{
Derive d(10);
d.Show(10);
//d.Base::Show();
return 0;
}
基礎類別指針 指向派生類物件
基礎類別參照 參照派生類物件
派生類中同名同參的虛擬函式覆蓋基礎類別中同名同參的虛擬函式
1.同名同參
2.不同作用域(繼承層次)
3.虛擬函式
1.靜多型 :
編譯確定函數的呼叫(call函數的入口地址)
2.動多型(預設爲動多型):
執行 確定函數的呼叫(call暫存器)
3.宏多型 :
預編譯 確定函數的呼叫
流程:虛擬函式爲了實現動多型,首先在編譯階段把函數的入口地址放在數據段(普通函數只放在符號表,虛擬函式符號表+數據段都放了),數據段是.rodata只讀數據段,虛擬函式的入口地址放在了只讀數據段中的虛擬函式表中,虛擬函式表被編譯鏈接,通過執行,載入指令和數據時,當做數據的一部分,載入到記憶體中,因此就可以在執行時找到虛擬函式的入口地址,實現動多型。
沒有物件就找不到入口地址
1.不依賴物件。
2.可以
3.不能取地址,直接替換。
4. cdcal呼叫約定,不依賴物件。
5. 和物件沒關係。
6. 可以
獲取內建單元對應的型別,只與定義點有關
自定義型別,typeid關鍵字列印時:先找到物件,然後找到虛擬函式指針型別,然後解除參照找到虛擬函式表,然後找到RTTI資訊
class Base
{
public:
Base(int a = 10)
:ma(a){}
virtual void Show()
{
std::cout << "Base::ma " << ma << std::endl;
}
//virtual void Print()
//{
// std::cout << "hello world!" << std::endl;
//}
protected:
int ma;
};
/*
繼承層次
基礎類別中同名同參的函數是虛擬函式
派生類中同名同參的函數也是虛擬函式
*/
class Derive : public Base
{
public:
Derive(int b)
:mb(b)
{
}
virtual void Show()
{
std::cout << "Derive::mb " << std::endl;
}
//virtual void Test()
//{
// std::cout << "Derive::Test()" << std::endl;
//}
private:
int mb;
};
/*
動多型
執行階段 確定函數的呼叫
虛擬函式機制 機製提供支援
*/
/*
純虛擬函式
*/
int main()
{
std::cout << "Base::size : " << sizeof(Base) << std::endl;//4
std::cout << "Derive::size : " << sizeof(Derive) << std::endl;
Base* pb = new Derive(10);----》pb是Base*型別
std::cout << "pb type: " << typeid(pb).name() << std::endl;
/*
*pb
動態型別提取 RTTI (有虛擬函式後,*pb變成了class derive)
*/
std::cout << "*pb type: " << typeid(*pb).name() << std::endl;----》*pb是Base型別
pb->Show();//動多型
return 0;
}
時機:指針呼叫虛擬函式+物件完整(虛擬函式的呼叫不在構造和解構中,在構造和解構內部,物件屬於半成品物件)
申請和釋放不是同一個地方,所以程式崩潰
基礎類別有虛解構函情況:(派生類的解構也會變成虛解構–》同名覆蓋)
要把虛表寫進虛擬函式指針裏面,指針是物件生成後纔有的空間,而物件是在執行階段生成的
==問題:==是在開闢記憶體後寫入還是建構函式呼叫之後?
將所有置爲0
程式出錯,因此虛表寫入的時機是:建構函式第一行程式碼執行之前
呼叫基礎類別時看不到派生類的虛擬函式表
1.畫出對應的派生類的記憶體佈局和虛擬函式表
1.模板方式
2.在繼承層次中,允許基礎類別指針指向派生類物件
C語言中是暴力轉化
去除常性轉換成int*型別
安全性更高
虛繼承關係中,基礎類別叫做虛基礎類別,ma就是虛基礎類別數據
構造解構順序:
構造:AC E B D(構造C之前要先構造A)
解構:
構造解構順序:
構造:E AB AC D(構造C之前要先構造A)
解構:DCABAE
vfptr的偏移:0-vfptr的位置
先處理虛擬函式,再處理虛繼承。
友元關係:
繼承失敗
exception是一個類,exception(" …"); 叫做顯示生成臨時物件
throw後面放的就是一個物件
try:包含可能發生異常的程式碼
catch:捕獲並處理異常
throw:拋出異常
err參照的就是 異常點 拋出的臨時物件
如果throw的是一個double型別的數據,可以在main函數catch一個double err