2,虚函数

C++的学习总是一个漫长的过程。前一篇是关于输入、输出流同步的问题,今天又谈到虚函数,给人一种无序的杂乱感。先不管了,记录下来,好记性不如烂笔头。

一、什么是虚函数



  在某基类中声明为 virtual 并在一个或多个派生类中被重新定 义的成员函数,用法格式为:virtual 函数返回类型 函数名(参数表) {函数体};实现多态性,通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数。

  这是百度百科的定义,简单明了,我们下面通过实际例子来感受下。

  1,程序a.cpp,我们定义类A,并派生出B,B类重载了A中的foo().主函数中,A *p = new B; p->foo();观察程序输出,判断调用了A还是B的foo().

 1 #include <iostream>
 2 #include <typeinfo>
 3 using namespace std;
 4
 5 class A {
 6 public:
 7     A(){};
 8     ~A(){};
 9     void foo() {
10         cout << "This is an A." << endl;
11     }
12 };
13
14 class B : public A
15 {
16 public:
17     B(){};
18     ~B(){};
19     void foo() {
20         cout << "This is a B." << endl;
21     }
22
23 };
24
25 int main() {
26     A *p = new B;
27
28     cout << " p has type of " << typeid(p).name()<<endl;
29     cout << " *p has type of " << typeid(*p).name()<<endl;
30     p->foo();
31     delete p;
32     return 0;
33 }

  

  从输出我们可以看到,p->foo()调用的是A的foo().另外,我们还可以通过typeid(p).name()来查询p的类型。typeid方法定义在<typeinfo>中,可以查看源码,里面定义了 class type_info, 这里不在多讲。

  2,程序b.cpp。这里,我们更改a.cpp中A,BD的foo()为虚函数。再看看输出效果如何

  先提前注意下,当声明A中的foo()为虚函数时,其析构函数也要声明为虚函数,否则会产生一下warning: deleting object of polymorphic class type ‘A‘ which has non-virtual destructor might cause undefined behavior [-Wdelete-non-virtual-dtor]|

  编译器为GCC 6.1.0,此warning的深层机制没有深究,还请广大博友赐教了。好,看程序。

 1 #include <iostream>
 2 #include <typeinfo>
 3 using namespace std;
 4
 5 class A {
 6 public:
 7     A(){};
 8     virtual ~A(){};
 9     virtual void foo() {
10         cout << "This is an A." << endl;
11     }
12 };
13
14 class B : public A
15 {
16 public:
17     B(){};
18     ~B(){};
19     virtual void foo() {
20         cout << "This is a B." << endl;
21     }
22
23 };
24
25 int main() {
26     A *p = new B;
27
28     cout << " p has type of " << typeid(p).name()<<endl;
29     cout << " *p has type of " << typeid(*p).name()<<endl;
30     p->foo();
31     delete p;
32     return 0;
33 }

  注意看输出,

  

  我们看到,p还是A类型的指针,但是*p却是class B类型。然后p->foo()调用的是B的foo().这就是RTTI机制(Runing Type Information),它提供了运行时确定对象类型的方法。

  总结下,当基类指针指向一个子类对象,通过这个指针调用子类和基类同名成员函数的时候,基类声明为虚函数(子类同名函数不是虚函数也可以)就会调用子类的这个函数,不声明就会调用基类的。

  这里,引入一个问题,如果我们把析构函数写成如下形式:

virtual ~A(){
        cout << "~A() is inline function??" << endl;
    };

  请问,~A()是内联函数吗?因为我们都知道,类中定义的函数默认为inline函数,那这里的~A()呢??

  虚函数与内联函数的联系,见下文三、虚函数可不可以是内联函数。

二、虚函数的用处



  大家都知道league of legends吧,假如我们所有的英雄都是基于父类Legeng的,有个纯虚函数q_attack() = 0;然后它派生出的子类,如FrostArcher,ProdigalExplorer,LooseCannon都实现了自己独特的q攻击函数q_attack()。当他们三个开团时,他们不停的用自己的攻击方式去攻击敌人(好怪异,开团只用q)。这个时候,我们就可以定义一个父类指针数组legend,把这三个英雄的指针封装起来,然后循环的legend[i]->q_attack(), 三个legend就不停的攻击敌人了。

  示意代码如下:

 1 int main() {
 2     Legend *legend[3];
 3     legend[0] = new ;
 4     legend[1] = new;
 5     legeng[2] = new;
 6
 7     for (int i = 0; i < 3; ++i) {
 8         legend[i]->q_attack();
 9     }
10
11     return 0;
12 }

三、虚函数可不可以是内联函数



  内联函数是指用inline关键字修饰的函数。在类定义中的函数被默认为内联函数。

  内联函数从源代码层看,有函数的结构,而在编译后,却不具备函数的性质。内联函数不是在调用时发生控制转移,而是在编译时将函数体嵌入在每一个调用处。编译时,类似宏替换,使用函数体替换调用处的函数名。一般在代码中用inline修饰,但是能否形成内联函数,需要看编译器对该函数定义的具体处理。

  上述定义来自百度百科,感觉还是比较清晰的。

  1,程序c.cpp。我们在b.cpp的基础上,为foo()方法添加inline关键字,观察程序输出。

 1 #include <iostream>
 2 #include <typeinfo>
 3 using namespace std;
 4
 5 class A {
 6 public:
 7     A(){};
 8     virtual ~A(){
 9         cout << "~A() is inline function??" << endl;
10     };
11     inline virtual void foo() {
12         cout << "This is an A." << endl;
13     }
14 };
15
16 class B : public A
17 {
18 public:
19     B(){};
20     ~B(){};
21     inline void foo() {
22         cout << "This is a B." << endl;
23     }
24
25 };
26
27 int main() {
28     A *p = new B;
29
30     cout << " p has type of " << typeid(p).name()<<endl;
31     cout << " *p has type of " << typeid(*p).name()<<endl;
32     p->foo();
33     delete p;
34     return 0;
35 }

  输出如下,

  

  我们看到,与b.cpp相比,程序输出一模一样。说明,即使虚函数声明为inline,仍然实现动态绑定。

  所以,结论是虚函数不可能是内联函数。

四、简单谈谈const(与虚函数无联系,临时想到)



  virtual,inline都是修饰函数的,现在突然想到const,也总结下吧。如果我们在foo()后面添加const ,有什么意义呢?那就是把foo()修饰为const,foo()函数体内不能对成员数据作任何改动。如果声明类的一个const 实例,那么它只能调用const修饰的函数。

  程序d.cpp。我们在c.cpp的情况下,修改B的foo()为foo() const, 并添加非cosnt函数foo2(). 当 const B cb;我们观察下cb.foo(), 和 cb.foo2()哪个会执行?

 1 #include <iostream>
 2 #include <typeinfo>
 3 using namespace std;
 4
 5 class A {
 6 public:
 7     A(){};
 8     virtual ~A(){
 9         cout << "~A() is inline function??" << endl;
10     };
11     inline virtual void foo() {
12         cout << "This is an A." << endl;
13     }
14 };
15
16 class B : public A
17 {
18 public:
19     B(){};
20     ~B(){};
21     inline void foo() const{
22         cout << "This is a B." << endl;
23     }
24
25     void foo2() {
26         cout << "foo2() is not const." <<endl;
27     }
28
29 };
30
31 int main() {
32     A *p = new B;
33     const B cb;
34     cout << " p has type of " << typeid(p).name()<<endl;
35     cout << " *p has type of " << typeid(*p).name()<<endl;
36     p->foo();
37
38     cb.foo();
39     //cb.foo2();//error: passing ‘const B‘ as ‘this‘ argument discards qualifiers [-fpermissive]|
40     delete p;
41     return 0;
42 }

  经实验,cb.foo2()会出现error,见程序注释。

  总结:

  const对象只能调用const成员函数。   
      const对象的值不能被修改,在const成员函数中修改const对象数据成员的值是语法错误 。
      在const函数中调用非const成员函数是语法错误。

  最后,贴出调用cb.foo()的运行结果

好了,虚函数简单写在这。另外,关于纯虚函数以及type_info的内容还有很多,可以参见http://blog.csdn.net/hackbuteer1/article/details/7558868和http://www.jb51.net/article/55968.htm,写的比较详细。

时间: 2025-01-04 11:17:22

2,虚函数的相关文章

C++ 虚函数和虚继承浅析

本文针对C++里的虚函数,虚继承表现和原理进行一些简单分析,有希望对大家学习C++有所帮助.下面都是以VC2008编译器对这两种机制内部实现为例. 虚函数 以下是百度百科对于虚函数的解释: 定义:在某基类中声明为 virtual 并在一个或多个派生类中被重新定 义的成员函数[1] 语法:virtual 函数返回类型 函数名(参数表) { 函数体 } 用途:实现多态性,通过指向派生类的基类指针,访问派生类中同名覆盖成员函数 函数声明和定义和普通的类成员函数一样,只是在返回值之前加入了关键字"vir

单继承与多继承中的虚函数表和虚函数指针

首先,我们了解一下何为单继承,何为多继承?? 单继承:一个子类只有一个直接父类. 多继承:一个子类有两个或多个直接父类. 单继承中的虚函数表分析: 示例程序: #include <iostream> using namespace std; typedef void(*FUNC)(); class Base { public: virtual void func1() { cout << "Base::func1()" << endl; } virt

C++:纯虚函数与抽象类

5.4.3 纯虚函数和抽象类 纯虚函数是一个在基类中说明的虚函数,它在该基类中没有定义,但是要求在派生类中根据需要对它进行定义,或仍然说明为纯虚函数. 声明纯虚函数的一般格式是: virtual 函数类型 函数名(参数表)=0: 纯虚函数的作用是:在基类中为其派生类保留一个函数的名字,以便派生类根据需要对它进行重新定义.纯虚函数没有函数体,它最后面“=0 ”并不表示函数的返回值是0,它只是形式上的作用,告诉编译系统这是纯虚函数.纯虚函数不具有函数的功能,不能被调用. //应用举例 #includ

含有虚函数的类sizeof大小

#include <iostream> using namespace std; class Base1{ virtual void fun1(){} virtual void fun11(){} public: virtual ~Base1(); }; class Base2{ virtual void fun2(){} }; class DerivedFromOne: public Base2 { virtual void fun2(){} virtual void fun22(){} }

C++沉思录之二——虚函数使用的时机

虚函数使用的时机 为什么虚函数不总是适用? 1. 虚函数有事会带来很大的消耗: 2. 虚函数不总是提供所需的行为: 3. 当我们不考虑继承当前类时,不必使用虚函数. 必须使用虚函数的情况: 1. 当你想删除一个表面上指向基类对象,实际却是指向派生类对象的指针,就需要虚析构函数. C++沉思录之二--虚函数使用的时机,布布扣,bubuko.com

C++中virtual(虚函数)的用法

转载:http://blog.csdn.net/foreverhuylee/article/details/34107615 在面向对象的C++语言中,虚函数(virtual function)是一个非常重要的概念. 什么是虚函数: 虚函数是指一个类中你希望重载的成员函数 ,当你用一个  基类指针或引用   指向一个继承类对象的时候,调用一个虚函数时, 实际调用的是继承类的版本. --摘自MSDN                                                 

C++抽象类与纯虚函数实战举例

虚函数与纯虚函数请参考:http://blog.csdn.net/hackbuteer1/article/details/7558868 #include <iostream> using namespace std; class Animal { public:     virtual void eat()=0;     virtual void sleep()=0; }; class Cat:public Animal { public:     void eat()     {     

c++ 虚函数详解

下面是对C++的虚函数的理解. 一,定义 简单地说,那些被virtual关键字修饰的成员函数,就是虚函数.虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离:用形象的语言来解释就是实现以共同的方法,但因个体差异而采用不同的策略.下面来看一段简单的代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 classA { publi

多态&amp;虚函数

(1).对象类型: a.静态类型:对象声明时的类型,编译的时候确定 b.动态类型:对象的类型是运行时才能确定的 class A {}; class B:public A {}; int main() { B* b; A* a=b;//a的静态类型是A*,动态类型(运行时)类型是B* return 0; } (2).多态 a.静态多态:函数重载.泛性编程 int Add(int a,int b) { return a+b; } float Add(float a,float b) { return

[转]什么是C++虚函数、虚函数的作用和使用方法

我们知道,在同一类中是不能定义两个名字相同.参数个数和类型都相同的函数的,否则就是“重复定义”.但是在类的继承层次结构中,在不同的层次中可以出现名字相同.参数个数和类型都相同而功能不同的函数.例如在例12.1(具体代码请查看:C++多态性的一个典型例子)程序中,在Circle类中定义了 area函数,在Circle类的派生类Cylinder中也定义了一个area函数.这两个函数不仅名字相同,而且参数个数相同(均为0),但功能不同,函数体是不同的.前者的作用是求圆面积,后者的作用是求圆柱体的表面积