c++:继承(3)

1.纯虚函数:

纯虚函数是一种特殊的虚函数,在基类中无法对虚函数给出有意义的实现,所以就叫做纯虚函数。

纯虚函数的作用:它的实现留给基类的派生类去做。

格式:

class <类名>
{
  virtual<类型><函数名>(参数列表) = 0;
  ....
};

纯虚函数可以让类先具有一个操作名称,而没有操作内容而后在基类的派生类中给出定义

引入原因:

  • 为了方便使用多态特性,我们常常需要在基类中定义虚函数
  • 很多情况下,基类本身生成对象是不合情理的。动物作为基类可以派生出企鹅,老鹰,狗等动物,但是动物本身生成对象明显不合常理

函数定义为纯虚函数,就可以解决上述问题。


2.抽象类:

凡是含有纯虚函数的类就叫抽象类,不能定义对象,只能为派生类用,可以定义指针与引用。

注意:

  • 在派生类中要安全实现基类中的纯虚函数,要不然派生类也要变成抽象类
  • 不能实例化对象
  • 在派生类中实现纯虚函数后,定义抽象类对象的指针,并指向或引用子类对象

对于纯虚函数来说:

1.在定义的时候是不能定义实现部分的

2.在没有重新定义这种纯虚函数之前,是不能调用该纯虚函数的

抽象类的唯一用途是为派生类提供基类,纯虚函数的作用是作为派生类中的成员函数的基础,并实现动态多态性

代码实现:

#include <iostream>
using namespace std;

//定义抽象基类
class Base
{
public:
    //定义虚函数等于零,就是为纯虚函数,必须在派生类进行,可以看成一个派生类的接口,调用此接口时,调用相应派生类的方法
    virtual void Fun() = 0;
};

class Derive : public Base
{
public:
    /* 若不实现此函数,编译将会出错 */
        void Fun()
       {
           cout << "Derive::Fun" << endl;
       }
        Derive()
       {
           cout << "Derive()" << endl;
       }
        ~Derive()
       {
           cout << "~Derive()" << endl;
       }
};

class Derive2: public Base
{
public:

    /* 若不实现此函数,编译将会出错 */
    void Fun()
    {
        cout << "Derive2::Fun" << endl;
    }
    Derive2()
    {
        cout << "Derive2()" << endl;
    }
    ~Derive2()
    {
        cout << "~Derive2()" << endl;
    }
};
 
void Show(Base& Base) {
    Base.Fun();
}

int main()
{
    //Base base;error,抽象类不可以实例化对象
    Derive derive1;
    Derive derive2;
    
    Base* base1 = &derive1;
    Base* base2 = &derive2;
    
    base1->Fun();
    base2->Fun();
    
    return 0;
}

如果给抽象基类定义对象时,编译会报错:

结果如下:


3.单继承与多继承:

单继承一个派生类只继承一个基类

多继承一个类可以同时继承多个不同基类的行为和特征功能

多继承的格式:

//基类
class <类名>
{

};
//派生类
class <类名> :<继承方式> <基类名1>,<继承方式> <基类2>...
{
···
};

代码:

class Base1
{
public:
	Base1() { cout << "Base1()" << endl; }
	~Base1() { cout << "~Base1()" << endl; }
};
class Base2
{
public:
	Base2() { cout << "Base2()" << endl; }
	~Base2() { cout << "~Base2()" << endl; }
};

/*
**  : 之后称为类派生表,表的顺序决定基类构造函数
** 调用的顺序,析构函数的调用顺序正好相反
*/
class Derive : public Base2, public Base1
{};
int main()
{
	Derive derive;
	return 0;
}

这段代码先声明Base2,在声明Base1而且运行没错。结果是Base2先构造,Base1在构造。

多继承会引发的问题:

  • 二义性问题
  • 菱形继承导致派生类持有间接基类的多份拷贝

4.二义性:

在上面的基类Base1和Base2中若存在相同的方法或成员变量,那么在派生类Derive中或使用Derive的对象时,若使用这个方法或成员变量时,那么编译器不知道需要调用Base1中的方法还是Base2中的方法或成员变量。

当然我们可以给方法添加作用域来解决这个问题,我们也可以通过在派生类Derive中重新定义这个方法来覆盖基类中的同名方法,从而使编译器能够正常工作

例子:

#include <iostream>
using namespace std;


class Base1
{
public:
    Base1(int a = 10 ):ma(a)
    {}
    ~Base1()
    {}
    void show()
    {
        cout << ma <<endl;
    }
private:
    int ma;
};

class Base2
{
public:
    Base2(int b = 10 ):mb(b)
    {}
    ~Base2()
    {}
    void show()
    {
        cout << mb <<endl;
    }
private:
    int mb;
};

//多继承
class Derive : public Base2,public Base1
{};
int main()
{
    Derive derive;
    derive.show();
    
    return 0;
}


编译时发现存在二义性:

解决方法:就是为show函数加上作用域,即可正常运行:(2.在Derive派生类中重写Show方法,将隐藏基类方法。


5.菱形继承:

  • Derive1继承了Base类,它的成员有ma、mb、mc
  • Derive2继承了Base类,它的成员有ma、mb、md
  • Derive3继承了Derive1类和Derive2类,它的成员有ma、mb、mc、ma、mb、md

示意图:

Derive3由于多重继承,拿到了它的间接基类Base的两份数据拷贝

代码:

#include <iostream>
using namespace std;


class Base
{};

class Derive1 : public Base
{};

class Derive2 : public Base
{};

class Derive3 : public Derive1,public Derive2
{};

int main()
{
    Derive3 derive3;
    Base* base = &derive3;
    
    return 0;
}

该代码在运行时,会出现错误 将派生类对象的地址赋给基类指针出现二义性

这种赋值将把基类指针设置为派生类对象中的基类指针的地址。但是现在Derive3中包含两个地址可选择,所以应该使用类型转换来指定对象

    Derive3 derive3;
    Base* base = (Derive1*) &derive3;
    Base* base1 = (Derive2*) &derive3;

这将使得使用基类指针来引用不同的对象(多态性)复杂化。

为了解决上述问题C++引入了一种新技术——虚基类


 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值