如何写出优质的C++代码:(八)C++继承体系的几个问题

C++的继承体系给我们的程序带来了很多奇特的现象,看看下面这段代码:

class CBase
{
public:
    CBase();
    ~CBase(){std::cout << "析构基类" << std::endl;}
};

//派生类
class CSon1 : public CBase
{
public:
    CSon1();
    ~CSon1();{std::cout << "析构派生类" << std::endl;}
}

......
CBase *pB = new CSon1();
......
delete pB;

我们定义了一个基类CBase和一个派生类CSon1,然后我们声明了一个基类的指针pB,并对其赋值以派生类的对象,指针被使用完之后,调用了delete对其资源进行释放,一切看起来都很正常,最终程序运行的结果是:

析构基类

这似乎跟我们预想的不一致,我们通过delete释放资源,最终只有基类的析构函数被执行,如果派生类中申请了大量的资源,将无法得到释放。

问题出在基类CBase中有一个非虚析构函数。在C++的语法规则中,当派生类经由一个基类指针被删除,而该基类带着一个非虚析构函数,实际执行时通常发生的是对象的派生成分没有被销毁。上述问题解决的办法是将基类CBase的析构函数声明为虚函数:

virtual ~CBase();
......
virtual ~CSon1();

再次运行程序得到输出结果:

析构派生类
析构基类

在写程序中,一般情况下,将基类的析构函数定义成虚函数是一个好的习惯

对于类的成员函数,虚函数也影响函数的调用结果,看看下面这段代码:

class CBase
{
    ......
    void func1() 
    {
        std::cout << "CBase::func1" << std::endl;
    }
};
class CSon1 : public CBase
{
    ......
	void func1() 
    {
        std::cout << "CSon1 ::func1" << std::endl;
    }
};

还是上面我们提到的两个类CBase和CSon1,我们在基类和派生类中都添加了一个同名的非虚函数func1(),我们通过以下两种方式来调用func1()函数:

CSon1 son;
//方法1
CBase *pB = &son;
pB->func1();
//方法2
CSon1 *pS = &son;
pS->func1();

这两种方法的区别是它们分别使用了基类指针和派生类指针调用了func1(),而两个指针指向了同一个对象son。我们可能会这样认为:这两个指针都通过son调用了func1()函数,他们的的行为应该相同。情况是这样吗,我们看看程序的执行结果:

CBase::func1
CSon1::func1

真是结果与我们预想的不一样,程序分别执行了基类和派生类的func1()函数,而造成这一结果的原因是fun1()是非虚函数。非虚函数都是静态绑定的,也就是说,通过pB调用非虚函数永远是CBase所定义的版本,即使pB指向了一个派生类对象。另一方面,虚函数则是动态绑定的,不论是通过基类指针,还是派生类指针,最终都会调用派生类的函数CSon1::func1()。

在上述的代码中,我们将func1()声明为virtual函数,执行结果是:

CSon1::func1
CSon1::func1

从这个例子中,我们可以学习到:如果基类中的函数为非虚函数,我们不要尝试去重新定义它

还有一个由C++继承带来的问题,看看下面的代码:

class CBase
{
    ......
    virtual void func1(BOOL bPrint = TRUE) 
    {
        if(bPrint)
            std::cout << "CBase::func1" << std::endl;
    }
};
class CSon1 : public CBase
{
    ......
	virtual void func1(BOOL bPrint = FALSE) 
    {
        if(bPrint)
            std::cout << "CSon1 ::func1" << std::endl;
    }
};

......
CBase *pB = new CSon1();
pB->func1();

程序中,pB的动态类型是CSon1*,所以调用的是CSon1::func1()的缺省参数,而pB的静态类型是CBase*,所以此一调用的缺省参数来自CBase::func1()。编译器基于最大化程序执行效率的考虑,最终选择了静态类型的缺省参数。程序执行的结果为:

CSon1 ::func1

这也就告诉我们,程序中尽量不要重新定义基类的缺省参数,这样可能得不到你想要的结果

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值