zoukankan      html  css  js  c++  java
  • 虚函数、纯虚函数、虚函数与析构函数

    一、虚函数

    只有用virtual声明类的成员函数,使之成为虚函数,不能将类外的普通函数声明为虚函数。因为虚函数的作用是允许在派生类中对基类的虚函数重新定义。所以虚函数只能用于类的继承层次结构中。

         一个成员函数被声明为虚函数后,在同一类族中的类就不能再定义一个非virtual的但与该虚函数具有相同的参数(包括个数和类型)和函数返回值类型的同名函数。

           根据什么考虑是否把一个成员函数声明为虚函数?

           ①  看成员函数所在的类是否会作为基类

           ② 看成员函数在类的继承后有无可能被更改功能,如果希望更改其功能的,一般应该将它声明为虚函数。

           如果成员函数在类被继承后功能不需修改,或派生类用不到该函数,则不要把它声明为虚函数。不要仅仅考虑到作为基类而把类中的所有成员函数都声明为虚函数。

          应考虑对成员函数的调用是通过对象名还是通过基类指针或引用去访问,如果是通过基类指针或引用去访问的,则应当声明为虚函数。有时在定义虚函数时,并不定义其函数体,即纯虚函数。它的作用只是定义了一个虚函数名,具体功能留给派生类去添加。

          说明:使用虚函数,系统要有一定的空间开销。当一个类带有虚函数时,编译系统会为该类构造一个虚函数表(vtbl),它是一个指针数组,存放每个虚函数的入口地址。系统在进行动态关联的时间开销很少,提高了多态性的效率。

    二、纯虚函数

            有时候,基类中的虚函数是为了派生类中的使用而声明定义的,其在基类中没有任何意义。此类函数我们叫做纯虚函数,不需要写成空函数的形式,只需要声明成:

          virtual 函数类型 函数名(形参表列)=0;

           注意:纯虚函数没有函数体;

          最后面的“=0“并不代表函数返回值为0,只是形式上的作用,告诉编译系统”这是纯虚函数”;

          这是一个声明语句,最后应有分号。

         纯虚函数只有函数的名字但不具备函数的功能,不能被调用。在派生类中对此函数提供定义后,才能具备函数的功能,可以被调用。

    三、虚析构函数

          析构函数的作用是在对象撤销之前把类的对象从内存中撤销。通常系统只会执行基类的析构函数,不执行派生类的析构函数

          只需要把基类的析构函数声明为虚函数,即虚析构函数,这样当撤销基类对象的同时也撤销派生类的对象,这个过程是动态关联完成的

          如果将基类的析构函数声明为虚函数时,由该基类所派生的所有派生类的析构函数都自动成为虚函数,即使派生类的析构函数与基类的析构函数名字不相同。

           最好把基类的析构函数声明为虚函数,这将使所有派生类的析构函数自动成为虚函数,如果程序中显式delete运算符删除一个对象,而操作对象用了指向派生类对象的基类指针,系统会调用相应类的析构函数。

            构造函数不能声明为虚函数。

    例如:

    #include <iostream>
    using namespace std;
    
    class Animal
    {
    public:
    	Animal()
    	{
    		cout << "Animal::Animal() is called" << endl;
    	};
    	virtual ~Animal()
    	{
    		cout << "Animal::~Animal() is called" << endl;
    	}
    	virtual void eat()
    	 { 
    	 	cout << "Animal::eat() is called" << endl;
    	 }
    	virtual void walk()
    	{ 
    	 	cout << "Animal::walk() is called" << endl;
    	}
    	/* data */
    };
    class Dog : public Animal
    {
    public:
    	Dog(int w,int h)
    	{
    		cout << "Dog::Dog() is called" << endl;
    		this->weight=w;
    		this->height=h;
    	}
    	virtual ~Dog()
    	{
    		cout << "Dog::~Dog() is called" << endl;
    	}
    	int weight;
    	int height;
    	void eat()
    	{
    		cout<<"i eat meat"<<endl;
    	}
    	void walk()
    	{
    		cout<<"run"<<endl;
    	}
    	/* data */
    };
     int main(int argc, char const *argv[])
    {
    	/* code */
    	Animal *ani= new Dog(12,23);
    	Dog *dog=new Dog(23,34);
    	ani->eat();
    	ani->walk();
    	dog->eat();
    	dog->walk();
    	delete ani;
    	//delete dog;
    	return 0;
    }
    

     (C++ 析构函数一般定义为虚函数)如果基类中析构函数没有定义为虚函数,则delete ani的时候,仅仅调用了父类的析构函数,子类的没有调用,如果在父类和子类的构造函数中都有动态内存分配,那么就会存在内存泄漏的问题。一般析构函数最好都写成虚函数,尤其是父类。 

    本文有参考:http://blog.csdn.net/lxnkobe/article/details/23115417

    http://blog.csdn.net/richerg85/article/details/9217875

  • 相关阅读:
    选课系统
    Python入门day34——网络编程基础
    Python入门day34——异常处理
    Python入门day33——元类、属性查找
    Python入门day33——反射、内置方法
    Python入门day30——mixins机制、super()、多态、绑定方法、内置函数
    从 0 到 1 实现 React 系列 —— 5.PureComponent 实现 && HOC 探幽
    前端中常见数据结构小结
    从 0 到 1 实现 React 系列 —— 4.setState优化和ref的实现
    从 0 到 1 实现 React 系列 —— 3.生命周期和 diff 算法
  • 原文地址:https://www.cnblogs.com/carsonzhu/p/5271961.html
Copyright © 2011-2022 走看看