Effective C++条款35 考虑virtual函数之外的操作

本文探讨了C++中通过virtual继承实现接口的局限性,并提出了三种替代方案:NVI(非虚拟接口)、使用函数指针及古典策略模式,以避免不必要的重写并提高代码灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  • 通过virtual继承实现接口:比如下面的Hero类的healthValue类,但有一些缺点:派生类不想实现的方法必须实现。又或者继承了不想用的特殊方法,于是本文围绕virtual函数的替代方法写了三种办法
#include <iostream>

class GameCharacter {
public:
    virtual int healthValue()const {
        std::cout << "Base" << std::endl;
        return 0;
    };
};

class Hero : public GameCharacter{
public:
    virtual int healthValue()const {
        std::cout << "Derived" << std::endl;
        return 0;
    };
};
int main() {
    Hero mhero;
    mhero.healthValue();
    return 0;
}
  • (1)替代方案之用NVI(non virtual interface)
  • #include <iostream>
    
    
    class GameCharacter {
    
    private:
        virtual int dohealthValue()const {
            std::cout << "Base" << std::endl;
            return 0;
        };
    };
    
    class Hero : public GameCharacter{
    public:
        int healthValue() const //客户调用public接口,实现对具体实现动作dohealthValue()封装
        {
            // 可以实现其他动作
            return dohealthValue();
        };
    private:
        virtual int dohealthValue()const {
            std::cout << "Derived" << std::endl;
            return 0;
        };
    };
    int main() {
        Hero mhero;
        mhero.healthValue();
        return 0;
    }

在Hero类把继承来的virtual函数的接口进行封装,并且对外提供封装,这种操作叫做NVI,但这其实也没有完全的避开重写virtual函数,这是相比把所有的动作放到dohealthValue(); 多了一层封装,但比完全的继承要好一些。

  • (2)通过函数指针来执行需要的动作,从而避免virutal的继承。
#include <iostream>

class GameCharacter;
int defHealthCalcFunc(const GameCharacter &) {
    std::cout << "defHealthCalcFunc" << std::endl;
    return 0;
}

class GameCharacter {
public:
    typedef int (*HealthCalcFunc) (const GameCharacter&);
    explicit GameCharacter(HealthCalcFunc hcf = defHealthCalcFunc):
    healthFunc(hcf)
    {

    };
    int dohealthValue() const {
        std::cout << "Base" << std::endl;
        return healthFunc(*this); //调用defHealthCalcFunc()
    };
private:
    HealthCalcFunc healthFunc;
};



int main() {
    GameCharacter mhero;  //默认构造,传入defHealthCalcFunc函数
    mhero.dohealthValue();
    return 0;
}

通过传入函数指针似乎可以解决一些问题,但传入的函数指针函数,你没办法再函数里面用这个class的private部分参数/函数。解决办法是放弃private属性,或者再public内添加接口供函数指针函数使用。但似乎也带来了额外的开销,具体需要看情况抉择。

  • 用std::function替代------略(理解不清楚)
  • (3)古典策略模式
#include <iostream>
#include <functional>

class GameCharacter;
// int defHealthCalcFunc(const GameCharacter &) {
//     std::cout << "defHealthCalcFunc" << std::endl;
//     return 0;
// }

class HealthCalcFuncBase { //Base
public:
    virtual int calc(const GameCharacter& gc)const {
        return 0;
    };
};

class HealthCalcFuncDerived : public HealthCalcFuncBase{ // Derived
public:
    HealthCalcFuncDerived() {
        std::cout << "HealthCalcFuncDerived start" << std::endl;
    }
    ~HealthCalcFuncDerived() {
        std::cout << "HealthCalcFuncDerived finish" << std::endl;
    }
    virtual int calc(const GameCharacter& gc)const {
        std::cout << "HealthCalcFuncDerived" << std::endl;
        return 0;
    }
};

HealthCalcFuncBase defaultHealthCalc;
 
class GameCharacter {
public:
    explicit GameCharacter(HealthCalcFuncBase* hcf = &defaultHealthCalc)
        :pHealthCalc(hcf) {}
 
    int healthValue() {
        return pHealthCalc->calc(*this);
    }
private:
    HealthCalcFuncBase* pHealthCalc;
};

int main() {
    HealthCalcFuncDerived tmp;
    GameCharacter mhero(&tmp);
    mhero.healthValue();
    return 0;
}

通过HealthCalcFuncDerived继承实现HealthCalcFuncBase,然后再构造GameCharacter()传入HealthCalcFuncDerived tmp;对象,通过多态实现调用HealthCalcFuncDerived的方法,避免了GameCharacter的继承。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

L7256

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值