C++抽象类小览

简介:

一、文章来由

virtual 方法和 virtual 类可以说是c++语言的一大特性,甚至有人说是c++语言的精髓,其实这么说也是有一定道理的,因为运行时多态在c++中体现淋漓尽致,而 virtual 就是为多态服务的。这也是一个一定要搞懂的c++问题,所以有了这篇文章。同时,我觉得这类底层问题不可能一文以蔽之,而且我也相信真正想搞懂这个问题的读者,不会只读我这一篇文章,所以只是小览,同时欢迎讨论和指正。

二、引入原因

其实,引入纯虚函数的原因我在我另一篇文章虚函数与多态小览就有写,不过重要的话说三遍,还只是两遍呢,哈哈~~知其然也需要知其所以然

2.1 定义

纯虚函数:纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加“=0”。

抽象类:含有一个或以上纯虚函数的类,叫做抽象类,有的地方还有叫纯虚类。我的理解是关于定义,还是选择一个一定正确的说法,也就是选择抽象类来叫更好。

但是百度百科关于纯虚类的定义却是另一种说法:所有函数都是纯虚函数的类是纯虚类。纯虚类可以有成员变量。纯虚类不能实例化。

还有另外一个概念叫“虚基类”,但是这是c++多重继承的概念,之后再讨论。

如: virtual void funtion()=0

2.2 引入原因

1、为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。

2、在很多情况下,基类本身生成对象是不合情理的。

例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数(方法:virtual ReturnType Function()= 0;),则编译器要求在派生类中必须予以重写以实现多态性。同时含有纯虚拟函数的类称为抽象类,它不能生成对象。这样就很好地解决了上述两个问题。

这样就清楚有抽象类的原因了吧~~

三、抽象类的特性

3.1 直接上特性

罗列的不一定有顺序,但是尽量罗列到,欢迎补充

1、首先说明:抽象类是区别于虚类的。虚类可以实例化,但是抽象类不能被实例化,也就永远没有对象。。。。。。T.T(永远没有对象,好惨!!)

2、如果子类没有实现纯虚函数,相当子类也有纯虚函数,所以子类也是抽象类。

3、其他类的定义与使用方式都与一般类差不多,大致有如下地方:

1)抽象类可以有成员变量 (可以)
2)抽象类可以有普通的成员函数 (可以)
3)抽象类可以有其他虚函数 (可以)
4)抽象类可不可以有带有参数的构造函数?(可以)
5)可不可以在抽象类的派生类的构造函数中显式调用抽象类的带参数构造函数 (可以)

3.2、一篇文章对抽象类的描述

一篇文章中这样写道:

1、抽象类是一种特殊的类,它是为了抽象和设计的目的而建立的,它处于继承层次结构的较上层(而不是绝对的上层,也有可能是中层,甚至底层)。

2、在实际中为了强调一个类是抽象类,可将该类的构造函数(设置为protected) 说明为保护的访问控制权限。

文章还写道:

3、抽象类的主要作用是将有关的组织在一个继承层次结构中,由它来为它们提供一个公共的根 (其实不一定是根),相关的子类是从这个根派生出来的。

4、抽象类刻画了一组子类的操作接口的通用语义,这些语义也传给子类。一般而言,抽象类只描述这组子类共同的操作接口,而完整的实现留给子类。

5、抽象类只能作为基类来使用(大多数情况是其他类的基类,但是抽象类本身也有可能是子类)。

6、可以定义指向抽象类的指针和引用,此指针可以指向它的派生类,进而实现多态性。

四、抽象类例子

作为接口使用的例子:

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;

class animal
{
protected:
    animal(){}
    virtual void  eat(const string name) =0;
};


class dog:public animal
{
public:
    vector<string> m_food;

    void eat(const string name);
    void push_back(const string name);
};

void dog::eat(const string name)
{
    vector<string>::iterator iter=std::find(m_food.begin(),m_food.end(),name);
    if (m_food.end() !=iter)
    {
        cout<<"Dog eat "<<*iter<<endl;
    }
}

void dog::push_back(const string name)
{
    if (m_food.end() ==std::find(m_food.begin(),m_food.end(),name))
    {
        m_food.push_back(name);
    }
}


int main(void)
{
    dog d;
    d.push_back("bone");
    d.eat("bone");

    return 0;
}

另一个好例子,多态的实例:

#include<iostream>
using namespace std;

const double PI=3.14159;

class Shapes   //抽象类
{
protected:
    int x, y;
public:
    void setvalue(int d, int w=0){x=d;y=w;}
    virtual void disp()=0;//纯虚函数
};

class Square:public Shapes
{
public:
    void disp(){
        cout<<"矩形面积:"<<x*y<<endl;
    }
};

class Circle:public Shapes{
public:
    void disp(){
        cout<<"圆面积:"<<PI*x*x<<endl;
    }
};

int main()
{
    Shapes *ptr[2]; //定义对象指针数组
    Square s1;
    Circle c1;
    ptr[0] = &s1;
    ptr[0]->setvalue(10, 5);
    ptr[0]->disp();
    ptr[1] = &c1;
    ptr[1]->setvalue(10);
    ptr[1]->disp();
    return 0;

}

五、抽象类与虚函数表

有这么个问题:

我们知道C++中有虚函数的类会有一个对应的虚函数表,那么抽象类有虚表吗,如果有的话怎么调用纯虚函数?

直觉上来讲,应该是有的。可是既然是抽象类,说明其对象永远不会被创建,那么维护个虚表貌似也不是很必要了。

设计个程序来验证一下:

class VirtualBase
{
public:
    VirtualBase()
    {
        // call pure virtual function through a non virtual function in base class's constructor
        non_virtual_fun();
    }

    void non_virtual_fun()
    {
        virtual_fun();
    }

    virtual void virtual_fun() = 0;
};

class Derived: public VirtualBase
{
public:
    virtual void virtual_fun(){}
};

int main(int argc, const char* argv[])
{

    size_t size = sizeof(VirtualBase);

    Derived d;
}

分析:

(1)sizeof(VirtualBase)返回4,而不是代表空类的1,这说明其内部是有一个虚表指针,而有虚表指针,那么极有可能有一个虚表。

(2)通过上面的程序,我们在实例化Derived时可以调入VirtualBase的构造函数,此时可以在watch窗口中查看this指针,的确有一个虚表指针指向一个虚表。

这里写图片描述

在构造函数中通过一个非虚函数调用一个纯虚函数 - 虽然该纯虚函数在Derived中被实现,但此时还在基类的构造函数中,所以,纯虚函数被调用 - 出错

这里写图片描述

抽象类需要虚表,我能想到的一个原因是在多个派生类中override时,我们需要保证被改写的函数是在正确的偏移地址的,为了保证这个地址是正确的,事先准备一个模板还是比较重要的。

—END—


参考文献

[1] http://blog.csdn.net/goondrift/article/details/19705797
[2] http://blog.csdn.net/Slience_Perseverance/article/details/20536277
[3] http://www.cnblogs.com/baiyanhuang/archive/2011/03/07/1976445.html

相关文章
|
6月前
|
算法 C++ iOS开发
软件开发入门教程网 Search之C++ 接口(抽象类)
软件开发入门教程网 Search之C++ 接口(抽象类)
|
28天前
|
存储 算法 编译器
【C++入门到精通】C++入门 —— 多态(抽象类和虚函数的魅力)
多态是面向对象编程中的一个重要概念,指的是同一个消息被不同类型的对象接收时产生不同的行为。通俗来说,**就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态**。
43 0
|
1月前
|
算法 C++
c++接口(抽象类)
c++接口(抽象类)
15 0
|
9月前
|
C++
C++学习笔记_04抽象类、多态 2021-04-15
C++学习笔记_04抽象类、多态 2021-04-15
|
4月前
|
测试技术 C++
C++零基础教程(抽象类和接口)
C++零基础教程(抽象类和接口)
29 0
|
4月前
|
测试技术 C++
[C++/PTA] 抽象类Shape
[C++/PTA] 抽象类Shape
30 0
|
7月前
|
C++
C++的纯虚函数和抽象类
在C++中,可以将虚函数声明为纯虚函数,语法格式为: virtual 返回值类型 函数名 (函数参数) = 0; 纯虚函数没有函数体,只有函数声明,在虚函数声明的结尾加上=0,表明此函数为纯虚函数。 最后的=0并不表示函数返回值为0,它只起形式上的作用,告诉编译系统“这是纯虚函数”。 包含纯虚函数的类称为抽象类(Abstract Class)。之所以说它抽象,是因为它无法实例化,也就是无法创建对象。原因很明显,纯虚函数没有函数体,不是完整的函数,无法调用,也无法为其分配内存空间。抽象类通常是作为基类,让派生类去实现纯虚函数。派生类必须实现纯虚函数才能被实例化。纯虚函数使用举例:
41 0
|
9月前
|
C++
【C++】 --- 纯虚函数和抽象类
【C++】 --- 纯虚函数和抽象类
29 0
|
11月前
|
算法 C++
软件开发入门教程网 Search之C++ 接口(抽象类)
接口描述了类的行为和功能,而不需要完成类的特定实现。 C++ 接口是使用抽象类来实现的,抽象类与数据抽象互不混淆,数据抽象是一个把实现细节与相关的数据分离开的概念。
|
12月前
|
Java 编译器 Android开发
Android C++系列:C++最佳实践2抽象类
OP面向对象程序设计的核心是数据抽象,继承和动态绑定。前面的文章我们介绍了使用virtual的虚类实现动态绑定的多态,有时候我们在做抽象时,对于抽象的实体不想让被人实例化,虚类没有这个功能,我们Java里面我们有抽象类,有接口来抽象一个实体的行为,而不允许被实例化,C++有没有这样的功能呢?C++怎么实现抽象类呢?
83 0