1. 程式人生 > >C++學習筆記 (五) ---- 繼承與派生

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

類成員的訪問許可權:publicprotected > 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

將派生類物件賦值給基類物件時,會捨棄派生類新增的成員:

注:同一基類的不同派生類物件之間也不能賦值