基类通过虚方法访问派生类成员的实现是否符合最佳实践?求常见模式
基类调用派生类虚方法的实现合理性与设计模式分析
问题背景
以下C++代码运行符合预期:基类B、C通过纯虚方法get_value(),成功访问到派生类D中声明的m_value成员变量,所有输出结果均为10。
示例代码
class A_abstract { protected: [[nodiscard]] virtual int get_value() const noexcept = 0; }; class B: A_abstract { protected: [[nodiscard]] int get_value_B1() const noexcept { return get_value(); } public: [[nodiscard]] int get_value_B2() const noexcept { return get_value(); } }; class C: A_abstract { protected: [[nodiscard]] int get_value_C1() const noexcept { return get_value(); } public: [[nodiscard]] int get_value_C2() const noexcept { return get_value(); } }; class D: public B, public C { protected: int m_value = 10; [[nodiscard]] int get_value() const noexcept override { return m_value; } public: [[nodiscard]] int get_value_1() const noexcept { return m_value; } [[nodiscard]] int get_value_2() const noexcept { return get_value(); } [[nodiscard]] int get_value_3() const noexcept { return get_value_B1(); } [[nodiscard]] int get_value_4() const noexcept { return get_value_C1(); } [[nodiscard]] int get_value_5() const noexcept { return get_value_B2(); } [[nodiscard]] int get_value_6() const noexcept { return get_value_C2(); } }; int main(const int argc, const char** argv) { D instance; std::cout << "get_value_1() = " << instance.get_value_1() << '\n'; std::cout << "get_value_2() = " << instance.get_value_2() << '\n'; std::cout << "get_value_3() = " << instance.get_value_3() << '\n'; std::cout << "get_value_4() = " << instance.get_value_4() << '\n'; std::cout << "get_value_5() = " << instance.get_value_5() << '\n'; std::cout << "get_value_6() = " << instance.get_value_6() << '\n'; std::cout << "get_value_B2() = " << instance.get_value_B2() << '\n'; std::cout << "get_value_C2() = " << instance.get_value_C2() << '\n'; }
输出结果
get_value_1() = 10 get_value_2() = 10 get_value_3() = 10 get_value_4() = 10 get_value_5() = 10 get_value_6() = 10 get_value_B2() = 10 get_value_C2() = 10
核心问题解答
1. 该实现从最佳实践角度是否合理?
这个实现虽然能正常运行,但存在多处违背C++最佳实践的问题:
- 菱形继承隐患:A_abstract被B、C非虚拟继承,D多重继承后会产生A_abstract的两个独立实例。即便当前A是纯虚类无成员数据,后续若给A添加变量,会直接导致数据冗余、访问歧义,维护成本陡增。
- 依赖关系倒置违规:基类B、C的方法直接依赖派生类D对
get_value()的实现,违反依赖倒置原则——高层模块不应直接依赖低层模块,二者都应依赖抽象接口。这种设计会让B、C无法脱离D独立复用,耦合度过高。 - 接口冗余臃肿:D类中定义了大量重复逻辑的方法(get_value_1到get_value_6),外部还可直接调用B、C的public方法,既增加了代码量,又容易让使用者混淆接口意图,后期维护易出错。
2. 此类场景的常见设计模式是什么?
这种基类定义框架逻辑、派生类实现具体细节的场景,最贴合的是模板方法模式:
- 模板方法模式的核心是在抽象基类中定义算法骨架(比如B的get_value_B1/B2就是骨架中的固定步骤),将可变的具体实现(比如获取值的逻辑)延迟到派生类中完成(D的get_value())。
- 如果需要进一步降低耦合,也可结合依赖注入思想:让基类通过构造函数或参数接收一个提供数据的抽象对象,而非直接依赖派生类的虚方法,这种方式更灵活,也符合单一职责原则。
内容的提问来源于stack exchange,提问作者canonier
相关产品推荐
相关产品推荐

