C++學習筆記 (五) ---- 繼承與派生
①、繼承可以理解為一個類從另一個類獲取成員變數和成員函式的過程,被繼承的類稱為基類,繼承的類稱為派生類。
派生類除了擁有基類成員,還可以自定義新成員。
#include<iostream> using namespace std; //基類 Pelple class People{ public: void setname(char *name); void setage(int age); char *getname(); int getage(); private: char *m_name; int m_age; }; void People::setname(char *name){ m_name = name; } void People::setage(int age){ m_age = age; } char* People::getname(){ return m_name; } int People::getage(){ return m_age;} //派生類 Student class Student: public People{ //Student類繼承People類,public方式; public: void setscore(float score); float getscore(); private: float m_score; }; void Student::setscore(float score){ m_score = score; } float Student::getscore(){ return m_score; } int main(){ Student stu; stu.setname("小明"); stu.setage(16); stu.setscore(95.5f); cout<<stu.getname()<<"的年齡是 "<<stu.getage()<<",成績是 "<<stu.getscore()<<endl; return 0; }
由此可看出繼承的一般方式為:
class 派生類名:[繼承方式] 基類名{
//派生類新增成員;
};
繼承方式有:public(公有)、private(私有)和 protected(受保護),預設 private。
類成員的訪問許可權:public > protected > private
其中,protected 成員也是和 private 成員類似,也不能通過物件訪問;但是在繼承的時候,基類的 protected 成員可以在派生類中使用,但是 private
不同繼承方式對不同屬性成員的影響
②、繼承中的名字遮蔽
如果派生類中的成員和基類中的成員重名,那麼會遮蔽從基類繼承過來的成員,即使用該成員時實際上使用的是派生類中的成員。因此,基類和派生類中的同名函式不構成過載。例:
#include<iostream> using namespace std; //基類Base class Base{ public: void func(); void func(int); }; void Base::func(){ cout<<"Base::func()"<<endl; } void Base::func(int a){ cout<<"Base::func(int)"<<endl; } //派生類Derived class Derived: public Base{ public: void func(char *); void func(bool); }; void Derived::func(char *str){ cout<<"Derived::func(char *)"<<endl; } void Derived::func(bool is){ cout<<"Derived::func(bool)"<<endl; } int main(){ Derived d; d.func("c.biancheng.net"); d.func(true); // d.func(); //compile error // d.func(10); //compile error d.Base::func(); d.Base::func(100); return 0; }
上例中,構成過載的只是 Base 類的兩個 func 或者 Derive 類的兩個 func。所以要是想訪問基類的同名函式,需要指定作用域,如 28、29 兩行所示。
③、派生類的建構函式
類的建構函式不能被繼承,所以在初始化基類的成員變數時,是在派生類的建構函式中呼叫基類的建構函式來完成初始化。
#include<iostream>
using namespace std;
//基類People
class People{
protected:
char *m_name;
int m_age;
public:
People(char*, int);
};
People::People(char *name, int age): m_name(name), m_age(age){}
//派生類Student
class Student: public People{
private:
float m_score;
public:
Student(char *name, int age, float score);
void display();
};
//People(name, age)就是呼叫基類的建構函式
Student::Student(char *name, int age, float score): People(name, age), m_score(score){ }
void Student::display(){
cout<<m_name<<"的年齡是"<<m_age<<",成績是"<<m_score<<"。"<<endl;
}
int main(){
Student stu("小明", 16, 90.5);
stu.display();
return 0;
}
像 24 行程式碼一樣,People(name, age) 就是呼叫基類的建構函式,並將 name 和 age 作為實參傳遞給它,m_score(score) 是派生類的引數初始化列表,它們之間用逗號隔開。
注: 多層派生中,派生類的建構函式中只能呼叫直接基類的建構函式,不能呼叫間接基類的。
同理,解構函式也是不能被繼承的,並且因為每個類只有一個解構函式,所以派生類的解構函式不需要顯式地呼叫基類的建構函式,編譯器會自動選擇。
另外,解構函式的執行順序和建構函式的執行順序也剛好相反:建立派生類物件時,建構函式的執行順序和繼承順序相同,即先執行基類建構函式,再執行派生類建構函式。而銷燬派生類物件時,解構函式的執行順序和繼承順序相反,即先執行派生類解構函式,再執行基類解構函式。
④、C++指標可以突破許可權限制
萬能的指標是可以穿透地址,隨意修改類的成員,即使是 private、const 成員也無濟於事。
⑤、C++向上轉型(將派生類賦值給基類)
C/C++中對不同型別的變數進行賦值時,編譯器會將其轉化成同類型,然後再賦值。同理,類也是一種資料型別,也可以進行資料轉化,這種轉化一般只發生在基類和派生類之間。將派生類賦值給基類稱為向上轉型,將基類賦值給派生類稱為向下轉型。
向上轉型很安全,由編譯器完成,向下轉型有風險,需要程式設計師手動干預。
#include <iostream>
using namespace std;
//基類
class A{
public:
A(int a);
void display();
int m_a;
};
A::A(int a): m_a(a){ }
void A::display(){
cout<<"Class A: m_a="<<m_a<<endl;
}
//派生類
class B: public A{
public:
B(int a, int b);
void display();
int m_b;
};
B::B(int a, int b): A(a), m_b(b){ }
void B::display(){
cout<<"Class B: m_a="<<m_a<<", m_b="<<m_b<<endl;
}
int main(){
A a(10);
B b(66, 99);
//賦值前
a.display();
b.display();
cout<<"--------------"<<endl;
//賦值後
a = b;
a.display();
b.display();
return 0;
}
//執行結果:
Class A: m_a=10
Class B: m_a=66, m_b=99
----------------------------
Class A: m_a=66
Class B: m_a=66, m_b=99
將派生類物件賦值給基類物件時,會捨棄派生類新增的成員:
注:同一基類的不同派生類物件之間也不能賦值