分类: C/C++
2010-04-12 22:38:01
2. 虚函数可以被直接使用,也可以被子类(sub class)重载以后以多态的形式调用,而纯虚函数必须在子类(sub class)中实现该函数才可以使用,因为纯虚函数在基类(base class)只有声明而没有定义,如果定义会报错。虚函数必须在父类中也定义,不能只声明。
note: 基类的纯虚函数也可以实现,但必须是在类外定义,类中声明。
http://www.cnblogs.com/chio/archive/2007/09/10/888260.html
~bs/bs_faq2.html#pure-virtual
3. 虚函数和纯虚函数都可以在子类(sub class)中被重载,以多态的形式被调用。
4. 虚函数和纯虚函数通常存在于抽象基类(abstract base class -ABC)之中,被继承的子类重载,目的是提供一个统一的接口。
5. 虚函数的定义形式:virtual {method body}
纯虚函数的定义形式:virtual { } = 0;
virtual 不能少。
在虚函数和纯虚函数的定义中不能有static标识符,原因很简单,被static修饰的函数在编译时候要求前期bind,然而虚函数却是动态绑定
(run-time bind),而且被两者修饰的函数生命周期(life recycle)也不一样。
6.
如果一个类中含有纯虚函数,那么任何试图对该类进行实例化的语句都将导致错误的产生,因为抽象基类(ABC)是不能被直接调用的。必须被子类继承重载以后,根据要求调用其子类的方法。
------
虚函数为了重载和多态的需要,在基类中是有定义的,即便定义是空,所以子类中可以重写也可以不写基类中的函数!纯虚函数在基类中是没有定义的,必须在子类中加以实现,很像java 中的接口函数!
-----
父类Animal(虚函数):
class Animal{子类Cat:
class Cat: public Animal{
public:
};
主函数使用:
Animal One;三次输出结果为:
Eat Something说明用动态分配的Cat是实际的Cat,即使指针为Animal *Three。那么为什么要引入纯虚函数呢?
总结一下(从网上查看到的):
一:
类里声 明为虚函数的话,这个函数是实现的,哪怕是空实现,它的作用就是为了能让这个函数在它的子类里面可以被重载,这样的话,这样编译器就可以使用后期绑定来达 到多态了
纯虚函 数只是一个接口,是个函数的声明而已,它要留到子类里去实现。
class A{
protected:
}
观点 二:
虚函数在子类里面也可以不重载的;但纯虚必须在子类去实现,这就像Java的接口一样。通常 我们把很多函数加上virtual,是一个好的习惯,虽然牺牲了一些性能,但是增加了面向对象的多态性,因为你很难预料到父类里面的这个函数不在子类里面 不去修改它的实现
观点 三:
虚函数 的类用于“实作继承”,继承接口的同时也继承了父类的实现。当然我们也可以完成自己的实现。纯虚函数的类用于“介面继承”,主要用于通信协议方面。关注的 是接口的统一性,实现由子类完成。一般来说,介面类中只有纯虚函数的。
观点 四:
带纯虚函数的类叫虚基类,这种基类不能直接生成对象, 而只有被继承,并重写其虚函数后,才能使用。这样的类也叫抽象类。
虚函数 是为了继承接口和默认行为
纯虚函 数只是继承接口,行为必须重新定义
------
class awov {
public:
virtual ~awov() = 0; // 声明一个纯虚析构函数
};
这个类有一个纯虚函数,所以它是抽象的,而且它有一个虚析构函数,所以不会产生析构函数问题。但这里还有一件事:必须提供纯虚析构函数的定义:
awov::~awov() {} // 纯虚析构函数的定义
这个定义是必需的,因为虚析构函数工作的方式是:最底层的派生类的析构函数最先被调用,然后各个基类的析构函数被调用。这就是说,即使是抽象类,编 译器也要产生对~awov的调用,所以要保证为它提供函数体。如果不这么做,链接器就会检测出来,最后还是得回去把它添上。
----
http://blog.csdn.net/lyflower/archive/2009/05/07/4157987.aspx
如果在一个类中声明了纯虚函数,而在其派生类中没有对该函数定义,则该函数在派生类中仍为纯虚函数。
【6】一个成员函数被声明为虚函数后,在同一类族中的类就不能再定义一个非virtual的但与该虚函数具有相同参数(个数与类型)和函数返回值类型的同
名函数。
【7】静态成员函数不能是虚函数,因为静态成员函数不受限于某个对象。
【8】inline函数不能是虚函数,因为
inline函数是不能在运行中动态确定其位置的。即使虚函数在类的内部定义,编译时,仍将其视为非inline的。
【5】使用虚函数,系统要
有一定的空间开销。当一个类带有虚函数时,编译器会为该类构造一个虚函数表(virtual function
tanle,vtable),它是一个指针数组,存放每个虚函数的入口地址。
5 纯抽象类
从C++的 角度来看,一个抽象类和一个接口之间没有任何区别。有时,我们习惯使用“纯抽象类”这个词来表示某个类仅仅只含有纯虚函数(不包含任何数据成员),它是抽 象类的最常见的形式。
使用纯抽象类有什么好处?最明显的例子就是“多接口、单实现”,这是一种很常见的情况。
在C++中加入了 纯虚函数的概念,一个纯虚函数必须被其派生类重写。借助此概念,你可以在一个C++类中通过将其成员函数 声明为纯虚函数的方法表明该类是一个纯接口类。从那以后,我就一直强调在C++中,有一种主要的使用类的方法就是让该类不包含任何状态, 而仅仅作为一个接口。
6 抽象类
将不用来定义对象而只作为一种基本类型用作继承的类,称为抽象类(abstract
class),由于它常用作基类,通常称为抽象基类。凡是包含纯虚函数的类都是抽象类。
如果在派生类中没有对所有的纯虚函数进行定义,则此派生类仍然是抽象类,不能用来定义对象。
可以定义指向抽象类数据的指针变量。当派生类成为具体
类后,就可以用这个指针指向派生类对象,然后通过该指针调用虚函数。
带有纯虚函数的类称为抽象类。抽象类是一种特殊的类,它是为了抽象和设计的目的而建立的,它处于继承层次结构的较上层。抽象类是不能定义对象的,在实际中 为了强调一个类是抽象类,可将该类的构造函数说明为保护的访问控制权限。抽象类的主要作用是将有关的组织在一个继承层次结构中,由它来为它们提供一个公共 的根,相关的子类是从这个根派生出来的。抽象类刻画了一组子类的操作接口的通用语义,这些语义也传给子类。一般而言,抽象类只描述这组子类共同的操作接 口,而完整的实现留给子类。抽象类只能作为基类来使用,其纯虚函数的实现由派生类给出。如果派生类没有重新定义纯虚函数,而派生类只是继承基类的纯虚函 数,则这个派生类仍然还是一个抽象类。如果派生类中给出了基类纯虚函数的实现,则该派生类就不再是抽象类了,它是一个可以建立对象的具体类了。
7 java 中的接口
不需要声明接口为抽象或虚拟(本来就是)
接口不允许有构造函数(纯抽象了,根本不需要构造)
接口不允许有析构函数(本来无构造,何需有析构)
接口的所有成员都是抽象的(纯抽象类嘛)
接口只可以从接口继承(因为只有接口可以保证使纯虚的,如果从抽象类继承,不能保证抽象类中可能存在非抽象的成员)
接口成员不允许有任何修饰(默认就是public的,也只有是public的)
一个类或结构可以实现多个接口
note: 纯虚析构
当用纯虚析构来构造抽象类的时候,必须在类外定义此纯虚析构函数,因为析构的时候会先调用派生类的析构,再调用基类的析构,如果不定义,则链接器会找不到函数定义(类中只有声明,不能定义),链接器会报错。
当然,也可以不用纯虚析构来构造抽象类,用其他函数,这样编译器会自己合成一个纯虚析构函数,则不会出错。
-------------------------------------------
1、c++实现多态的方法
其实很多人都知道,虚函数在c++中的实现机制就是用虚表和虚指针,但是具体是怎样的呢?从more
effecive c++其中一篇文章里面可以知道:是每个类用了一个虚表,每个类的对象用了一个虚指针。具体的用法如下:
class A
{
public:
virtual void f();
virtual void g();
private:
int a
};
class
B : public A
{
public:
void g();
private:
int b;
};
//A,B
的实现省略
因为A有virtual void f(),和g(),所以编译器为A类准备了一个虚表vtableA,内容如下:
A::f 的地址 |
A::g 的地址 |
A::f 的地址 |
B::g 的地址 |
vptr : 指向B的虚表vtableB |
int
a: 继承A的成员 |
int b: B成员 |