| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 431 人关注过本帖
标题:C++中成员函数的重载、覆盖和隐藏
取消只看楼主 加入收藏
zhangqi_gsts
Rank: 6Rank: 6
来 自:甘肃天水
等 级:侠之大者
威 望:1
帖 子:227
专家分:457
注 册:2011-3-27
结帖率:87.5%
收藏
 问题点数:0 回复次数:0 
C++中成员函数的重载、覆盖和隐藏
C++是强大的,stl库简直就是人类智慧的结晶;C++是复杂的,在此之前我都不敢说熟悉C++,对于那些在程序中只用了cin、cout就说他的那是C++代码的程序员们,我感到很好笑。
现在谈谈C++中成员方法间的重载、覆盖以及隐藏。以下文章纯属自己的感觉,大神勿喷,高手绕道,若有错误,欢迎指出。

那天去和同学去面试,他问我C++技术面会面试什么,我说就那些,继承、重载、覆盖、隐藏。听到‘隐藏’,他感到很惊讶,他说隐藏是什么,我诡异的笑了。
先来练习一下。
class CBase
{
public:
    void NotVitrualFunction( double x )
    {
        cout <<"CBase::NotVirtualFunction( double )"<<endl;
    }

    void NotVitrualFunction()
    {
        cout<<"CBase::NotVirtualFunction()"<<endl;
    }

    virtual void VirtualFunction()
    {
        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成员方法。
virtual是C++实现多肽的基础之一,在此先讨论非vittual方法。
重载:所谓重载,简单的说就是函数名相同,而参数不同的一组函数。但是这儿还有个问题,“这些函数的作用域是不是必须要相同,还是可以不相同”。
覆盖:说句实话,覆盖和隐藏很像。从汉语的角度解释,所谓覆盖,指的是在原物体表面上放上别的物体。在那个位置上你仍然可以看见有物体。而所谓隐藏,指的便是在那个位置你看不到东西。
如果C++中没有隐藏,那么重载和覆盖还是比较容易区别的。但是正如其名——“隐藏”——它总是显得若隐若现。
现在讨论的非virtual函数,所以直接给出一句我的总结:如果子类中有于父类中同名的成员方法,那么,不管他们返回值类型、参数类型、参数顺序是否一样,子类都不能直接父类的该(系列)成员方法——除非将子类强制转换成父类。此谓之隐藏。
看最开始的练习。p->NotVirtualFunction( 10.00 );这句居然没有输出CBase:: NotVitrualFunction( double )。而是把10.00从double强制换成成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 void VirtualFunction( double x )
    {
        cout <<"CBase::VirtualFunction( double )"<<endl;
    }
    virtual void VirtualFunction()
    {
        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 void VirtualFunction( int x )
    {
        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; delete p;
    若CBase类的析构函数不是虚函数,那么delete p 的时候会调用什么?由前面的例子可以知道,会调用CBase的析构函数,这样一来,CChild中的m_childData不就造成内存泄露了?
   
重载:重载的函数必须有相同的作用域,子类和父类的函数永远不可能构成重载。
覆盖:子类重写父类中的虚函数。
隐藏:子类重写了父类中的非虚函数。
我们的目的不是弄清楚这三个的区别,我们的目的是在程序中能判断调用的是哪个函数。
搜索更多相关主题的帖子: public double 程序员 文章 技术 
2013-11-19 15:02
快速回复:C++中成员函数的重载、覆盖和隐藏
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.017512 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved