C++学习笔记——virtual学习
来源:互联网 发布:node.js addlistener 编辑:程序博客网 时间:2024/04/28 16:59
1、含义
virtual关键字只用于类定义内部,出了类定义范围无法使用,即只能用于限定成员函数
定义为virtual的函数是基类希望派生类重新定义的,否则不能声明为virtual函数。
C++中的类成员函数默认为非virtual函数。
2、virtual与动态绑定
通过动态绑定我们能够在编写程序时使用继承层次中的任意对象,无需关心对象的具体类型。使用这些类的程序无需关心函数是在基类还是在派生类中定义。
在C++中通过基类的引用(或指针)调用虚函数时候,发生动态绑定,即引用(或指针)既可以是指向基类对象也可以是指向派生类对象。用引用(或指针)调用的虚函数在运行时确定被调用函数的实际定义。
触发条件:
- 只有指定为虚函数的成员函数才能进行动态绑定;
- 必须通过基类类型的引用或指针进行函数调用;
3、virtual与派生类
派生类一般会重新定义所有基类的虚函数(继承的),如果没有重新定义虚函数则默认使用基类中版本。
派生类中对于虚函数的声明必须与基类中完全一样(但若返回类型是对基类的引用或指针的虚函数,在派生类中可以返回基类函数所返回类型的派生类应用或指针)。
一旦函数在基类中声明为虚函数,它就一直为虚函数,派生类可以保留virtual关键字。
4、virtual与构造、析构函数
C++明确指出:当派生类对象通过基类指针被删除,而该基类有non-virtual析构函数,其结果未定义——实际执行时候该派生类的派生成分未被销毁,只销毁了继承自基类的部分。这就发生了诡异的“局部销毁”,形成资源泄露。因此:任何有多态性或者含有virtual函数的基类都必须包含有一个virtual析构函数。
此外决不能在构造函数和析构函数中调用virtual函数,具体原因见参考文献【1】。
5、virtual与强制覆盖
某些情况下希望覆盖虚函数,强调使用虚函数的特定版本,这是可以使用作用域操作符:
base *p_base = &dreived;T d = p_base->base_class::print_m();
这段代码强调对于func()的调用确定为base中定义的版本,这就使该调用在编译是确定。
6、pure virtual
如果某个基类只是为了让其他类继承而不会有或者不应该有类对象,可以在类中定义纯虚函数(pure virtual function)。包含纯虚函数的类为抽象类,抽象类除了作为抽象基类进行派生,抽象类不能创建对象。
class base {public: virtual void print_m() = 0;}
上面定义的base为抽象类,func为纯虚函数。
7、示例
#include <iostream>#include <vector>#include <boost/foreach.hpp>using namespace std;class base{public: virtual ~base(){ cout<<__func__<<"\n"; } virtual void print_m(){ cout<<"IN Base: "<<__func__<<";Line "<<__LINE__<<"\n"; }};//=======================================================================class son1 : public base{public: son1(int mem):member(mem){ } ~son1() { cout<<__func__<<"\n"; } void print_m() { cout<<"Here IN class son1,FUNC: "<<__func__<<" and its member="<<member<<"\n"; }private: int member;};//=======================================================================class son2 : public base{public: son2(int mem):member(mem){ } ~son2() { cout<<__func__<<"\n"; } void print_m() { cout<<"Here IN class son2,FUNC: "<<__func__<<" and its member="<<member<<"\n"; }private: int member;};//=======================================================================class son3 : public base{public: son3(int mem):member(mem){ } ~son3() { cout<<__func__<<"\n"; } void print_m() { cout<<"Here IN class son3,FUNC: "<<__func__<<" and its member="<<member<<"\n"; }private: int member;};//=======================================================================base * get_base_ptr(int i, int mem=6){ switch (i) { case 1 : return new son1(mem); case 2 : return new son2(mem); default : return new son3(mem); }}int main(){ std::vector<base*> v; for (int i = 1; i <= 3; ++i) { v.push_back(get_base_ptr(i,i*i*i)); } BOOST_FOREACH(base * &b,v) { b->print_m(); if(b!=NULL) delete b; } return 0;}/*****************执行结果*************************Here IN class son1,FUNC: print_m and its member=1~son1~baseHere IN class son2,FUNC: print_m and its member=8~son2~baseHere IN class son3,FUNC: print_m and its member=27~son3~base[Finished in 0.6s]**************************************************/
并且如果去掉基类中析构函数的virtual输出结果为:/*****************执行结果*************************Here IN class son1,FUNC: print_m and its member=1~baseHere IN class son2,FUNC: print_m and its member=8~baseHere IN class son3,FUNC: print_m and its member=27~base[Finished in 0.6s]**************************************************/
可以看出如果没有基类的虚析构函数,在通过基类指针销毁派生类对象时候未能完全销毁。此时基类为抽象类,如果给该基类声明对象
base b;
则会出现以下错误:/home/abing/software/clang_tool/test.cc: 在函数‘int main()’中:/home/abing/software/clang_tool/test.cc:74:10: 错误: 不能将变量‘b’声明为具有抽象类型‘base’/home/abing/software/clang_tool/test.cc:6:7: 附注: 因为下列虚函数在‘base’中为纯虚函数:/home/abing/software/clang_tool/test.cc:15:18: 附注: virtual void base::print_m()[Finished in 0.4s with exit code 1]
如果将基类中的:
virtual void print_m() = 0 ;
改为:
virtual void print_m(){ cout<<"IN Base: "<<__func__<<";Line "<<__LINE__<<"\n"; }
并且将BOOST_FOREACH(base * &b,v) { b->print_m(); if(b!=NULL) delete b; }
改为:BOOST_FOREACH(base * &b,v) { b->base::print_m(); if(b!=NULL) delete b; }
则执行结果为:/*****************执行结果*************************IN Base: print_m;Line 13~son1~baseIN Base: print_m;Line 13~son2~baseIN Base: print_m;Line 13~son3~base[Finished in 0.6s]**************************************************/
8、参考文献[1] 《C++ Primer》第四版
[2] 《Effective C++》第三版
注:转载请说明出处http://blog.csdn.net/abingzhao/article/details/25548263
- C++学习笔记——virtual学习
- c++学习笔记——虚函数(virtual function)
- C#virtual,override,new学习
- C++—STL学习笔记
- 考虑virtual函数之外的其他选择——effective c++学习笔记
- 绝不重新定义继承而来的no-virtual函数——effective c++学习笔记
- Effective c++学习笔记——条款07:为多态基类声明virtual析构函数
- Effective c++学习笔记——条款07:为多态基类声明virtual析构函数
- C++11 virtual函数学习笔记
- C++11 virtual函数学习笔记
- LVS(Linux Virtual Server) 学习笔记
- LVS(Linux Virtual Server) 学习笔记
- 《Effective C++》学习笔记条款07 为多态基类声明virtual析构函数
- C语言学习笔记——指针
- C学习笔记——概述
- 认识C#——C#学习笔记
- c语言学习笔记——链表
- C / C++ / Linux / —— 学习笔记
- JQuery获取常用的尺寸属性
- linux下的基本命令
- 读书有感
- 学习C/C++语言:递归方法求阶乘
- 好久没来过这里了
- C++学习笔记——virtual学习
- 九度 1201
- SPI基本概念详解
- 屌丝也能开发安卓版2048(App Inventor)
- poj3070 Fibonacci 斐波那契数列的第n项的矩阵求法
- PHP中容易混淆的地方
- SCI收录期刊——航空航天学科
- Oracle VM VirtualBox 4.1.18+kubuntu14.04,使用samba与windows共享文件
- 充分利用ASP.NET缓存提高站点性能