浅谈 C++中成员函数的重载、覆盖和隐藏

C++是强大的,stl库简直就是人类智慧的结晶;C++是复杂的,在此之前我都不敢说熟悉C++,对于那些在程序中只用了cincout就说他的那是C++代码的程序员们,我感到很好笑。

 现在谈谈C++中成员方法间的重载、覆盖以及隐藏。以下文章纯属自己的感觉,大神勿喷,高手绕道,若有错误,欢迎指出。

 那天去和同学去面试,他问我C++技术面会面试什么,我说就那些,继承、重载、覆盖、隐藏。听到隐藏,他感到很惊讶,他说隐藏是什么,我诡异的笑了。

 先来练习一下。

class CBase

{

public:

    void NotVitrualFunction(double x )

    {

       cout <<"CBase::NotVirtualFunction(double )"<<endl;

    }

 

    void NotVitrualFunction()

    {

       cout<<"CBase::NotVirtualFunction()"<<endl;

    }

 

    virtual voidVirtualFunction()

    {

       cout<<"CBase::VirtualFunction()"<<endl;

    }

};

 

class CChild : public CBase

{

public:

    void NotVirtualFunction(int x )

    {

       cout<<"CChild::NotVirtualFunction(int )"<<endl;

    }

     void VirtualFunction( int x )

    {

       cout<<"CChild::irtualFunction(int  )"<<endl;

    }

}; 

 

int main()

{

    CChild *p = new CChild;

    p->NotVirtualFunction(10 );

    p->NotVirtualFunction(10.00 );

    p->VirtualFunction();

    p->VirtualFunction( 0 );

    delete p;

    return 0;

}

 

求最后的输出,如果能全做对,我觉得你就没必要往下看了。

 

答案:整个程序编译出错。

 

第一个输出CChild::NotVirtualFunction( int );

第二个输出CChild::NotVirtualFunction( int );

第三个编译出错;

第四个输出 CChild::VirtualFunction( int  )。

 

上面的程序中还没有涉及到多肽。

 

一、非virtual成员方法。

 virtualC++实现多态的基础之一,在此先讨论非vittual方法。

 重载:所谓重载,简单的说就是函数名相同,而参数不同的一组函数。但是这儿还有个问题,这些函数的作用域是不是必须要相同,还是可以不相同

 覆盖:说句实话,覆盖和隐藏很像。从汉语的角度解释,所谓覆盖,指的是在原物体表面上放上别的物体。在那个位置上你仍然可以看见有物体。而所谓隐藏,指的便是在那个位置你看不到东西。

 如果C++中没有隐藏,那么重载和覆盖还是比较容易区别的。但是正如其名——“隐藏”——它总是显得若隐若现。

 现在讨论的非virtual函数,所以直接给出一句我的总结:如果子类中有于父类中同名的成员方法,那么,不管他们返回值类型、参数类型、参数顺序是否一样,子类都不能直接父类的该(系列)成员方法——除非将子类强制转换成父类。此谓之隐藏。

 看最开始的练习。p->NotVirtualFunction( 10.00 );这句居然没有输出CBase:: NotVitrualFunction(double )。而是把10.00double强制换成成int了。要知道,除非迫不得已,否则编译器是不会自动强制类型转换的。

在以前,我一直以为他们会构成重载。

构不成重载,何解?我只能说:他们的范围不一样,所以构不成重载。由于子类中出现了于父类中同名的成员函数(非virtual),所以尽管子类继承了父类的成员方法,但是他们都被隐藏了。

 换个角度:

class CBase

{

public:

    int m_data;

};

 

class CChild : public CBase

{

public:

    int m_data;

};

 

你觉得CChild对象能调用CBase中的m_data么——它被隐藏了。

子类和父类的成员方法构不成重载。重载是编译器的特性,C++只是应用了这种特性。

至于覆盖,仅仅指的是子类重写父类的virtual方法。

 

二、virtual成员

    如果没有virtual成员,上面还算简单。

 class CBase

{

 public:

    virtual voidVirtualFunction( doublex )

    {

       cout <<"CBase::VirtualFunction(double )"<<endl;

    }

 

    virtual voidVirtualFunction()

    {

       cout<<"CBase::VirtualFunction()"<<endl;

    }

};

  

class CChild : public CBase

{

 public:

    void VirtualFunction( int x )

    {

       cout<<"CChild:: VirtualFunction( int )"<<endl;

    }

};

 

int main()

{

    CChild *p = new CChild;

    p->VirtualFunction();

    p->VirtualFunction( 10.0 );

    delete p;

    return 0;

}

现在讨论这段程序的输出。很遗憾,整个程序编译出错,提示是VirtualFunction不能接受无参调用。

        我们会想,VirtualFunction是虚函数啊,如果不在子类定义,那就是自动继承的啊。但是,既然不能调用,我就只能说“不管是不是虚函数,它都被隐藏了… …”。

        上面基本都是隐藏在唱主角。现在我们来看看神秘的覆盖。

class CBase

{

public:

    virtual voidVirtualFunction( intx )

    {

       cout <<"CBase::VirtualFunction(int )"<<endl;

    }

};

 

class CChild : public CBase

{

public:

    void VirtualFunction( double x )

    {

       cout<<"CChild::VirtualFunction(double )"<<endl;

    }

};

  

int main()

{

    CBase *p = new CChild;

    p->VirtualFunction( 10.0 );

    delete p;

    return 0;

}

 

看看这段程序输出什么。

 输出的是CBase::VirtualFunction(int );

        应该输出CChild::VirtualFunction( double )啊,CBase中的不是被隐藏了么。是的,确实被隐藏了,这儿的关键是“多肽”。由此可以得到另一条结论:基类指针只能调用在基类中被声明为virtual的成员,否则就要强制转换。这又联系到另外一个问题“C++中的常把析构函数声明为虚函数,为什么,有什么作用?”。

        答:“防止内存泄露”。至于为什么,先看下面两个类:

 

class CBase

{

public:

    CBase()

    {

       m_baseData = new char[32];

    }

    virtual ~CBase()

    {

       delete[] m_baseData;

       cout<<"CBase::Destructor()"<<endl;

    }

    char *m_baseData;

};

 

 class CChild : public CBase

{

public:

    CChild()

    {

       m_childData = new char[32];

    }

    ~CChild()

    {

       delete []m_childData;

       cout<<"CChild::Destructor()"<<endl;

    }

    char *m_childData;

};

 

举的这两个类很对,但还要看你怎么用了。倘若你举例CChild child,然后分析其析构过程,对不起,你达不到你的目的。如果是CChild child,即使析构函数不是虚函数,child对象在析构时也会先调用自己的析构函数,再调用父类的析构函数,既然两个析构函数都调用了,就不会存在内存泄露的问题了。

       但,假如是CBase *p = new CChild; deletep; 

       CBase类的析构函数不是虚函数,那么delete p 的时候会调用什么?由前面的例子可以知道,会调用CBase的析构函数,这样一来,CChild中的m_childData不就造成内存泄露了?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值