C++语言- 抽象类(存虚函数)、接口、多重继承
小标 2018-07-25 来源 : 阅读 1257 评论 0

摘要:本文主要向大家介绍了C++语言- 抽象类(存虚函数)、接口、多重继承,通过具体的内容向大家展示,希望对大家学习C++语言有所帮助。

本文主要向大家介绍了C++语言- 抽象类(存虚函数)、接口、多重继承,通过具体的内容向大家展示,希望对大家学习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指针的地址.

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标编程语言C/C+频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程