C++之成员函数声明为虚函数的规则
2017-03-20 00:15
239 查看
虚函数
定义:
虚函数必须是基类的非静态成员函数,其访问权限可以是protected或public,是C++多态的一种表现。
作用:
实现动态联编,也就是在程序的运行阶段动态地选择合适的成员函数,在定义了虚函数后,可以在基类的派生类中对虚函数重新定义,在派生类中重新定义的函数应与虚函数具有相同的形参个数和形参类型,以实现统一的接口,不同定义过程。如果在派生类中没有对虚函数重新定义,则它继承其基类的虚函数。当程序发现虚函数名前的关键virtual后,会自动将其作为动态联编处理,即在程序运行时动态地选择合适的成员函数。
使用方法:
动态绑定指出,只能通过指向基类的指针或基类对象的引用来调用虚函数,其格式:
指向基类的指针变量名->虚函数名(实参表)
或
基类对象的引用名. 虚函数名(实参表)
什么函数不能声明为虚函数:
1.构造函数不能声明为虚函数
因为在执行构造函数时类对象还未完成建立过程,更谈不上把函数与类对象的绑定(关于绑定可参考上一篇博文)因为构造函数本来就是为了明确初始化对象成员才产生的,然而virtual function主要是为了再不完全了解细节的情况下也能正确处理对象。
2.静态成员函数不能声明为虚函数
(1)静态成员函数并不是“成员”,它没有this指针。
(2)静态成员函数对每个类只有一份代码而且是共享的,所以它不被继承,当然也不能被声明为虚函数。
(3)静态成员函数是静态的在编译时,而虚函数是动态的在运行时。两者创建时间不同。
(4)静态成员函数可以通过类调用,但不能通过对象调用,而虚函数只能通过对象调用才有意义。
3.友元函数不能声明为虚函数
和静态成员类似,它不是成员,不能被继承
4.赋值运算符重载函数最好不要声明为虚函数
5.析构函数可以声明为虚函数
因为无论指针值指的是同一个类中的哪个类对象,系统都会采用动态绑定,调用相应类的析构函数对该对象进行清理。而且继承时最好将析构函数声明为虚函数。
6.普通函数不声明为虚函数
普通函数(非成员函数)声明为虚函数也没有什么意思,因此编译器会在编译时邦定函数。多态的运行期行为体现在虚函数上,虚函数通过继承方式来体现出多态作用,顶层函数不属于成员函数,是不能被继承的。
7.内联函数不能声明为虚函数
其实很简单,内联函数就是为了在代码中直接展开,减少函数调用花费的代价,虚函数是为了在继承后对象能够准确的执行自己的动作,这是不可能统一的。再者内联函数和虚函数有着本质的区别,内联函数是在程序被编译时就展开,在函数调用处用整个函数体去替换,而虚函数是在运行期才能够确定如何去调用的,因而内联函数体现的是一种编译期机制,虚函数体现的是一种运行期机制。此外,一切虚函数都不可能是内联函数。
下面这个示例意在证明将析构函数声明为虚函数的好处:
第一段代码:
用派生类创建派生类指针
运行结果:
Do something in Derived!
~Derived()
~Base()
这段代码中基类的析构函数不是虚函数,在main函数中用继承类的指针去操作继承类的成员,释放指针d的过程是:先释放继承类的资源,再释放基类资源。
若将以上代码的主函数改为用派生类创建基类指针
输出结果:
Do something in Base
~Base()
这段代码中基类的析构函数同样不是虚函数,不同的是在main函数中用基类的指针去操作继承类的成员,释放指针b的过程是:只是释放了基类的资源,而没有调用继承类的析构函数.调用Fun1()函数执行的也是基类定义的函数。
一般情况下,这样的删除只能够删除基类对象,而不能删除子类对象,形成了删除一半形象,造成内存泄漏。
在公有继承中,基类对派生类及其对象的操作,只能影响到那些从基类继承下来的成员.如果想要用基类对非继承成员进行操作,则要把基类的这个函数定义为虚函数。
析构函数自然也应该如此:如果它想析构子类中的重新定义或新的成员及对象,当然也应该声明为虚的。
运行结果:
Do something in Derived
~Derived()
~Base()
这段代码中基类的析构函数被定义为虚函数,在main函数中用基类的指针去操作继承类的成员,释放指针b的过程是:只是释放了继承类的资源,再调用基类的析构函数.调用Fun1()函数执行的也是继承类定义的函数。
所以如果不需要基类对派生类及对象进行操作,则不能定义虚函数,因为这样会增加内存开销.当类里面有定义虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间.所以,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数。
定义:
虚函数必须是基类的非静态成员函数,其访问权限可以是protected或public,是C++多态的一种表现。
作用:
实现动态联编,也就是在程序的运行阶段动态地选择合适的成员函数,在定义了虚函数后,可以在基类的派生类中对虚函数重新定义,在派生类中重新定义的函数应与虚函数具有相同的形参个数和形参类型,以实现统一的接口,不同定义过程。如果在派生类中没有对虚函数重新定义,则它继承其基类的虚函数。当程序发现虚函数名前的关键virtual后,会自动将其作为动态联编处理,即在程序运行时动态地选择合适的成员函数。
使用方法:
动态绑定指出,只能通过指向基类的指针或基类对象的引用来调用虚函数,其格式:
指向基类的指针变量名->虚函数名(实参表)
或
基类对象的引用名. 虚函数名(实参表)
什么函数不能声明为虚函数:
1.构造函数不能声明为虚函数
因为在执行构造函数时类对象还未完成建立过程,更谈不上把函数与类对象的绑定(关于绑定可参考上一篇博文)因为构造函数本来就是为了明确初始化对象成员才产生的,然而virtual function主要是为了再不完全了解细节的情况下也能正确处理对象。
2.静态成员函数不能声明为虚函数
(1)静态成员函数并不是“成员”,它没有this指针。
(2)静态成员函数对每个类只有一份代码而且是共享的,所以它不被继承,当然也不能被声明为虚函数。
(3)静态成员函数是静态的在编译时,而虚函数是动态的在运行时。两者创建时间不同。
(4)静态成员函数可以通过类调用,但不能通过对象调用,而虚函数只能通过对象调用才有意义。
3.友元函数不能声明为虚函数
和静态成员类似,它不是成员,不能被继承
4.赋值运算符重载函数最好不要声明为虚函数
5.析构函数可以声明为虚函数
因为无论指针值指的是同一个类中的哪个类对象,系统都会采用动态绑定,调用相应类的析构函数对该对象进行清理。而且继承时最好将析构函数声明为虚函数。
6.普通函数不声明为虚函数
普通函数(非成员函数)声明为虚函数也没有什么意思,因此编译器会在编译时邦定函数。多态的运行期行为体现在虚函数上,虚函数通过继承方式来体现出多态作用,顶层函数不属于成员函数,是不能被继承的。
7.内联函数不能声明为虚函数
其实很简单,内联函数就是为了在代码中直接展开,减少函数调用花费的代价,虚函数是为了在继承后对象能够准确的执行自己的动作,这是不可能统一的。再者内联函数和虚函数有着本质的区别,内联函数是在程序被编译时就展开,在函数调用处用整个函数体去替换,而虚函数是在运行期才能够确定如何去调用的,因而内联函数体现的是一种编译期机制,虚函数体现的是一种运行期机制。此外,一切虚函数都不可能是内联函数。
下面这个示例意在证明将析构函数声明为虚函数的好处:
第一段代码:
用派生类创建派生类指针
#include <iostream> using namespace std; class Base { public: Base() {} ~Base() { cout<<"~Base()"<<endl; } void Fun1() { cout<<"Do something in Base"<<endl; } }; class Derived:public Base { public: Derived() {} ~Derived() { cout<<"~Derived()"<<endl; } void Fun1() { cout<<"Do something in Derived"<<endl; } }; int main() { Derived *d = new Derived; d->Fun1 (); delete d; return 0; }
运行结果:
Do something in Derived!
~Derived()
~Base()
这段代码中基类的析构函数不是虚函数,在main函数中用继承类的指针去操作继承类的成员,释放指针d的过程是:先释放继承类的资源,再释放基类资源。
若将以上代码的主函数改为用派生类创建基类指针
int main() { Base *b = new Derived; b->Fun1 (); delete b; return 0; }
输出结果:
Do something in Base
~Base()
这段代码中基类的析构函数同样不是虚函数,不同的是在main函数中用基类的指针去操作继承类的成员,释放指针b的过程是:只是释放了基类的资源,而没有调用继承类的析构函数.调用Fun1()函数执行的也是基类定义的函数。
一般情况下,这样的删除只能够删除基类对象,而不能删除子类对象,形成了删除一半形象,造成内存泄漏。
在公有继承中,基类对派生类及其对象的操作,只能影响到那些从基类继承下来的成员.如果想要用基类对非继承成员进行操作,则要把基类的这个函数定义为虚函数。
析构函数自然也应该如此:如果它想析构子类中的重新定义或新的成员及对象,当然也应该声明为虚的。
#include <iostream>
using namespace std;
class Base
{
public:
Base()
{}
virtual ~Base()
{
cout<<"~Base()"<<endl;
}
void Fun1()
{
cout<<"Do something in Base"<<endl;
}
};
class Derived:public Base
{
public:
Derived()
{}
~Derived()
{
cout<<"~Derived()"<<endl;
}
void Fun1()
{
cout<<"Do something in Derived"<<endl;
}
};
int main() { Base *b = new Derived; b->Fun1 (); delete b; return 0; }
运行结果:
Do something in Derived
~Derived()
~Base()
这段代码中基类的析构函数被定义为虚函数,在main函数中用基类的指针去操作继承类的成员,释放指针b的过程是:只是释放了继承类的资源,再调用基类的析构函数.调用Fun1()函数执行的也是继承类定义的函数。
所以如果不需要基类对派生类及对象进行操作,则不能定义虚函数,因为这样会增加内存开销.当类里面有定义虚函数的时候,编译器会给类添加一个虚函数表,里面来存放虚函数指针,这样就会增加类的存储空间.所以,只有当一个类被用来作为基类的时候,才把析构函数写成虚函数。
相关文章推荐
- 【继承与多态】C++:继承中的赋值兼容规则,子类的成员函数,虚函数(重写),多态
- 【继承与多态】C++:继承中的赋值兼容规则,子类的成员函数,虚函数(重写),多态
- C++学习总结_成员函数的声明和实现需要注意问题
- 将基类的private成员函数声明为虚函数的讨论
- 类成员声明与定义前加inline的区别(C++ inline 函数)
- C++之普通成员函数、虚函数以及纯虚函数的区别与用法要点
- C++语言基础 例程 类声明和成员函数定义的分离
- 成员函数声明为常量的解释--C++学习笔记
- C++继承类和基类之间成员函数和虚函数调用机制
- C++静态成员函数不能声明为const、volatile、virtual的原因 与 C++的对象模型
- C++函数中那些不可以被声明为虚函数的函数
- C++语言特性:构造函数,析构函数,虚函数,内联函数,静态成员函数,重载,覆盖,隐藏
- C++不能声明为虚函数的函数
- 【反汇编分析】C++成员函数和虚函数
- c++ 变量声明: 成员函数指针 成员变量指针
- [原创] 将基类的private成员函数声明为虚函数的讨论
- C++第六周任务5:解决用一个项目多个文件的方式实现,其中两个类的声明放在一个.h文件中,每个类的成员函数分别放一个文件,main()函数用一个文件。体会这样安排的优点。
- C++ const 参数 成员函数 规则
- 写程序说明C++中成员函数的初始化顺序只跟在类中的声明顺序有关, 而跟初始化列表中的顺序无关(笔试考过)
- C++ 虚函数和其他成员函数