• 还原virtual函数的本质-----C++


    当你每次看到C++类中声明一个virtual函数,特别是看到了一个virtual的虚构函数。你知道它的意思吗?你肯定会毫不犹豫的回答:不就是多态么。。。在运行时确定具体的行为么。。。完全正确,但这里我要讲的不只是这些东西。

     有些类需要虚函数,有些不需要虚函数。这是为什么,一般你看到的类如果有一个虚析构函数,那么这个类中应该会有至少一个是虚函数的。。这是为什么呢??如果我们类中没有用其他虚函数的话,你创建了这个也是多余的,而且会增加类对象的大小。。说这些纯理论的东西,也许大家不知所云。。下面我就给例子来验证。。

    1:

    class A
    {
    public:
    	A(){};
    //	virtual ~A(){};
    	~A();
    };
    void main()
    {
    
    
    	A a;
    	cout<<sizeof(a)<<endl;
    }


    结果为1。这个1应该是编译器自己为它加上的。。哪怕你不在类中不写任何东西,它也是1;例如;

    class A
    {
    
    };
    void main()
    {
    
    	A a;
    	cout<<sizeof(a)<<endl;
    }
    


    如果你把析构函数声明为虚函数。。如:

    2:

    class A
    {
    public:
    A(){};
    virtual ~A(){};
    //~A();
    };
    void main()
    {


    A a;
    cout<<sizeof(a)<<endl;
    }

    结果是4。先不说这是为什么。。


    然后还是说一下关于虚函数基础的东西(多态)吧,也给个例子:

    #include<iostream>
    #include<string>
    using namespace std;
    
    class Base
    {
    public:
    	Base();
    	virtual ~Base();
    
    	virtual  void test();
    private :
    	int count;
    };
    Base::Base(){
    	cout<<"Base部分创建了"<<endl;
    }
    Base::~Base(){
    	cout<<"Base部分被销毁了"<<endl;
    }
    void Base::test()
    {
    	cout<<"Base Test"<<endl;
    	
    }
    
    class Derive1:public Base
    {
    public:
    	Derive1();
    	virtual  ~Derive1();
    	
    	void test();
    
    };
    Derive1::Derive1(){
    	cout<<"子类部分创建了"<<endl;
    }
    Derive1::~Derive1(){
    	cout<<"子类部分被销毁了"<<endl;
    }
    void Derive1::test()
    {
    	cout<<"Derive1 Test"<<endl;
    }
    
    
    
    
    void main()
    {
    	Base* d1=new Derive1();
    	d1->test();
    	delete d1;
    
    }
    



    由此看见,当通过声明一个父类指针并且让它指向一个子类的对象,在子对象创建的时候,会先去调用父类的构造函数,然后再是自己的构造函数,当通过父类指针去调用一个虚函数test()时,它实际上回去调用子类的test()函数,这是为什么呢,它肯定有什么信息让它这样做吗。。这个信息肯定是子类对象给它的。。这个信息就是虚函数指针(vptr),它指向一个虚函数表(vtbl),这个虚函数表其实就是包含了这个类的所有虚函数的函数名(函数指针),每个类就只包含了那一个虚函数指针和它的一些成员变量。这下可以解释上面为什么是1,为什么是4了。。

     在win32的机器上,每个指针是4字节。刚才也提到每个类的大小取决于两部分,一个是成员变量,一个是虚函数指针而且有且只有一个,在例子一中,因为没有成员变量,而有一个虚函数---析构函数,此时肯定会有一个虚函数指针,所以是4。。 其实刚才也就同时说清楚了多态的本质,就是子对象的虚函数指针给出了这个信息,父类指针才知道去执行哪个函数。。

    最后一个问题:为什么析构函数要声明为虚函数呢?(当至少有一个为虚函数的时候)

    从刚才的那个结果也可以看出,当我们delete那个指针的时候,会发生析构,而且这个过程是从子类到父类的顺序进行。假如此时析构函数不为虚函数,父类指针也就不知道去执行子类的析构函数。。也就不会去释放子对象的那部分内存,造成内存泄漏。。例如:(这里我们只是对上一段代码进行修改,去掉了父类中的virtual):

    #include<iostream>
    #include<string>
    using namespace std;
    
    class Base
    {
    public:
    	Base();
    	 ~Base();
    
    	virtual  void test();
    private :
    	int count;
    };
    Base::Base(){
    	cout<<"Base部分创建了"<<endl;
    }
    Base::~Base(){
    	cout<<"Base部分被销毁了"<<endl;
    }
    void Base::test()
    {
    	cout<<"Base Test"<<endl;
    	
    }
    
    class Derive1:public Base
    {
    public:
    	Derive1();
    	  ~Derive1();
    	
    	void test();
    
    };
    Derive1::Derive1(){
    	cout<<"子类部分创建了"<<endl;
    }
    Derive1::~Derive1(){
    	cout<<"子类部分被销毁了"<<endl;
    }
    void Derive1::test()
    {
    	cout<<"Derive1 Test"<<endl;
    }
    
    
    
    
    
    
    void main()
    {
    	Base* d1=new Derive1();
    	d1->test();
    	delete d1;
    
    }

    所以当至少有一个虚函数的话,我们也要把它的析构函数声明为virtual。(插一句:有些人会说你子类中的那个函数哪里是虚函数哦,我没看到virtual 啊。。其实C++允许我们这样做,重写父类的虚函数,不是必须要声明出来的。)

    总结:一个类有了虚函数,是为了成为一个基类,如果不是这样的话,那么父类中的任何函数都没有必要是虚函数,甚至会增加类的大小。多态告诉了我们这点。。一旦成为了基类,那么就要把析构函数声明为一个虚函数。。

    好了,虚函数的内容就Over了。。。。。

  • 相关阅读:
    linux安装nexus
    k8s批量删除资源
    spug 一个好用运维管理平台
    SpringMvc几种数据转化方法
    Esxi通过U盘启动
    LobHandler和LobCreator
    Spring的NamedParameterJdbcTemplate
    Spring中的JdbaTemplate
    Rufus
    UltraISO 注册码
  • 原文地址:https://www.cnblogs.com/pangblog/p/3258031.html
Copyright © 2020-2023  润新知