小凱15天快速講完c語言-簡單學習第十課

瞿小凱發表於2022-11-24

複習

0.1 建構函式

物件被建立的時候,會自動呼叫
全域性物件
區域性物件
堆中的物件
建構函式的函式名字和類名一樣
建構函式沒有返回值,可以有引數,可以過載,一個類中可以實現多個建構函式

0.2 解構函式

物件被銷燬的時候,會自動呼叫
全域性物件 程式結束的時候會自動銷燬
區域性物件 離開作用域的時候,會自動銷燬
堆中的物件 delete釋放的時候,會自動銷燬
解構函式名字:~ 類名
析構沒有返回值,沒有引數,不能過載,一個類就只能有1個

0.3 複製建構函式

一個物件初始化另外一個物件的時候,會自動呼叫

class  CTest
{
//......
    private:
int m_nNum;
}
1. 直接初始化
int  main()
{
     CTest  obj1;
     CTest obj2 = obj1;
}
2. 函式傳參的時候
void  Fun(CTest obj)
{

}
Fun(obj1);

編譯器預設會提供一個淺複製的複製構造。
當類中成員,有指標指向堆空間的時候,我們大機率需要自己實現一個深複製的複製構造

0.4 轉換建構函式

只有一個引數的建構函式,也稱之為轉換建構函式。
這個語法容易造成誤解,可以使用explicit 禁止轉換

1. 繼承

1.1 繼承的基本語法

#include <iostream>
class CA
{
public:
    CA() :m_a(1)
    {

    }
    void Fun1()
    {
        std::cout << m_a;
    }
public:
    int m_a;
};
//繼承的語法:
//class 子類:public 父類的名字
class CB:public CA
{
public:
    CB() :m_b(2)
    {

    }
    void Fun2()
    {
        std::cout << m_b;
    }
public:
    int m_b;
};
int main()
{
    std::cout << sizeof(CB)<<std::endl;
    CB objB;
    objB.Fun1();//這個函式從CA繼承而來
    objB.Fun2();//自己實現的

    objB.m_a = 100;//使用的從CA繼承來的
    objB.m_b = 200;//自己定義的

    return 0;
}

圖片.png

1.3 繼承的許可權

子類包含父類中的所有成員
子類也可以使用父類中的成員
在使用類的成員的時候,有兩種情況:
1.透過類物件,在類外使用成員變數和成員函式
2.在類內的函式中,直接使用成員變數和成員函式
圖片.png

#include <iostream>
class CBase
{
public:
    int m_BaseA;
    void FunCBase()
    {
        std::cout << "我是CBase的FunCBase" << std::endl;
    }
protected:
    int m_BaseB;
private:
    int m_BaseC;
};
class CA :public CBase
{
public:
    void FunCA()
    {
        m_BaseA = 10;
        m_BaseB = 20;
        //m_BaseC = 5; 父類中的私有成員,無論如何繼承都不能直接訪問
    }
};
class CB :protected CBase
{
public:
    void FunCB()
    {
        m_BaseA = 10;
        m_BaseB = 20;
        //m_BaseC = 5; 父類中的私有成員,無論如何繼承都不能直接訪問
    }
};
class CC :private CBase
{
public:
    void FunCC()
    {
        m_BaseA = 10;
        m_BaseB = 20;
        //m_BaseC = 5; 父類中的私有成員,無論如何繼承都不能直接訪問
    }
};
class CTest1 :public CB
{
public:
    void FunTest1()
    {
        m_BaseA = 10;
        m_BaseB = 20;
    }
};
class CTest2 :public CC
{
public:
    void FunTest2()
    {
        //m_BaseA = 10;
        //m_BaseB = 20;
    }
};

int main()
{
    CA objA;
    CB objB;
    CC objC;
    //CA是公有繼承,公有繼承也稱之為  【介面繼承】
    //父類中是公有的,在子類中還是公有的,介面依然是介面
    objA.m_BaseA = 1;
    objA.FunCBase();
    //私有繼承或者保護結成也稱之為  【實現繼承】
    //會使得父類中的公有成員變為私有的或者保護的
    //子類就失去了父類中的介面,變成了內部實現。
    //objB.m_BaseA = 2;
    //objB.FunCBase();
    //objC.m_BaseA = 2;
    //objC.FunCBase();

    //保護繼承和私有繼承,都是實現繼承,有什麼區別呢???
    //私有繼承,原公有或者保護成員,
    //在子類中都變成了私有成員,再往下繼承,就會不可訪問
    //保護繼承,原公有或者保護成員,
    //在子類中都是保護屬性,再往下繼承,類內還是能訪問的

    return 0;
}

1.4 重定義問題

父類中和子類中,只要重名了,就會發生重定義。透過子類物件訪問同名成員,預設訪問的都是子類自己的。父類中的同名成員被隱藏了。
無論是函式名和函式名同名,還是變數名和變數名同名,還是變數名和函式名同名。函式名同名,無論引數是一樣還是不一樣,都是重定義。父類中的識別符號,都會隱藏,預設使用子類自己的。
注意:父類和子類中同名函式,不能構成過載的,因為他們作用域不同

#include <iostream>
class CBase
{
public:
    void Fun()
    {
        std::cout << "CBase的Fun" << std::endl;
    }
    void Fun1()
    {

    }
    int m_BaseA;
};
class CA :public CBase
{
public:
    void Fun1(int n)
    {

    }
    int m_BaseA;
    int Fun;
};
int main()
{
    CA obj;
    obj.Fun = 100;
    obj.Fun();
    obj.Fun1();
    obj.m_BaseA = 10;
    return 0;
}

1.5 在繼承關係中構造析構呼叫問題

#include <iostream>
class CTest1 
{
public:
    CTest1(int n) :m_Test1(n)
    {
        std::cout << "我是Test1成員構造" << std::endl;
    }
    ~CTest1() 
    {
        std::cout << "我是Test1成員析構" << std::endl;
    }
public:
    int m_Test1;
};
class CTest2
{
public:
    CTest2(int m) :m_Test2(m)
    {
        std::cout << "我是Test2成員構造" << std::endl;
    }
    ~CTest2()
    {
        std::cout << "我是Test2成員析構" << std::endl;
    }
public:
    int m_Test2;
};
class CBase1
{
public:
    CBase1(int n) :m_BaseA(n)
    {
        std::cout << "我是1父類構造" << std::endl;
    }
    ~CBase1()
    {
        std::cout << "我是1父類析構" << std::endl;
    }
    int m_BaseA;
};
class CBase2
{
public:
    CBase2(int m) :m_BaseA(m)
    {
        std::cout << "我是2父類構造" << std::endl;
    }
    ~CBase2()
    {
        std::cout << "我是2父類析構" << std::endl;
    }
    int m_BaseA;
};
class CA :public CBase1,public CBase2
{
public:
    CA() : obj2(20),m_A(2), obj1(10), CBase2(6), CBase1(5)
    {
        std::cout << "我是子類構造" << std::endl;
    }
    ~CA()
    {
        std::cout << "我是子類析構" << std::endl;
    }
    CTest1 obj1;
    CTest2 obj2;
    int m_A;
};
int main()
{
    CA obj;
    return 0;
}

總結:
1.構造優先呼叫父類,再呼叫成員,最後呼叫自己
2.析構順序和構造順序相反
3.父類構造需要傳參,就在子類的初始化列表中主動呼叫父類的構造傳參
4.成員的構造需要傳參,也需要在類的初始化列表中主動呼叫成員的構造傳參
5.成員的構造順序和定義順序一致
6.父類的構造順序和繼承順序一致

1.6 多繼承的問題

#include <iostream>
class CBase1
{
public:
    CBase1(int n) :m_Base1(n)
    {
        std::cout << "我是1父類構造" << std::endl;
    }
    ~CBase1()
    {
        std::cout << "我是1父類析構" << std::endl;
    }
    int m_Base1;
};
class CBase2
{
public:
    CBase2(int m) :m_Base2(m)
    {
        std::cout << "我是2父類構造" << std::endl;
    }
    ~CBase2()
    {
        std::cout << "我是2父類析構" << std::endl;
    }
    int m_Base2;
};
class CA :public CBase1, public CBase2
{
public:
    CA() :  m_A(2),  CBase2(6), CBase1(5)
    {
        std::cout << "我是子類構造" << std::endl;
    }
    ~CA()
    {
        std::cout << "我是子類析構" << std::endl;
    }
public:
    int m_A;
};
int main()
{
    //子類多繼承的時候,和單繼承差不多的,擁有所有父類的所有成員
    CA obj;
    obj.m_Base1 = 10; //從CBase1繼承來的
    obj.m_Base2 = 20; //從CBase2繼承來的
    obj.m_A = 30;     //這個是它自己的
    return 0;
}

具體示例:

#include <iostream>
class Cpeople
{
public:
    void Run()
    {
        std::cout << "我在快樂的跑步" << std::endl;
    }
public:
    char m_szName[20];
    int m_nGender;
    int nAge;
};
class CSpider//蜘蛛
{
public:
    void Tusi()
    {
        std::cout << "Piu" << std::endl;
    }
public:
    int nAge;
};
class CSuperHero
{
public:
    void SaveWorld()
    {
        std::cout << "拯救世界" << std::endl;
    }
    int nAge;
};
class CSPiderMan :public Cpeople, public CSpider,public CSuperHero
{
public:
    void BianShen()
    {
        std::cout << "Piu" << std::endl;
    }
};
int main()
{
    CSuperHero obj1;
    obj1.SaveWorld();
    CSPiderMan obj;
    obj.Run();
    obj.Tusi();
    obj.BianShen();
    obj.SaveWorld();

    //注意多繼承的時候,多個父類如果有同名成員,會造成二義性問題。
    //可以使用作用域防止BUG產生
    //更好的方式是使用虛繼承
    obj.CSpider::nAge = 5;
    obj.Cpeople::nAge = 20;
    obj.CSuperHero::nAge = 15;
    return 0;
}

2. 名稱空間

名稱空間機制是為了防止命名衝突
math1.h

#pragma once
namespace Code1
{
    int GetMax(int a, int b);

    int GetAdd(int a, int b);
}

math1.cpp

namespace Code1
{
    int GetMax(int a, int b)
    {
        if (a > b)
        {
            return a;
        }
        else
        {
            return b;
        }
    }
    int GetAdd(int a, int b)
    {
        return a + b;
    }
}

math2.h

#pragma once
namespace Code2
{
    int GetMax(int a, int b);
    int GetAvg(int a, int b);
    namespace inner
    {
        int GetMin(int a, int b);
    }
}

math2.cpp

namespace Code2
{
    int GetMax(int a, int b)
    {
        if (a < b)
        {
            return b;
        }
        else
        {
            return a;
        }
    }
}
namespace Code2
{
    int GetAvg(int a, int b)
    {
        return (a + b) / 2;
    }
    namespace inner
    {
        int GetMin(int a, int b)
        {
            if (a < b)
            {
                return a;
            }
            else
            {
                return b;
            }
        }
    }

}

主檔案

#include "Math1.h"
#include "Math2.h"
using Code1::GetMax;
using Code2::GetAvg;
namespace mi = Code2::inner;
int main()
{

    GetMax(10, 20);
    GetAvg(10, 15);
    mi::GetMin(5, 10);
    return 0;
}

補充作業:
1.設計一個狼人類,繼承自人類和狼類。
a.人類:學習方法 有年齡屬性 有參構造進行初始化
b.狼類:攻擊方法 有奔跑速度屬性 有參構造進行初始化
c.狼人類:變身方法 有攻擊力屬性 有參構造進行初始化
定義正確定義狼人物件,能夠呼叫繼承過來的所有函式

#include<iostream>
using namespace std;
class people
{
public:
    people(int age) :m_age(age) //建構函式使用
    {

    }
    void study()
    {
        cout << "i am study now" << endl;          //學習方法
    }
private:
    int m_age;
};
class wolf
{
public:
    wolf(int speed) :m_speed(speed)    //建構函式使用
    {

    }
    void attack()
    {
        cout << "i am fighting here" << endl;
    }
private :
    int m_speed;
};
class wolfman :public people, public wolf  //多繼承,繼承多個成員的屬性
{
public:
    wolfman(int power,int age, int speed):power(power),people(age),wolf(speed)//建構函式,用於初始化成員
    {

    }
    void bianshen()
    {
        cout << "update!" << endl;
    }
private:
    int power;  //初始化新定義的power
};
int main()
{
    wolfman obj(100, 20, 70);
    obj.study();
    obj.attack();
    obj.bianshen();
    return 0;
}

相關文章