C++纯虚类小览

一、文章来由

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

二、引入原因

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

2.1 定义

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

纯虚类:含有一个或以上纯虚函数的类,叫做纯虚类,又叫抽象类。

如: 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

版权声明:欢迎转载,注明出处就好!如果不喜欢请留言说明原因再踩哦,谢谢,我也可以知道原因,不断进步!!

时间: 2024-10-28 06:05:23

C++纯虚类小览的相关文章

纯虚类中的纯析构函数

1. 纯虚类特点 纯虚类不能实例化: class AES{ public: virtual ~AES()=0; }; AES::~AES() { } class BES:public AES{ public: virtual ~AES()=0; }; int main() { AES *p=new BES; delete p; return 0; } // 通过基类的指针释放派生类对象:(虚函数实现)

什么情况下应用纯虚类

前几天跟同事brainstorm,讨论一个关于纯虚类的使用问题,挺有意思.回来心中久久不能平静,写出来一吐为快. 不论在C++中还是C#中,纯虚类都是不能实例化的,这是因为纯虚类其实是一个对业务类型的一种高度抽象,本质上是不存在这种东西的,所以也就不能实例化它.对于C++中只要类中含有一个纯虚函数就是纯虚类,而C#中是abstract修饰的类就是纯虚类,即使类中没有虚方法也可以是纯虚类,在这里我觉得C#的纯虚类没有C++的严谨,如果纯虚类中没有纯虚方法的话,那有何意义. 明白了纯虚类的原理,那纯

C++虚函数virtual,纯虚函数pure virtual和Java抽象函数abstract,接口interface与抽象类abstract class的比较

由于C++和Java都是面向对象的编程语言,它们的多态性就分别靠虚函数和抽象函数来实现. C++的虚函数可以在子类中重写,调用是根据实际的对象来判别的,而不是通过指针类型(普通函数的调用是根据当前指针类型来判断的).纯虚函数是一种在父函数中只定义而不实现的一种函数,不能用来声明对象,也可以被称为抽象类.纯虚函数的实现也可以在类声明外进行定义.C++中的抽象类abstract class是指至少有一个纯虚函数的类,如果一个类全部由纯虚函数组成,不包括任何的实现,被称为纯虚类. Java中的普通函数

C++基础(纯虚函数与抽象类)

C++基础之纯虚函数与抽象类 引言 纯虚函数在C++编程中的地位很重要,其关联到了设计模式中"接口"的概念. 语法 纯虚函数的语法: 1.  将成员函数声明为virtual 2.  后面加上 = 0 3.  该函数没有函数体 1 class <类名> 2 { 3 virtual <类型><函数名>(<参数表>) = 0; 4 - 5 }; 例如: 1 class CmdHandler 2 { 3 virtual void OnComman

你所不知道的C++ 之 C++虚类模型

我们知道,一个C++类如果带有virtual关键字的函数,那么,它就是一个虚类.虚类都有虚函数表.这个虚函数表真的存在吗?能摸得着.看得见吗? 的确是可以的.下面我们就展示一下如何看到C++类的虚函数表.请注意,我使用的系统是Ubutu 10.04, g++ 4.6.3. 下面我们先定义一个简单的纯虚类和它的实现类: class BaseClassNoDeconstructor { public: virtual void func() = 0; }; class VirtualClassNoD

C++中的纯虚函数和虚函数的作用

1. 虚函数和纯虚函数可以定义在同一个类(class)中,含有纯虚函数的类被称为抽象类(abstract class),而只含有虚函数的类(class)不能被称为抽象类(abstract class). 2. 虚函数可以被直接使用,也可以被子类(sub class)重载以后以多态的形式调用,而纯虚函数必须在子类(sub class)中实现该函数才可以使用,因为纯虚函数在基类(base class)只有声明而没有定义. 3. 虚函数和纯虚函数都可以在子类(sub class)中被重载,以多态的形式

C++ 纯虚函数接口,标准 C 导出 DLL 函数的用法

CMakeLists.txt project(virtual) # 创建工程 virtual add_library(virtual SHARED virtual.cpp) # 创建动态连接库 libvirtual.dll add_executable(main main.cpp) # 创建运行程序 main.exe target_link_libraries(main virtual) # 让 main.exe 连接 libvirtual.dll virtual.h #pragma once

python 使用abc实现接口类/虚类(2.2)

python 使用abc实现接口类/虚类 具体类 class BaseA: def run(self): print('base A running') class ChildA(BaseA): def run(self): print('child a') # 具体类可以直接继承也可以实现多态 a = BaseA() a.run() a = ChildA() a.run() 虚类 class BaseB(): @abc.abstractmethod def run(self): return

虚函数与多态小览

一.文章来由 Bill又写文章来由了哇~~早就想好好搞清这个问题了,这是c++领域里面比较难搞定的一块知识点,而且最近在看设计模式,里面有涉及这块,之前学过的不用容易玩忘记,于是就干脆研究透一点,也好碰到.用到的时候不心慌~于是有了这篇文章. 二.从编译时和运行时说起 2.1 编译时: 顾名思义就是正在编译的时候.就是编译器帮你把源代码翻译成机器能识别的代码.(当然只是一般意义上这么说,实际上可能只是翻译成某个中间状态的语言.比如Java只有JVM识别的字节码,C#中只有CLR能识别的MSIL)