最近學習到C++中的的派生類,講到了一個面向對象的繼承,然後看書上的例子就感覺有一些熟悉,因爲在C語言求struct的時候就有類似的東西,把一個結構體作爲另一個結構體的成員,
#include <stdio.h>
struct A
{
int a;
int b;
};
struct B
{
int c;
struct A;
};
int main()
{
printf("%d\n", sizeof(struct A));
printf("%d\n", sizeof(struct B));
system("pause");
return 0;
}
繼承(inheritance)機制是面向對象程序設計使代碼可以複用的最重要的手段,它允許程序員在保持原有類特性的基礎上進行擴展,增加功能。這樣產生新的類,稱派生類。繼承呈現了面向對象程序設計的層次結構,體現了由簡單到複雜的認知過程
#include <iostream>
using namespace std;
class base
{
public:
base();
base(const base& s);
~base();
//訪問權限
public:
int _pb;
private:
int _pi;
protected:
int _po;
};
//public爲繼承權限
class der :public base
{
per(int pub, int pri, int pro)
{
base::_pb = 10;
//base::_pi = 20;
base::_po = 30;
}
public:
int _pub;
private:
int _pri;
protected:
int _pro;
};
void test()
{
cout << sizeof(base) << endl;
cout << sizeof(per) << endl;
}
int main()
{
test();
system("pause");
return 0;
}
//der類就是base類的派生類,base類時der類的基類
繼承權限
和訪問權限一樣,繼承權限也是三個
public :
protected:
private:
基類的private成員在派生類中是不能被訪問的,如果基類成員不想在類外直接被訪問,但需要在派生類中能訪問,就定義爲protected,在訪問權限中,我們是將protected和private的權限看作是一樣的,這裏可以看出保護成員限定符是因繼承纔出現的
public繼承:
是一個接口繼承,保持is-a原則,每個父類可用的成員對子類也可用,因爲每個子類對象也都是一個父類對象
protected/private繼承:
是一個實現繼承,基類的部分成員並非完全成爲子類接口的一部分,是 has-a的關係原則,所以非特殊情況下不會使用這兩種繼承關係,在絕大多數的場景下使用的都是公有繼承。
私有繼承通常組合更低級,但當一個派生類需要訪問基類保護成員或需要重定義基類的虛函數時它就是合理的。
不管是哪種繼承方式,在派生類內部都可以訪問基類的公有成員和保護成員,基類的私有成員存在但是在子類中不可見(不能訪問)。
使用關鍵字class時默認的繼承方式是private,使用struct時默認的繼承方式是public,不過最好顯示的寫出繼承方式
繼承中的作用域:
在繼承體系中基類和派生類是兩個不同的作用域
子類和父類中有同名成員,子類成員將屏蔽父類對成員的直接訪問。(在子類成員函數中,可以使用 基類::基類成員 訪問)--隱藏 --重定義
注意在實際中在繼承體系裏面最好不要定義同名的成員
派生類的繼承形式
單繼承 :
派生類只有一個直接父類
class person;
class student:public person;
多繼承:
派生類有多個直接父類
class person;
class anime;
class student : public person,public anime
菱形繼承:
派生類有多個直接父類
多個直接父類繼承自同一個父類
存在數據冗餘和二義性問題
class person;
class student :public person; class teacher:public person;
class assistant: public student,public teacher;
菱形虛擬繼承:
在菱形繼承的基礎上解決了菱形繼承的數據冗餘和二義性問題,在後續的學習中會再詳細瞭解。
繼承的繼承與轉換(賦值兼容規則):
1.子類對象可以賦值給父類對象
2.父類對象不能賦值給子類對象
3.父類的指針/引用可以指向子類對象
4.子類的指針/引用不能指向父類對象,但是可以通過強轉來完成
在繼承中有幾點要注意:
1.友元,友元關係是不能繼承的,基類的友元成員不能訪問派生類的protected成員和protected成員。
2.靜態成員變量,如果在基類中定義了static成員,那麼整個繼承體系裏面只有一個這樣的成員。無論派生出多少個子類,都只有一個static成員
3. 類的六個默認函數是不能被繼承的。
繼承體系中的構造函數次序
如果在體系中先創建派生類的對象,是如何調用構造函數的呢?
#include <iostream>
using namespace std;
class Student
{
private:
int _num;
string _name;
char _gender;
public:
Student(int num, string name, char gender)
{
_num = num;
_name = name;
_gender = gender;
cout << _gender << endl;
cout << "基類的構造函數" << endl;
}
};
class Student1: public Student
{
private:
string _addr;
string _major;
Student object_1;
public:
Student1(string addr, string major, int num, string name, char gender,char object)
:Student(num, name, gender)
,object_1(num, name, object)
{
_addr = addr;
_major = major;
cout << "派生類新成員構造函數" << endl;
}
};
int main()
{
Student1 object("西安", "計算機", 123456789, "羅宇", 'M', 'W');
system("pause");
return 0;
}
但從結果圖中可以看出,順序是
基類的構造函數->
派生類中對象的構造函數->
派生類的構造函數
但是我們一般都知道,我們是創建哪個對象調用哪個對象的構造函數,顯然在這裏是不適用的,那麼我們再來一段代碼
class Base
{
public:
Base()
{
cout<<"B()" <<endl;
}
~Base ()
{
cout<<"~B()" <<endl;
}
void ShowBase()
{
cout<<"_pri = " <<_pri<< endl;
cout<<"_pro = " <<_pro<< endl;
cout<<"_pub = " <<_pub<< endl;
}
private:
int _pri;
protected:
int _pro;
public:
int _pub;
};
class Derived:public Base
{
public:
Derived()
{
cout<<"D()"<<endl;
}
~Derived ()
{
cout<<"~D()"<<endl;
}
void ShowDerived()
{
cout<<"_d_pri = "<<_d_pri<< endl;
cout<<"_d_pro = "<<_d_pro<< endl;
cout<<"_d_pub = "<<_d_pub<< endl;
}
private:
int _d_pri;
protected:
int _d_pro;
public:
int _d_pub;
};
在創建派生類對象這裏打一個斷點,然後F5運行
,
可以看到,在構造派生類對象時,最先使用的時派生類的構造函數,然後我們再F11,
然後馬上又跳到基類的構造函數,因爲在基類沒有構造對象之前,派生類無法構造對象,所以,我們在這裏可以說明關於繼承體系中構造函數的調用過程應該如下:
1.調用派生類的構造函數,在派生類的構造函數中call基類的構造函數。
2.調用基類構造函數,
3.調用派生類對象的構造函數,
4.調用派生類的構造函數
基類的構造函數調用順序按照繼承列表的聲明順序,對象的構造函數調用順序按照對象的聲明順序
說明:
1.基類沒有缺省構造函數,派生類必須要在初始化列表中顯式給出基類名和參數列表。
2.基類沒有定義構造函數,則派生類也可以不用定義,全部使用缺省構造函數。
3.基類定義了帶有形參表構造函數,派生類就一定定義構造函數
析構函數與構造函數的次序相反。