C++中有哪些函数不能声明为虚函数

来源:互联网 发布:淘宝店提高销量 编辑:程序博客网 时间:2024/04/28 15:31

转自:http://blog.csdn.net/ta893115871/article/details/8194836

一、首先回顾下什么是虚函数及其作用,以便更好理解什么函数不能声明或定义为虚函数:

1. 定义:

虚函数必须是基类的非静态成员函数,其访问权限可以是protected或public,在基类的类定义中定义虚函数的一般形式:

  virtual 函数返回值类型 虚函数名(形参表)  { 函数体 }

2. 作用:

虚函数的作用是实现动态联编,也就是在程序的运行阶段动态地选择合适的成员函数,在定义了虚函数后,可以在基类的派生类中对虚函数重新定义,在派生类中重新定义的函数应与虚函数具有相同的形参个数和形参类型,以实现统一的接口,不同定义过程。如果在派生类中没有对虚函数重新定义,则它继承其基类的虚函数。

当程序发现虚函数名前的关键字virtual后,会自动将其作为动态联编处理,即在程序运行时动态地选择合适的成员函数。

3. 使用方法:

动态联编规定,只能通过指向基类的指针或基类对象的引用来调用虚函数,其格式:

  指向基类的指针变量名->虚函数名(实参表)

      基类对象的引用名. 虚函数名(实参表)

4. 其它说明:  

虚函数是C++多态的一种表现:

例如:子类继承了父类的一个函数(方法),而我们把父类的指针指向子类,则必须把父类的该函数(方法)设为virtual(虚函数)。  使用虚函数,我们可以灵活的进行动态绑定,当然是以一定的开销为代价。 如果父类的函数(方法)根本没有必要或者无法实现,完全要依赖子类去实现的话,可以把此函数(方法)设为virtual 函数名=0 我们把这样的函数(方法)称为纯虚函数。  如果一个类包含了纯虚函数,称此类为抽象类 。

常见的不能声明为虚函数的有:普通函数(非成员函数);静态成员函数;内联成员函数;构造函数;友元函数。

设置虚函数须注意: 
1:只有类的成员函数才能说明为虚函数; 
2:静态成员函数不能是虚函数; 
3:内联函数不能为虚函数; 
4:构造函数不能是虚函数; 
5:析构函数可以是虚函数,而且通常声明为虚函数。

类里面“定义”的成员函数是内联的,但是仍然可以成为虚函数,那么是不是可以说“内联函数不能成为虚函数”这句话有问题呢,是不是应该改成“显式定义的内联函数不能成为虚函数”。比如下面这个示例程序: 
#include   <iostream> 
using   namespace   std; 
class   Base{ 
        public: 
        virtual     void   f1(){cout < < "Father " < <endl;} 
        }; 
class   Drived1:public   Base{ 
        public: 
            void   f1(){cout < < "Son1 " < <endl;} 
        }; 
class   Drived2:public   Base{ 
        public: 
            void   f1(){cout < < "Son2 " < <endl;} 
        }; 
void   myPrint(Base*   pBs){ 
        pBs-> f1(); 
        }       
int   main() 

    Base   father; 
    Drived1   son1; 
    Drived2   son2; 
    myPrint(&father); 
    myPrint(&son1); 
    myPrint(&son2); 
    
    system( "PAUSE ");
    return   0; 

输出: 
Father 
Son1 
Son2 
你可以发现,虽然f1在基类中定义的,按理说应该是内联函数,但是它仍然可以成为虚函数。
类中定义的成员函数(函数体在类中)能成为虚函数,大部分编译器能够将虽然声明为inline但实际上不能inline的函数自动改为不inline的。至于编译器会不会将inline   and   virtual的函数照模照样的实现,与编译器及优化方式有关。
要想成为虚函数,必须能够被取到地址.内联函数不能被取到地址所以不能成为虚函数. 
你写inline   virtual   void   f(),不能保证函数f()一定是内联的,只能保证f()是虚函数(从而保证此函数一定不是内联函数) 
对于问题: 
到底内联函数能不能成为虚函数? 
答案是不能.问题是你不能够确定一个函数到底是不是inline的.inlien关键字只是对编译器的一个建议:"如果有可能,请把此函数搞成inline的"


1.为什么C++不支持普通函数为虚函数?

普通函数(非成员函数)只能被overload,不能被override,声明为虚函数也没有什么意思,因此编译器会在编译时邦定函数。

多态的运行期行为体现在虚函数上,虚函数通过继承方式来体现出多态作用,顶层

函数不属于成员函数,是不能被继承的

2.为什么C++不支持构造函数为虚函数?

这个原因很简单,主要是从语义上考虑,所以不支持。因为构造函数本来就是为了明确初始化对象成员才产生的,然而virtual function主要是为了再不完全了解细节的情况下也能正确处理对象。另外,virtual函数是在不同类型的对象产生不同的动作,现在对象还没有产生,如何使用virtual函数来完成你想完成的动作。(这不就是典型的悖论)

(1)构造函数不能被继承,因而不能声明为virtual函数

       (2)构造函数一般是用来初始化对象,只有在一个对象生成之后,才能发挥多态作用,如果将构造函数声明为virtual函数,则表现为在对象还没有生成的情况下就使用了多态机制,因而是行不通的。

3.为什么C++不支持内联成员函数为虚函数?

其实很简单,那内联函数就是为了在代码中直接展开,减少函数调用花费的代价,虚函数是为了在继承后对象能够准确的执行自己的动作,这是不可能统一的。(再说了,inline函数在编译时被展开,虚函数在运行时才能动态的邦定函数)

inline函数和virtual函数有着本质的区别,inline函数是在程序被编译时就展开,在函数调用处用整个函数体去替换,而virtual函数是在运行期才能够确定如何去调用的,因而inline函数体现的是一种编译期机制,virtual函数体现的是一种运行期机制。此外,一切virtual函数都不可能是inline函数。

4.为什么C++不支持静态成员函数为虚函数?

这也很简单,静态成员函数对于每个类来说只有一份代码,所有的对象都共享这一份代码,他也没有要动态邦定的必要性。不能被继承,只属于该类。

5.为什么C++不支持友元函数为虚函数?

因为C++不支持友元函数的继承,对于没有继承特性的函数没有虚函数的说法。友元函数不属于类的成员函数,不能被继承。

eg:


[cpp] view plaincopy
  1. /* 
  2.  * main.cpp 
  3.  * 
  4.  *  Created on: 2012-11-17 
  5.  *      Author: china 
  6.  */  
  7. #include <iostream>  
  8. using namespace std;  
  9. class B {  
  10. public:  
  11.     B() {  
  12.         cout << "基类构造" << endl;  
  13.     }  
  14.     /*在类的继承中,如果有基类指针指向派生类,那么用基类指针delete时,如果不定义成虚函数,派生类中派生的那部分无法析构。 
  15.      * 你可以吧virtual去掉试一下 
  16.      * 因此在类的继承体系中,基类的析构函数不声明为虚函数容易造成内存泄漏。所以如果你设计一定类可能是基类的话,必须要声明其为虚函数。 
  17.      * */  
  18.     virtual ~B() {  
  19.         cout << "基类析构" << endl;  
  20.     }  
  21.     virtual void func() {  
  22.         cout << "基类的func()" << endl;  
  23.     }  
  24.   
  25. private:  
  26. };  
  27. class D :public B{  
  28. public:  
  29.     D() {  
  30.         cout << "派生类构造" << endl;  
  31.     }  
  32.     ~D() {  
  33.         cout << "派生类析构" << endl;  
  34.     }  
  35.     void func() {  
  36.         cout << "派生类的func()" << endl;  
  37.     }  
  38.   
  39. private:  
  40. };  
  41. int main(int argc, char **argv) {  
  42.   
  43.     D d; //调用构造先有对象  
  44.     B*p = &d;  
  45.     p->func(); //再体现多态机制  
  46.     p = new D(); //再调用构造  
  47.     p->func(); //再体现多态机制  
  48.     delete p;  
  49.     return 0;  

0 0
原创粉丝点击