C++学习笔记——virtual学习

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 
#include 
#include 
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="< 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
~base
Here IN class son2,FUNC: print_m and its member=8
~son2
~base
Here 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
~base
Here IN class son2,FUNC: print_m and its member=8
~base
Here 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
~base
IN Base: print_m;Line 13
~son2
~base
IN 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++学习笔记)