Stories

Detail Return Return

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

複習

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;
}

Add a new Comments

Some HTML is okay.