zoukankan      html  css  js  c++  java
  • 24.C++- 抽象类(存虚函数)、接口、多重继承

    抽象类和接口

    什么是抽象类

    • 用来表示现实世界中的抽象概念
    • 是一种只能定义类型,而不能产生对象的类
    • 只能被子类继承,且抽象类的相关成员函数没有完整的体现,用来被子类重写.

    比如图形(Shape)类, 就是一个抽象的概念,因为我们无法计算这个“图形”的面积,所以它的成员函数area()是空的。

    而继承它的子类(矩形,圆形,三角形等)就可以去重写area()成员函数. 里面通过数学公式,计算出面积.

    参考图形(Shape)类,代码如下:

    class Shape
    {
    public:
           double area()
        {
             return 0;
        }
    };

    既然Shape是个抽象的类,那就根本没有该类的对象,我们该如何避免他人使用Shape类创建对象呢?

    答:

    在C++中,通过纯虚函数来避免 

    • 纯虚函数只需要声明函数名,不用实现函数内容.通过子类去实现
    • 当类中有纯虚函数时,该类就无法创建对象,因为纯虚函数里没有具体内容,所以这个类便成为了抽象类.
    • 如果子类没有实现存虚函数,则子类也会成为抽象类

     

    纯虚函数

    纯虚函数需要在声明函数名前面加上virtual,在最后面加个=0;

    比如:

    class Shape
    {
    public:
           virtual double area()=0;               //不需要实现函数内容
    };

     

    接口

    当类满足下面条件,则称为接口

    • 类中没有定义任何成员变量
    • 所有的成员函数都是公有的,并且都是纯虚函数
    • 接口是一种特殊的抽象类

    举个例子

    比如我们的蓝牙,可以打开,关闭,收发数据

    网卡也一样,可以打开,关闭,收发数据.

    类似的还有串口等等

    这些类都拥有同样的行为,只是内容不同,所以它们的父类Channel只需要构造纯虚函数,所以便被称为接口,该父类代码如下:

    class Channel{
    public
           virtual bool open()=0;
           virtual bool close()=0;
           virtual bool send(char* buf,int len)=0;
           virtual bool recv(char* buf,int len)=0;
    };

    多重继承

    • 一个类可以继承于多个父类
    • 子类拥有所有父类的成员变量和函数
    • 子类对象可以当做任意父类对象使用

    例如:

    class Derived : public BaseA,
                    public BaseB,
                    public BaseC
    {  
           //... ...
    }

    多重继承的问题1

    多个不同的父类指针指向同一个多重继承的子类时,可能拥有不同地址

    比如:

    #include <iostream> 
    
    using namespace std;
    
    class BaseA
    {
        int ma;
    public:
        BaseA(int a)
       {
            ma = a;
       }
        int getA()
       {
            return ma;
       }
    };
    
    class BaseB
    {
        int mb;
    public:
        BaseB(int b)
       {
            mb = b;
       }
        int getB()
       {
            return mb;
       }
    };
    
    class Derived : public BaseA, public BaseB
    {
        int mc;
    public:
         Derived(int a, int b, int c) : BaseA(a), BaseB(b)
        {
            mc = c;
        }
    };
    
    int main()
    { 
        Derived d(1, 2, 3);
        BaseA* pa = &d;
        BaseB* pb = &d;
    
        if((void *)pa==(void *)pb)
       {
           cout<<"true"<<endl;
       }
        else
       {
    cout
    <<"false"<<endl; } cout << "&d= " << &d << endl; cout << "pa= " << pa << endl; cout << "pb= " << pb << endl; }

    运行打印:

    false
    &d= 0x28fefc
    pa= 0x28fefc
    pb= 0x28ff00

    为什么,pa指针pb指针都指向d对象,它们的地址却有所不同?

    这是因为Derived d对象地址里依次存了两个不同的父类成员变量值,如下图所示:

     

    从上图看到,其实pa和pb还是位于d对象地址里,只是指向的位置不同而已.所以在多重继承里,最好不要使用等号直接判断两个指针对象是否相等.

     

    多重继承的问题2

    多重继承可能产生冗余的成员

    比如:

    老师teacher类,学生student类都继承于people类

    有些老师,为了工作还要考博士学位,既是老师又是学生,所以同时继承于老师teacher类,学生student类,则该类的成员便会拥有两个people类成员,从而产生冗余

     

    在工程中,如何正确使用多重继承

    • 只继承一个父类多个接口
    • 由于接口只有存虚函数,从而避免了冗余的成员
    • 在父类中提供equal()成员函数,
    • 通过equal()成员函数来判断指针是否指向当前对象,使用dynamic_cast强制转换 

    例如:

    #include <iostream>
    using namespace std; class Base { protected: int mi; public: Base(int i) { mi = i; } int getI() { return mi; } bool equal(Base* obj) { return (this == obj); } }; class Interface1 { public: virtual void add(int i) = 0; virtual void minus(int i) = 0; }; class Interface2 { public: virtual void multiply(int i) = 0; virtual void divide(int i) = 0; }; class Derived : public Base, public Interface1, public Interface2 { public: Derived(int i) : Base(i) { } void add(int i) { mi += i; } void minus(int i) { mi -= i; } void multiply(int i) { mi *= i; } void divide(int i) { if( i != 0 ) { mi /= i; } } }; int main() { Derived d(100); Derived* p = &d; Interface1* pInt1 = &d; Interface2* pInt2 = &d; cout << "p->getI() = " << p->getI() << endl; // 100 pInt1->add(10); pInt2->divide(11); pInt1->minus(5); pInt2->multiply(8); cout << "p->getI() = " << p->getI() << endl; // 40 cout << "pInt1 == p : " << p->equal(dynamic_cast<Base*>(pInt1)) << endl;     cout << "pInt2 == p : " << p->equal(dynamic_cast<Base*>(pInt2)) << endl; cout << "&d == p : " << p->equal(dynamic_cast<Base*>(&d)) << endl; return 0; }

    运行打印:

    p->getI() = 100
    p->getI() = 40
    pInt1 == p : 1
    pInt2 == p : 1
    &d== p : 1

    可以发现,使用dynamic_cast转换,判断出来的地址就是相等的.

    p->equal(dynamic_cast<Base*>(pInt1))为例,我们编译时,编译器就会去检查pInt1所在的地址,然后找到是d对象,通过d对象找到Base父类,从而去修正pInt1指针的地址.

  • 相关阅读:
    Python笔记 —— 使用open打开和操作txt文件
    Python笔记 —— 文件路径
    Python笔记 —— 四种容器(列表,元组,字典,集合)
    C++笔记 —— 异常
    C++笔记 —— map常用方法
    C++笔记 —— 在模板类中重载操作符
    C++笔记 —— vector常用方法
    C++笔记 —— 强制类型转换
    TCP连接三次握手四次挥手
    HashMap中自定义对象key,实现hashcode与equal
  • 原文地址:https://www.cnblogs.com/lifexy/p/8743963.html
Copyright © 2011-2022 走看看