• 【c++基础】第三章 宏观部分:面向对象之类的层次结构与相互关系


    关系

    • has_a关系
    • use_a关系
    • is_a关系

    has_a关系

    又名:包含,组合,层次,复合关系

    是一个类,由其他类对象共同组成

    #include 
    
    using namespace std;
    
    class Desk{
        int a;
    public:
        Desk(){
            cout << "Desk构造" << this << endl;
        }
        ~Desk(){
            cout << "Desk析构" << endl;
        }
    };
    
    class Person{
        int a;
    public:
        Person(){
            cout << "Person构造" << this << endl;
        }
        ~Person(){
            cout << "Person析构" << endl;
        }
        void say(){
            cout << "hello" << endl;
        }
    };
    
    class Chair{
        int a;
    public:
        Chair(){
            cout << "Chair构造" << this << endl;
        }
        ~Chair(){
            cout << "Chair析构" << endl;
        }
    };
    
    class Room{
    private:
        Person p;
        Desk d;
        Chair c;
    public:
        Room(){
            cout << "Room构造" << this << endl;
        }
        ~Room(){
            cout << "Room析构" << endl;
        }
    };
    
    int main()
    {
        Room r;
        Room *r1=new Room();
        ((Person *)(r1))->say();
        delete r1;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    has_a关系中构造与析构的先后顺序:

    • 构造:先顺序构造类中成员对象,最后构造最外层对象
    • 析构:先析构最外层对象,然后逆序析构类中成员对象

    内存关系布局图:
    在这里插入图片描述

    use_a关系(友元关系)

    类与函数,类与类之间的亲密关系。

    存在两种形式:友元函数友元类

    友元函数

    语法:friend 函数声明;

    在类中使用friend 关键字声明类外的某个函数为本类的友元函数,类内即生效

    特性:

    • 当友元函数有本类的对象时,在友元函数中对象访问类中的属性是不受权限约束的
    #include 
    
    using namespace std;
    
    class Boy{
        int money;
    public:
        Boy(int money){
            this->money=money;
        }
    
        friend void beautifulGril(Boy &boy);//friend关键字,声明成为友元函数
        
    };
    
    void beautifulGril(Boy &boy){
        cout << boy.money << endl;
    }
    
    int main()
    {
        Boy boy(10000);
    
        beautifulGril(boy);
        
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    友元类

    语法:friend class 友元类名

    单文件,只能实现单友谊

    #include 
    
    using namespace std;
    
    class Boy{
    private:
        string name;
        int age;
    public:
        Boy(string name,int age){
            this->name=name;
            this->age=age;
        }
        void hard_work(){
            cout << "woking" << endl;
        }
        
        friend class Gril;
    };
    
    class Gril{
    private:
        string name;
        int age;
    public:
        Gril(string name,int age){
            this->name=name;
            this->age=age;
        }
        void go_shopping(){
            cout << "shopping" << endl;
        }
        void go_shopping(Boy& boy){
            cout << boy.name << "  .ing  " << this->name << "  shopping  " << endl;
        }
        
    };
    
    int main()
    {
        Boy boy("yao",18);
        Gril gril("sun",17);
        gril.go_shopping(boy);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    分文件编程

    boy.h:

    #ifndef BOY_H
    #define BOY_H
    #include 
    using namespace std;
    #include "girl.h"
    class Girl;
    
    class Boy
    {
    public:
        string name;
        int age;
    public:
        Boy(string name,int age);
        void hard_work(Girl& girl);
        friend class Girl;
    };
    
    #endif // BOY_H
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    girl.h:

    #ifndef GRIL_H
    #define GRIL_H
    #include 
    using namespace std;
    #include "boy.h"
    class Boy;
    
    class Girl
    {
    public:
        string name;
        int age;
    public:
        Girl(string name,int age);
        void goshopping(Boy& boy);
        friend class Boy;
    };
    
    #endif // GRIL_H
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    boy.c:

    #include "boy.h"
    #include "girl.h"
    
    Boy::Boy(string name,int age)
    {
        this->name=name;
        this->age=age;
    }
    
    void Boy::hard_work(Girl& girl){
       cout << girl.name << "  accompany.ing  " << this->name << "  Set out for Happiness" << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    girl.c:

    #include "girl.h"
    
    Girl::Girl(string name,int age)
    {
        this->name=name;
        this->age=age;
    }
    void Girl::goshopping(Boy &boy){
        cout << boy.name << "  be being  " << this->name << "  shopping" << endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    main.c:

    #include 
    #include "girl.h"
    #include "boy.h"
    
    using namespace std;
    
    int main()
    {
        Boy boy("liangliang",18);
        Girl girl("minmin",17);
        girl.goshopping(boy);
        boy.hard_work(girl);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    不存在传导关系(即A与B是好友,B与C是好友,A和C不一定是好友)

    is_a关系(继承关系)

    继承(派生)作用:提高代码复用性和高拓展性

    单继承

    语法:class+子类 :继承权限+父类

    在这里插入图片描述

    #include 
    
    using namespace std;
    
    class Car{
        int weight;
    public:
        void run(){
            cout << "runing" << endl;
        }
    };
    
    class BMW : public Car//is a Car  类
    {
    public:
        
        int size;
        void show(){
            cout << "i am is BMW" << endl;
        }
    };
    
    class BNZ : private Car//is a Car  类
    {
    public:
        int size;
        void show(){
            this->run();
            cout << "i am is BNZ" << endl;
        }
    };
    
    class TRAM : public BMW//is a BMW  类
    {
    public:
        void say(){
            cout << "my father is BMW" << endl;
        }
    };
    
    int main()
    {
        cout << sizeof (Car) << endl;
        Car c;
        c.run();
    
        cout << sizeof (BMW) << endl;
        BMW b;
        b.run();
        b.show();
    
        TRAM tram;
        cout << sizeof (TRAM) << endl;
        tram.run();
        tram.show();
        tram.say();
    
        BNZ bnz;
        bnz.show();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61

    理解:抽象定义基类(父类),继承(派生)子类(继承类)基类本身功能不可修改,此原则叫做开闭原则,即拓展开放,对修改关闭。

    继承关系中构造与析构的先后顺序:

    • 构造:先顺序构造父类中成员对象,最后构造子类中对象
    • 析构:先析构子类对象,然后逆序析构父类中成员对象当子类析构触发,父类析构也执行。

    单继承关系下内存布局:
    在这里插入图片描述

    #include 
    
    using namespace std;
    
    class A{
        int a;
    public:
        A(){
           cout << "i am is A structure" << this <<  endl;
        }
        ~A(){
           cout << "i am is A destruct" << endl;
        }
    };
    
    class B : public A{
        int b;
    public:
        B(){
           cout << "i am is B structure" << this << endl;
        }
        ~B(){
           cout << "i am is B destruct" << endl;
        }
    };
    
    int main()
    {
        A a;
        B b;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    注意:派生中的基类,不同于本身类

    解决同名问题(隐藏机制):

    当继承类与基类存在同名,基类隐藏在类域之中,如果想访问基类,方法:域名::访问

    #include 
    
    using namespace std;
    
    class A{
    public:
        int a=100;
        A(){
           cout << "i am is A structure" << this <<  endl;
        }
        ~A(){
           cout << "i am is A destruct" << endl;
        }
        void show(){
            cout << "A inside" << endl;
        }
    };
    
    class B : public A{
    public:
        int a=1000;
        B(){
           cout << "i am is B structure" << this << endl;
        }
        ~B(){
           cout << "i am is B destruct" << endl;
        }
        void show(){
            cout << "B inside" << endl;
        }
    };
    
    int main()
    {
        B b;
        cout << b.a << endl;//1000
        cout << b.A::a << endl;//100
        b.show();//B inside
        b.A::show();//A inside
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    类型兼容规则is_a关系:子类是基类的一个拓展。

    兼容语法如下:

    1. 基类指针可以安全的访问继承类对象
    2. 当父类不存在默认构造时,应在继承类的构造函数的初始化列表中初始化
    3. 父类指针只能调用子类类域中的所包含的基类中的类域方法和属性,但存在继承类不能析构问题。
    4. 所以基类指针释放资源时,要强转成子类指针才能释放,避免内存泄漏。
    #include 
    
    using namespace std;
    class Person{
    private:
        string name;
        int age;
    public:
        Person(string name,int age){
            this->name=name;
            this->age=age;
            cout << "Person structure" << endl;
        }
        void showInfo(){
            cout << "姓名:" << this->name << ",年龄" << this->age << endl;
        }
        int getAge(){
            return  this->age;
        }
        string getName(){
            return  this->name;
        }
        ~Person(){
            cout << "Pseron desturct" << endl;
        }
    };
    
    class Stu:public Person
    {
    private:
        int score;
        int *p;
    public:
        //当父类中没有默认构造,应该在子类的构造函数的初始化列中指定。
        Stu(string name,int age,int score):Person(name,age)
        {
            this->score=score;
            this->p=new int[1024];
            cout << "stu structure" << endl;
        }
        void showInfo(){
            cout << "姓名:" << this->getName() << ",年龄" << this->getAge() << ",成绩" << score <showInfo();
    //    Stu* stu=new  Person("yao",19);//不能子类指向父类
        //查看基类指针是否指向继承类(继承类是否开辟空间),方法:把基类指针强转成继承类指针
        ((Stu*)p)->showInfo();
    
        //强转存在风险,原因:继承类可能不开辟空间
        cout << "强制转换的风险性" << endl;
        Person* q=new Person("minmin",18);
        q->showInfo();
        ((Stu*)q)->showInfo();
        delete (Stu*)p;
        delete q;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68

    多继承

    语法:class+子类:继承权限1+父类1,继承权限2+父类2,继承权限n+父类n

    • 优点:可以大大提高代码的复用性

    • 缺点:

      • 存在代码冗余,占用空间大,资源浪费
      • 存在二义性问题,当多个父类中,有同名的函数或属性时,还需要使用类域::域名解析符访问,带来不便
    • 推荐:使用多继承多个抽象类,因为抽象类没有具体的属性,只有**纯虚函数**

    多继承的内存布局:

    在这里插入图片描述
    父类指针指向子类对象时:

    1. 父类指针永远指向父类类域的起始地址。
    2. 如果要释放内存,必须要强转成继承类类型,避免内存泄露。
    #include 
    
    using namespace std;
    
    class Phone{
    public:
        string logo;
        int power;
    public:
        Phone(){
            cout << "Phone structure" << this << endl;
        }
        ~Phone(){
            cout << "Phone destruct" << endl;
        }
        void display(){
            cout << "手机正在显示中" << endl;
        }
    };
    
    class Computer{
    public:
        string logo;
        int power;
    public:
        Computer(){
            cout << "Computer structrue" << this << endl;
        }
        ~Computer(){
            cout << "Computer deatruct" << endl;
        }
        void display(){
            cout << "电脑正在显示中" << endl;
        }
    };
    
    class Pad:public Phone,public Computer
    {
    public:
        Pad(){
            cout <<"Pad structrue"<< this << endl;
        }
        ~Pad(){
            cout <<"Pad destruct"<< endl;
        }
        void playgame(){
            cout << this->Phone::logo << endl;
            this->Phone::display();
            cout << this->Phone::power << endl;
        }
    };
    
    int main()
    {
        cout << sizeof(Pad) << endl;
        Pad p;
        p.playgame();
        Phone* phone=new Pad ();
        cout << phone << endl;
        delete (Pad*)phone;
    
        Computer* computer=new Pad ();
        cout << computer << endl;
        delete (Pad*)computer;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    菱形继承

    分析图:
    在这里插入图片描述
    常看到的模型:
    在这里插入图片描述
    问题解决(virtual关键字):虚继承

    #include 
    
    using namespace std;
    
    class A{
    public:
        int a;
        string name;
        A(){
           cout << "i am is A structure" << this <<  endl;
        }
        ~A(){
           cout << "i am is A destruct" << endl;
        }
    };
    
    class B :virtual  public A{
    public:
        int b;
        B(){
           cout << "i am is B structure" << this << endl;
        }
        ~B(){
           cout << "i am is B destruct" << endl;
        }
    };
    
    class C :virtual public A{
    public:
        int c;
        C(){
           cout << "i am is C structure" << this << endl;
        }
        ~C(){
           cout << "i am is C destruct" << endl;
        }
    };
    
    class D : public B,public C{
    public:
        int d;
        D(){
           cout << "i am is D structure" << this << endl;
        }
        ~D(){
           cout << "i am is D destruct" << endl;
        }
    };
    
    int main()
    {
        D d;
        cout << sizeof (d) << endl;//没有加virtual 96 个
        //加后72个,极大的节省了空间
        //原因,virtual会安插虚基表指针,生成两个指针
        //A为40个字节,B中有一个指针一个int,对齐后16个字节,C中有一个指针一个int,对齐后16个字节,D中int进行对齐,所以共72字节
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    虚基表指针指向虚基表
    虚基表保存着虚基表偏移量
    通过偏移量找到父类中的属性

    开发经验:

    has_a>单继承>多继承(多个抽象类)>菱形继承(virtual)

    多态(polymorphic)

    动态多态、绑定多态

    形成条件(必须满足三条):

    1. 继承关系
    2. 父类中存在虚函数,子类中完成对父类虚函数的重写
    3. 使用父类指针或引用,指向或引用子类实例

    虚函数

    类中成员函数前加virtual,特殊的构造函数不能加vritual

    虚函数特性:类中的虚函数具有虚属性,也就是说函数逻辑可以在子类之中完成重写

    重写:即是类似于子类之中对父类中的同名函数的重新定义

    两个关键字:override(检查重写)与final(结束重写)

    #include 
    
    using namespace std;
    
    class Person{
    private:
        string name;
        int age;
    public:
        Person(string name,int age){
            this->name=name;
            this->age=age;
        }
        virtual void showInfo(){
            cout << "姓名:" << this->name << ",年龄:" << this->age << endl;
        }
        int getAge(){
            return  this->age;
        }
        string getName(){
            return  this->name;
        }
    };
    
    class Stu:public Person{
    private:
        int score;
    public:
        Stu(string name,int age,int score):Person(name,age){
            this->score=score;
        }
        //c++11提供的信关键字override重写,用来检查重写父类虚函数的返回值与形参类型是否匹配,不匹配error
        //关键字final进行重写结束(替换在override位置上)
        void showInfo()override{//同名就是虚函数
            cout << "姓名:" << this->getName() <<"年龄:"  << this->getAge() << "成绩:" << score << endl ;
        }
    };
    
    int main()
    {
        Person* person=new Stu("yao",19,100);
        person->showInfo();//现在是子类的,因为加virtual,进行多态重写
        Person* p=new Stu ("minmin",18,90);
        p->showInfo();
        ((Stu*)person)->showInfo();
        Stu stu("sunsun",18,200);
        stu.Person::showInfo();
        stu.showInfo();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    多态底层机制:虚函数、虚函数表指针、虚函数表

    关系解析图:

    在这里插入图片描述
    虚函数表指针由构造函数创建 .rodata段存放虚函数表 虚函数表存放虚函数的地址偏移量

    **通过virtual关键字进行重写 **override关键字进行检查重写 final关键字进行结束重写

    多态实例:

    实现飞行员可以驾驶各类飞机:

    思路:

    1. 创建一个基类(飞行员),定义一个虚函数,用于多态继承
    2. 创建多个继承类,共同继承基类,功能实现
    #include 
    
    using namespace std;
    class Plan{
    public:
        virtual void fly(){
            cout << "飞机正在飞" << endl;
        }
    };
    
    class AirBus:public Plan{
    public:
        void fly()override{
            cout << "客中客车在飞行" << endl;
        }
    };
    
    class J20:public Plan{
    public:
        void fly()override{
            cout << "J20在飞行" << endl;
        }
    };
    
    class Polit{
    public:
        void driver(Plan& plan){//Plan& plan=
            plan.fly();
        }
    };
    
    int main()
    {
        AirBus airbus;
        J20 j20;
        Polit polit;
        polit.driver(airbus);
        polit.driver(j20);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    多态问题

    问题子析构无法得到执行

    解决方案虚析构(基类析构函数为虚析构)

    好处避免内存泄漏,无需强转

    原理多态状态下,子析构进行重写

    #include 
    
    using namespace std;
    
    class Person{
    private:
        string name;
        int age;
    public:
        Person(string name,int age){
            this->name=name;
            this->age=age;
            cout << "Person structure" << endl;
        }
        virtual void showInfo(){
            cout << "姓名:" << this->name << ",年龄:" << this->age << endl;
        }
        int getAge(){
            return  this->age;
        }
        string getName(){
            return  this->name;
        }
        virtual ~Person(){ //设计为虚析构
            cout << "Person destruct" << endl;
        }
    };
    
    class Stu:public Person{
    private:
        int score;
    public:
        Stu(string name,int age,int score):Person(name,age){
            this->score=score;
            cout << "Stu structure" << endl;
        }
        //c++11提供的信关键字override重写,用来检查重写父类虚函数的返回值与形参类型是否匹配,不匹配error
        //关键字final进行重写结束(替换在override位置上)
        void showInfo()override{//同名就是虚函数
            cout << "姓名:" << this->getName() <<"年龄:"  << this->getAge() << "成绩:" << score << endl ;
        }
        ~Stu()override{
            cout << "Stu destruct" << endl;
        }
    };
    
    int main()
    {
        Person* person=new Stu("yao",19,100);
        person->showInfo();//现在是子类的,因为加virtual,进行多态重写
        delete (Stu*)person;
        cout << "-------------------------------------------------" << endl;
        Person* p=new Stu ("minmin",18,90);
        p->showInfo();
        delete p;  //不强转,子析构不会存在,解决方案:虚析构
        cout << "-------------------------------------------------" << endl;
        Person* q=new Person("sun",18);
        q->showInfo();
        delete (Stu*)q;//存在问题,如果不是多态,会报错
        cout << "-------------------------------------------------" << endl;
    
        ((Stu*)person)->showInfo();
        Stu stu("sunsun",18,200);
        stu.Person::showInfo();
        stu.showInfo();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

    纯虚函数与抽象类

    介绍:无函数体,无任何逻辑,是特殊的虚函数,是作为函数API使用的

    逻辑:在子类中重写

    分析:用名字实现功能,本身不定义,由子类继承后进行重写定义,别名《接口函数》

    应用:实现多态,顶层类的设计使用

    规则:该抽象类是不可以定义对象的,只能使用指针或引用,指向或引用子类重写纯虚函数的子类对象。

    包含纯虚函数的类,也叫做抽象类,也被称接口类

    如果子类没有重写纯虚函数,那么子类也将成为抽象类

    #include 
    
    using namespace std;
    
    class A{
    public:
        virtual void showInfo()=0;
        A(){
            cout << "A structure" << endl;
        }
        ~A(){
            cout << "A destruct" << endl;
        }
    };
    
    class B:public A{
    public:
        void showInfo() override{
            cout << "hello world" << endl;
        }
        B(){
            cout << "B structure" << endl;
        }
        ~B(){
            cout << "B destruct" << endl;
        }
    };
    
    class C:public A{
    public:
        void showInfo() override{
            cout << "hi world" << endl;
        }
        C(){
            cout << "C structure" << endl;
        }
        ~C(){
            cout << "C destruct" << endl;
        }
    };
    
    class D{
    public:
        virtual void showInfo_D()=0;
        D(){
            cout << "D structure" << endl;
        }
        ~D(){
            cout << "D destruct" << endl;
        }
    };
    
    class E:public A,public D{
    public:
        void showInfo() override{
            cout << "hello world" << endl;
        }
        void showInfo_D() override{
            cout << "hi world" << endl;
        }
        E(){
            cout << "E structure" << endl;
        }
        ~E(){
            cout << "E destruct" << endl;
        }
    };
    
    int main()
    {
        A *a=new B ();
        a->showInfo();
        delete (B*)a;
    //    A b;//error,不能定义接口类,只能定义指针或引用,指向或引用子类
    //    A* b=new A ();//error,不能定义接口类,只能定义指针或引用,指向或引用子类
        A* c=new C();
        c->showInfo();
        delete (C*)c;
        cout << "-------------------解决多继承问题-------------------" << endl;
        A* e=new E ();
        e->showInfo();
        delete (E*)e;
        D* f=new E ();
        f->showInfo_D();
        delete (E*)f;
        E h;
        h.showInfo();
        h.showInfo_D();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90

    类型转换

    强转,临时有效

    静态类型转换运算符:static_cast

    可转基本类型

    #include 
    
    using namespace std;
    
    int main()
    {
        int a=500;
        double b=3.14;
        cout << (int)b << endl;
        cout << static_cast(b) << endl;
    
        double* p=&b;
        cout << *p << endl;
    //    cout << *(int *)p << endl;//只取小端后4个字节
    //    cout << *(static_cast(p)) << endl;//static_cast强转基本类型指针不被允许,这是一种安全保护
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    不同基本类型之间避免转换

    可转自定义类型

    #include 
    
    using namespace std;
    
    class A{
    private:
        string my_Astr="la la la la" ;
    public:
        void showInfo_A(){
            cout << this->my_Astr << endl;
        }
    };
    
    class B: public A{
    private:
        string my_Bstr="ha ha ha ha" ;
    public:
        void showInfo_B(){
            cout << this->my_Bstr << endl;
        }
    };
    
    int main()
    {
        A* aa=new B ();
        ((B*)aa)->showInfo_B();
        static_cast(aa)->showInfo_B();
    
        A* a=new A();
        //存在的问题
    //    ((B*)a)->showInfo_B();
    //    static_cast(a)->showInfo_B();// 出现bug
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    在继承类中,向下转型并不安全,向上是安全的,原因:可能不会开辟空间,安全由程序员来保证

    动态类型转换运算符:dynamic_cast

    依赖多态完成,会去查看虚函数表中的运行时类型信息识别槽

    如果不存在,返回nullptr,直接杀死进程,并报错安全

    #include 
    
    using namespace std;
    
    class A{
    private:
        string my_Astr="la la la la" ;
    public:
        void showInfo_A(){
            cout << this->my_Astr << endl;
        }
        virtual ~A()=default; //使用c++11的默认函数体
    };
    
    class B: public A{
    private:
        string my_Bstr="ha ha ha ha" ;
    public:
        void showInfo_B(){
            cout << this->my_Bstr << endl;
        }
    };
    
    int main()
    {
        A* aa=new B ();
        ((B*)aa)->showInfo_B();
        static_cast(aa)->showInfo_B();
        dynamic_cast(aa)->showInfo_B();
    
        A* a=new A();	
    //    ((B*)a)->showInfo_B();
    //    static_cast(a)->showInfo_B();// 出现bug
    //    dynamic_cast(a)->showInfo_B();//直接杀死进程
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    常类型转换运算符:const_cast

    使用场景改变指针或者引用的常属性

    #include 
    
    using namespace std;
    
    int main()
    {
        int a=100;
        const int *p=&a;
        *((int*)p)=500;
        cout << a << endl;
        *const_cast(p)=500;
        cout << a << endl;
        cout << *p << endl;
    
        cout << "-----------------改变常引用类型----------" << endl;
        const int& b=a;
        const_cast(b)=600;
        cout << b << endl;
        cout << a << endl;
        
        int c=10;
        int* const q=&a;
        //const_cast(q)=&c;//const不能改变指针的指向,原因:const_cast的设计是根据c++引用而来的,引用不能只能初始化一次
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    注意:

    const不能改变指针的指向,原因:const_cast的设计是根据c++引用而来的,引用只能初始化一次

    解释类型转换运算符:reinterpret_cast

    应用场景:任意类型间的转换

    范围:广泛

    难度:困难

    分析:特别底层,没有类型检查和格式转换,仅仅是简单的二进制转换不安全

    #include 
    
    using namespace std;
    
    int main()
    {
        int a=100;
        int *p=&a;
        cout << reinterpret_cast(p) << endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    临时对象与C++11右值引用

    临时对象

    • 生命周期临时对象声明短暂,仅存在一行
    • 作用用来进行数据传递,会发生拷贝,放在栈中

    右值引用

    作用用引用引用右值引用

    底层实现引用右值在栈上的临时变量,是临时变量的别名

    规则只能进行右值引用,不能引用左值(类型不匹配,相当于指针与类型)

    解决移动语句函数move

    #include 
    
    using namespace std;
    
    class A{
    public:
        virtual void showInfo(){
            cout << "i am is A" << endl;
        }
        A(const A& other){
            cout << "A copy" << endl;
        }
        A(){
            cout<< "A structure" << endl;
        }
        virtual ~A(){
            cout << "A destruct" << endl;
        }
    };
    
    class B:public A{
    public:
        void showInfo()  override{
            cout << "i am is B" << endl;
        }
        B(const B& other){
            cout << "B copy" << endl;
        }
        B(){
            cout<< "B structure" << endl;
        }
        virtual ~B(){
            cout << "B destruct" << endl;
        }
    };
    
    int main()
    {
        A* a=new B ();
        a->showInfo();
        delete (B*)a;
    //    A a1=B();//B是临时变量,单纯一个临时对象,用来进行数据传递,赋值给a1时会发生拷贝,放在栈中
    //    static_cast(a1).showInfo();//error,原因临时对象声明短暂,仅存在一行
    //    a1.showInfo();
    
        //c++中的语法糖
        cout << "---------------------语法糖----------------" << endl;
        const A& a2=B();//B temp=B();(此处发生一次编译优化:RVO编译优化)  const A& a2=temp;
    //    a2.showInfo();//在类函数中加const或者强转
        const_cast(a2).showInfo();//都很麻烦,所以右值引用出现
    
        //c++11的右值引用,右值是没有办法取地址的,如:常量,临时对象
        cout << "---------------------右值引用----------------" << endl;
        A&& a3=B();
        a3.showInfo();
    
        //c++11中的右值引用,如果想要引用左值,c++11提供了std::move(左值对象)
        cout << "---------------------右值引用,引用左值----------------" << endl;
        A&& a4=move(a3);
        a4.showInfo();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    拓展:

    (N)RVO(Return Value Optimization),是一种编译器优化技术,通过该技术,编译器可以减少函数返回时生成临时对象的个数,从某种程度上可以提高程序的运行效率,对需要分配大量内存的类对象其值复制过程十分友好。
    当一个未具名且未绑定到任何引用的临时变量被移动或复制到一个相同的对象时,拷贝和移动构造可以被省略。当这个临时对象在被构造的时候,他会直接被构造在将要拷贝/移动到的对象。当未命名临时对象是函数返回值时,发生的省略拷贝的行为被称为RVO(返回值优化)。
    RVO优化针对的是返回一个未具名对象或N(具名对象),也就是说(N)RVO的功能是消除函数返回时创建的临时对象。
    编译器会把存储这个局部对象的地址和存储返回值临时对象的地址进行复用,也就是说避免了从局部对象到临时对象的拷贝操作,这就是RVO。
    可以通过-fno-elide-constructors来禁用RVO。

    移动构造函数

    是深拷贝的强有力的补充

    逻辑转移资源

    使用条件原对象不在使用时,移动到新的空间

    #include 
    
    using namespace std;
    
    class A{
        int *p;
    public:
        A(){
            this->p=new int[1024];
            cout << "A structure" << endl;
        }
        ~A(){
            if(nullptr==this->p){
                delete []p;
            }
            cout << "A destruct" << endl;
        }
        A(const A& other){
            //升级为深拷贝
            this->p=new int[1024];
            memcpy(this->p,other.p,sizeof (int[1024]));
            cout << "A copy" << endl;
        }
        //c++11提供了移动构造,避免空间浪费
        A(A&& other){
            this->p=other.p;
            other.p=nullptr;
            cout << "A move constructor" << endl;
        }
        void showInfo(){
            cout << "hello" << endl;
        }
    };
    int main()
    {
        A a1;
        //此时放生移动构造
    //    A a2=a1;//此时出现两份,浪费空间
        A a2=std::move(a1);
        a1.showInfo();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    c++异常处理机制

    c中异常处理存在问题:

    #include 
    
    using namespace std;
    
    float my_div(float a,float b){
        if(b==0){
            return -1;
        }
        return a/b;
    }
    float my_add(float a,float b){
        if(my_div(a,b)==-1){
            return -1;
        }
        return a+b+my_div(a,b);
    }
    int main()
    {
        float a=10,b=-10;
        cout << my_add(a,b) << endl;//-1
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    c++中处理机制:

    throw抛出异常关键字

    #include 
    
    using namespace std;
    
    float my_div(float a,float b){
        if(b==0){
            throw -1;
        }
        return a/b;
    }
    float my_add(float a,float b){
        return a+b+my_div(a,b);
    }
    int main()
    {
        float a=10,b=-10;
        cout << my_add(a,0) << endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    在这里插入图片描述

    try{捕获throw抛出异常}catch(表达式){处理异常}语句捕获异常,并处理。

    #include 
    #include 
    
    using namespace std;
    
    class Error:public exception{
    private:
        string error;
    public:
        Error(string error){
            this->error=error;
        }
        //封装一个what函数
        const char * what() const noexcept override{
            return this->error.c_str();
        }
    };
    float my_div(float a,float b){
        if(b==0){
            throw logic_error("除数不可为0");
            //throw -1;
        }
        if(-10==b){
            throw "嘛呢,大哥" ;
        }
        if(88==b){
            throw Error("好好学习");
        }
        return a/b;
    }
    float my_add(float a,float b){
        return a+b+my_div(a,b);
    }
    int main()
    {
        float a=10,b=-10;
        try {
            //一旦出现异常,就要跳转到catch,所以try...catch不能分开
            cout << my_add(a,88) << endl;
            //...下面语句不会被执行
            cout << "hello" << endl;
        } catch (exception& error) {//多态
            //catch (int error)
            cout << error.what() << endl;
            //cout << "除数不可为0" << endl;
        }catch(const char* c_char){
            cout << c_char << endl;
        }catch(Error& error){
            cout << error.what() << endl;
        }
        //这句话正常执行
        cout << "11111" << endl;
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
  • 相关阅读:
    【2022全网最全教程】快速上手前端自动化测试
    【并发编程】原子类
    进销存管理的基本功能
    海豚调度系列之:任务类型——Apache SeaTunnel
    Turbos Finance DEX提供高效的智能路由
    dubbo发送过程编码失败,会唤醒发送线程吗?
    【JavaScript】你真的熟悉bind吗
    栅栏涂色题
    网络安全: Kali Linux 进行 SSH 渗透与防御
    centos下
  • 原文地址:https://blog.csdn.net/distant_Rove666/article/details/128187742