C++基礎(chǔ)-(繼承)

C++ 基礎(chǔ)

概念及工方式

  1. 保持已有類的特性而構(gòu)造新類的過(guò)程稱為繼承。
  2. 在已有類的基礎(chǔ)上新增自己的特性而產(chǎn)生新類的過(guò)程稱為派生。
  3. 被繼承的已有類稱為基類(或父類)。
  4. 派生出的新類稱為派生類(或子類)

繼承的目的

  • 實(shí)現(xiàn)代碼重用
  • 派生的目的:當(dāng)新的問(wèn)題出現(xiàn),原有程序無(wú)法解決(或不能完全解決)時(shí),需要對(duì)原有程序進(jìn)行改造

派生類的構(gòu)造

  1. 派生類可以直接訪問(wèn)基類的保護(hù)數(shù)據(jù)成員
  2. 只以接口作溝通。即使基類與子類也不例外。這正是類能夠發(fā)揮其生命力的原因所在。
  3. 在構(gòu)造一個(gè)子類時(shí),完成其基類部分的構(gòu)造由基類的構(gòu)造函數(shù)去做

繼承的訪問(wèn)權(quán)限

  • 不同繼承方式的影響主要體現(xiàn)在:
  1. 派生類成員對(duì)基類成員的訪問(wèn)權(quán)限
  2. 通過(guò)派生類對(duì)象對(duì)基類成員的訪問(wèn)權(quán)限
  • 三種繼承方式
  1. 公有繼承
  2. 私有繼承
  3. 保護(hù)繼承

公有繼承

  1. 基類的public和protected成員的訪問(wèn)屬性在派生類中保持不變,但基類的private成員不可直接訪問(wèn)。
  2. 派生類中的成員函數(shù)可以直接訪問(wèn)基類中的public和protected成員,但不能直接訪問(wèn)基類的private成員。
  3. 通過(guò)派生類的對(duì)象只能訪問(wèn)基類的public成員。

私有繼承

  1. 基類的public和protected成員都以private身份出現(xiàn)在派生類中,但基類的private成員不可直接訪問(wèn)。
  2. 派生類中的成員函數(shù)可以直接訪問(wèn)基類中的public和protected成員,但不能直接訪問(wèn)基類的private成員。
  3. 通過(guò)派生類的對(duì)象不能直接訪問(wèn)基類中的任何成員。

保護(hù)繼承

  1. 基類的public和protected成員都以protected身份出現(xiàn)在派生類中,但基類的private成員不可直接訪問(wèn)。
  2. 派生類中的成員函數(shù)可以直接訪問(wèn)基類中的public和protected成員,但不能直接訪問(wèn)基類的private成員。
  3. 通過(guò)派生類的對(duì)象不能直接訪問(wèn)基類中的任何成員

默認(rèn)的繼承是私有

private 和protect的區(qū)別

  1. 在單個(gè)類中,protected和private沒(méi)有什么區(qū)別。
  2. 但在繼承關(guān)系中,基類的private成員不但對(duì)應(yīng)用程序隱藏,
  3. 甚至對(duì)派生類也隱藏。而基類的保護(hù)成員則只對(duì)應(yīng)用程序隱藏,而對(duì)派生類則毫不隱瞞。

繼承

  • 繼承
    • 抽出共性,重新分配,代碼可重用
    • 繼承可以使我們用一種簡(jiǎn)單的方式來(lái)描述事物
#include <iostream>
using namespace std;
class Shop
{
    public:
        int m_a;
        void sailDailyGoods()
        {
            cout<<"賣日用品"<<endl;
            return;
        }
};
class Market:public Shop
{
    public:
        void saleFood()
        {
            m_a=110;
            cout<<"賣食物"<<endl;
            return;
        }
};
class SuperMarket:public Market
{
    public:
        void pay()
        {
            cout<<"代繳水電費(fèi)"<<endl;
            return;
        }
};
int main(int argc,char**argv)
{
    SuperMarket sm;
    sm.sailDailyGoods();
    sm.saleFood();
    sm.pay();
    
}
  • 三種繼承方式
    • 公有繼承
    • 私有繼承
    • 保護(hù)繼承
  • 派生類可以直接訪問(wèn)
#include <iostream>
using namespace std;
class shop
{
    int m_a;
    public:
        shop(int a/*=110*/)
        {
            m_a=a;
            cout<<"shop construct"<<m_a<<endl;
        }
        ~shop()
        {
            cout<<"析構(gòu)shop"<<endl;
        }
        /*
        shop()
        {
            m_a=333;
        }*/
};
class test
{
    public:
        test()
        {
            cout<<"test construct"<<endl;
        }
        ~test()
        {
            cout<<"析構(gòu)test"<<endl;
        }
};
class Market:public shop//繼承重用代碼
{
    test m_t;
    public:
        Market():shop(120)
        {
            cout<<"Market construct"<<endl;
        }
        ~Market()
        {
            cout<<"析構(gòu)Market"<<endl;
        }
};
int main(int argc,char**argv)
{
    Market m;
    cout<<"===="<<endl;
}
//shop construct120
//test construct
//Market construct
//====
//析構(gòu)Market
//析構(gòu)test
//析構(gòu)shop

#include<iostream>
using namespace std;
class Shop
{
protected:
    int m_a;
public:
    void saleDaliyGoods()
    {
        cout<<"賣日用品"<<endl;
        return;
    }
};
class Market:public Shop
{
public:
    void saleFood()
    {
        m_a=10;
        cout<<"食品"<<m_a<<endl;
        return;
    }
};
int main(int argc,char **argv)
{
    Market m;
//  m.m_a=10;
    m.saleDaliyGoods();
    m.saleFood();
}
//用protected ,則在Market中可以訪問(wèn),但是在int main中,m.m_a=10;是錯(cuò)誤的

#include<iostream>
using namespace std;
class Shop
{
public:
    Shop()
    {
        cout<<"Shop construct"<<endl;
    }
};
class Market:public Shop
{
public:
    Market()
    {
        cout<<"Market construct"<<endl;
    }
};
int main()
{
    Market m;
}
//結(jié)果為:
//shop construct
//Market construct

#include<iostream>
using namespace std;
class Shop
{
    int m_a;
public:
    Shop(int a)
    {
        m_a=a;
        cout<<"Shop construct"<<m_a<<endl;
    }
};
class Market:public Shop
{
public:
    Market():Shop(222)
    {
        cout<<"Market construct"<<endl;
    }
};
int main()
{
    Market m;
}
//結(jié)果為:
//Shop construct222
//Market construct

#include<iostream>
using namespace std;
class Shop
{
    int m_a;
public:
    Shop(int a=111)
    {
        m_a=a;
        cout<<"Shop construct"<<m_a<<endl;
    }

};
class Market:public Shop
{
    
public:
    Market():Shop()
    {
        cout<<"Market construct"<<endl;
    }
    
};
int main()
{
    Market m;
}
//結(jié)果為:Shop construct111
//Market construct

#include<iostream>
using namespace std;
class Shop
{
    int m_a;
public:
    Shop(int a)
    {
        m_a=a;
        cout<<"Shop construct"<<m_a<<endl;
    }
    Shop()
    {
        m_a=333;
        cout<<"Shop construct"<<m_a<<endl;
    }
};
class Market:public Shop
{
    
public:
    Market():Shop()
    {
        cout<<"Market construct"<<endl;
    }
    
};
int main()
{
    Market m;
}
//結(jié)果為:
Shop construct333
//Market construct

賦值兼容規(guī)則

  • 賦值兼容規(guī)則中所指的替代包括以下的情況:
  1. 派生類的對(duì)象可以賦值給基類對(duì)象。
  2. 派生類的對(duì)象可以初始化基類的引用。
  3. 派生類對(duì)象的地址可以賦給指向基類的指針。

重寫虛函數(shù),實(shí)現(xiàn)多態(tài)(多態(tài))

  • 多態(tài)是C++的重要概念,是面向?qū)ο笤O(shè)計(jì)理念的精華, 大型的C++軟件項(xiàng)目和大型的系統(tǒng)實(shí)現(xiàn)中,多態(tài)是必不可少的,可以減輕系統(tǒng)升級(jí)、維護(hù)、調(diào)試的工作量和復(fù)雜度.

多態(tài)的條件

  1. C++多態(tài)是通過(guò)虛函數(shù)實(shí)現(xiàn)的,虛函數(shù)允許子類重新定義成員函數(shù),而子類重新定義父類接口的做法稱為覆蓋或重寫
  2. 子類需要公有繼承于父類
  3. 定義父類的指針或引用,然后通過(guò)指針或引用去調(diào)用相應(yīng)的虛函數(shù)

虛函數(shù)

  1. 成員函數(shù)之前加上 virtua
    關(guān)鍵字之后,就是虛成員函數(shù)

判斷子類的成員函數(shù)是否為虛函數(shù)

  1. 該函數(shù)是否與基類的虛函
    數(shù)相同的函數(shù)名
  2. 該函數(shù)是否與基類的虛函數(shù)有相同的參數(shù)個(gè)數(shù)及相同的對(duì)應(yīng)參數(shù)類型
  3. 該函數(shù)是否與基類的虛函數(shù)有相同的返回值或者滿足賦值兼容規(guī)則的指針、引用的返回值

多態(tài)與非多態(tài)的區(qū)別

  • 多態(tài)與非多態(tài)的區(qū)別就是函數(shù)地址是早綁定還是晚綁定。如果函數(shù)的調(diào)用在編譯期間就可以確定函數(shù)的調(diào)用地址,并生產(chǎn)代碼,就是靜態(tài)的也就是早綁定是非多態(tài)的。而如果函數(shù)的調(diào)用地址不能在編譯期間確定,需要在運(yùn)行時(shí)才確定,這就是晚綁定也稱動(dòng)態(tài)聯(lián)編,是多態(tài)的
#include <iostream>
using namespace std;
class B0
{
    public:
        virtual void display()
        {
            cout<<"B0 Display"<<endl;
        }
};
class B1:public B0
{   
    public:
        void display()
        {
            cout<<"B1 Display"<<endl;
        }
};
class B2:public B1
{   
    public:
        void display()
        {
            cout<<"B2 Display"<<endl;
        }
};
int main()
{
/*
    B0 b0;b0.display();
    B1 b1;b1.display();
    B2 b2;b2.display();
    B2 b3;b3.B1::display();*/
    B0 b0,*p0;
    p0=&b0;
    p0->display();
    B1 b1;
    p0=&b1;//一個(gè)函數(shù)表現(xiàn)出不同的形態(tài)
    p0->display();
    B2 b2;
    p0=&b2;
    p0->display();
}

重載與重寫的區(qū)別(重寫重要)

  1. 重載overload:函數(shù)名相同,參數(shù)列表不同,重載只是在同類的內(nèi)部存在,并且不能靠返回值來(lái)判斷
  2. 重寫overwrite:也稱覆蓋,子類重新定義父類中的同名函數(shù)。函數(shù)特征相同,單數(shù)具體實(shí)現(xiàn)不同,基類的函數(shù)要有關(guān)鍵字virtual,主要是在繼承關(guān)系中出現(xiàn)。
  • 多態(tài)實(shí)現(xiàn)原理
    • 每個(gè)含有虛函數(shù)的類,編譯器都會(huì)為他自動(dòng)生成一個(gè)虛表,表中每一個(gè)元素都是指向虛函數(shù)的地址。
    • 虛指針:編譯器會(huì)為含有虛函數(shù)的類增加一個(gè)成員,是指向該虛函數(shù)的指針,每一個(gè)由此的派生出來(lái)的類都活有一個(gè)虛指針。
  • 函數(shù)的調(diào)用地址在編譯期間就能決定,那就是非多態(tài)。如果在運(yùn)行時(shí)才能確定,就是多態(tài)的。
  • 繼承為了實(shí)現(xiàn)代碼重用。
  • 多態(tài)為了實(shí)現(xiàn)接口重用。
#include <iostream>
using namespace std;
/*class B0
{
    public:
        virtual void display()
        {
            cout<<"B0 Display"<<endl;
        }
};
class B1:public B0
{   
    public:*/
    /*  void display()
        {
            cout<<"B1 Display"<<endl;
        }*/
/*};

int main()
{*/
/*
    B0 b0;b0.display();
    B1 b1;b1.display();
    B2 b2;b2.display();
    B2 b3;b3.B1::display();*/
/*  B0 b0,*p0;
    p0=&b0;
    p0->display();
    B1 b1;
    p0=&b1;//一個(gè)函數(shù)表現(xiàn)出不同的形態(tài)
    p0->display();
    
}*/
class B0
{
    public:
        virtual void display()=0;//純虛函數(shù)
};
class B1:public B0
{   
    public:
        void display()
        {
            cout<<"B1 Display"<<endl;
        }
};

int main()
{
/*
    B0 b0;b0.display();
    B1 b1;b1.display();
    B2 b2;b2.display();
    B2 b3;b3.B1::display();*/
    B0 *p0;
    B1 b1;
    p0=&b1;//一個(gè)函數(shù)表現(xiàn)出不同的形態(tài)
    p0->display();
}
//B1 Display
  • 共態(tài)的使用
#include <iostream>
using namespace std;
class person
{
    public:
        int id;
};
class A:virtual public person
{
    public:
        void performForA()
        {
            cout<<"呆,傻,逗,癡"<<endl;
        }
};
class B:virtual public person
{
    public:
        void performForB()
        {
            cout<<"浪,騷,蕩,賤"<<endl;
        }
};
class sb:public A,public B
{
};
int main()
{
        sb jxb;
        jxb.performForA();
        jxb.performForB();      
        jxb.id=3;
        person *p=&jxb;
}

繼承順序

  1. 任何虛擬基類的構(gòu)造函數(shù)按照它們被繼承的順序構(gòu)造
  2. 任何非虛擬基類的構(gòu)造函數(shù)按照它們被繼承的順序構(gòu)造;
  3. 任何成員對(duì)象的構(gòu)造函數(shù)按照它們聲明的順序調(diào)用;
    類自己的構(gòu)造函數(shù)。
  • 如果兩個(gè)父類。。。。。。。。。。。。。。。。
#include <iostream>
using namespace std;
class A
{
    public:
        A()
        {
            cout<<"A construct"<<endl;
        }
        ~A()
        {
            cout<<"析構(gòu)A"<<endl;
        }
};
class B
{
    public:
        B()
        {
            cout<<"B construct"<<endl;
        }
        ~B()
        {
            cout<<"析構(gòu)B"<<endl;
        }
};
class C
{
    public:
        C()
        {
            cout<<"C construct"<<endl;
        }
        ~C()
        {
            cout<<"析構(gòu)C"<<endl;
        }
};
class D:public A,public B,virtual public C
{
    public:
        D()
        {
            cout<<"D construct"<<endl;
        }
        ~D()
        {
            cout<<"析構(gòu)D"<<endl;
        }
};
int main()
{
    D d;
}/*
C construct
A construct
B construct
D construct
析構(gòu)D
析構(gòu)B
析構(gòu)A
析構(gòu)C*/
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容