一、文章来由
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
版权声明:欢迎转载,注明出处就好!如果不喜欢请留言说明原因再踩哦,谢谢,我也可以知道原因,不断进步!!