EffectiveC++-条款36:绝不重新定义继承而来的 non-virtual 函数

一. 内容

  1. 考虑以下示例,一个 D class 继承自 B class 的体系中,两者均定义了同样的 non-virtual 函数:

    class B {
    public:
        std::string ToString() const{
            return "B";
        }
    };
    
    class D : public B {
    public:
        std::string ToString() const{
            return "D";
        }
    };
    
    inline void BTry() {
        D Derived;
        B* BasePointer = &Derived;
        D* DerivedPointer = &Derived;
        std::cout << BasePointer->ToString() << "\n"; //B
        std::cout << DerivedPointer->ToString() << "\n"; //D
    }
    
  2. 结果令人奇怪,毕竟两个指针都指向对象 Derived ,并都调用相同的 ToString 函数,但结果却不一样。首先按照条款33,Derived class 声明的同名函数 已经将来自 Base Class 中的 ToString 函数遮掩,所以通过 Derived* 访问不了 Base::ToString 函数。但现在是指向同一对象,按理说都是处理 Derived 作用域,为什么使用 Base* 指向 Derived 时可以调用 Base::ToString 函数呢?

    因为 non-virtual 函数是静态绑定的,见条款37,也就是说通过 Base* 指针声明时,non-virtual 就已经根据它的指针类型绑定了函数,而 virtual 函数可以在赋值中改变,所谓动态绑定,意味着无论通过 Base* 或者 Derived* ,因为它们实际指向同样的 Derived 对象,所以都会调用 Derived::ToString 函数。

  3. 当然,以上只是表象的讨论,我知道你真正想要理论上的东西。条款32已经说过,所谓 public 继承是 is-a 的关系,条款34则描述为什么声明 non-virtual 函数强调了 class 的不变性,把这两点放在 class B,D 和 non-virtual 函数 B::ToString上:

    • 适用于 B 对象的每一件事,也适用于 D 对象,因为每一个 D 对象都是 B 对象。
    • B 的 derived classes 一定会继承 ToString 的接口和实现,因为 ToString 是一个 non-virtual 函数。

    现在,如果 D 重新定义 ToString ,你的设计便出现矛盾。如果 D 真的有必要实现出于 B 不同的 ToString,那么适用于 B 对象的每一件事便不再适用于 D。既然如此,D 便不该以 public 形式继承 B。另一方面,如果 D 真的必须以 public 继承 B,并且 D需要表现出于 B 不同的 ToString,那么 ToString 应该设计成 virtual 函数。最后,如果每一个 D 都是 B,并且不希望 D 特化 ToString,那么 D 便不应该重新定义 ToString。

    无论哪个观点,结论都相同:任何情况下都不该重新定义一个继承而来的 non-virtual 函数

  4. 如果你感到枯燥乏味,或许是因为你已经读过条款7,该条款解释为什么多态性质的 base classes 应该声明 virtual 析构函数。如果你在多态性质下的 base class 声明了 non-virtual 函数,那么derived class 便绝不应该重新定义一个继承而来的 non-virtual 析构函数。但即使你没有定义,条款5曾说,编译器会默认为你生成它,所以多态性质的 base classes 都需要 virtual 析构函数。因此就本质而言,条款7只不过是本条款的一个特殊案例,尽管它足够重要到单独成为一个条款。

二. 总结

  1. 绝对不要重新定义继承而来的 non-virtual 函数。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值