嵌套类设计模式疑问:成员类为何无法像成员函数一样工作
问题描述
我想知道为什么成员类不能像成员函数那样工作,以下是最简示例代码:
#include <iostream> class Base { public: Base ():my_stuff () { }; struct Stuff { Stuff () { std::cout << "Base created Stuff" << std::endl; }; }; Stuff my_stuff; }; class Derived: Base { public: struct Stuff { Stuff () { std::cout << "Derived created Stuff" << std::endl; }; }; }; int main () { Base the_base; Derived the_derived; return (0); }
实际输出
Base created Stuff
Base created Stuff
期望输出
Base created Stuff
Derived created Stuff
除非显式重载构造函数,否则这类示例都会有相同表现,但我有大量派生类,不想这么做。我怀疑没有解决方案,不然编译器应该支持“virtual class”说明符——这正是我想要的。我知道这听起来不太合理,但Python支持类似特性,我刚从Python转过来。
补充问题
相关类A、B、C分别使用组件X、Y、Z,但使用方式完全相同,这种场景下的最佳实践或设计模式是什么?以迭代器为例,每个标准容器都实现了自己的迭代器,那是否存在一个公共容器基类包含迭代器基类成员?该如何实现?
为什么成员类不会自动覆写
C++中,类的成员类型(比如示例里的Stuff结构体)不存在“虚类型”概念,也不会像虚函数那样被派生类的同名类型自动覆写。Base类中的my_stuff成员类型在编译期就已确定为Base::Stuff,不管派生类有没有定义同名的Stuff,Derived对象初始化时会先构造基类Base的部分,基类里的my_stuff必然是Base::Stuff类型,所以会输出对应的构造信息。
解决方案:用模板或策略模式实现类型替换
方案1:类模板(编译期多态)
把Base改成模板类,将Stuff类型作为模板参数传入,派生类可以直接指定自己的Stuff类型,无需重复编写构造函数:
#include <iostream> template <typename StuffType> class Base { public: Base() : my_stuff() {} StuffType my_stuff; }; struct BaseStuff { BaseStuff() { std::cout << "Base created Stuff" << std::endl; } }; struct DerivedStuff { DerivedStuff() { std::cout << "Derived created Stuff" << std::endl; } }; // 基础类型绑定BaseStuff using Base = Base<BaseStuff>; // 派生类继承绑定DerivedStuff的Base模板实例 class Derived : public Base<DerivedStuff> {}; int main() { Base the_base; Derived the_derived; return 0; }
运行后输出会符合预期,新增派生类只需定义自己的Stuff类型,再继承对应模板实例即可。
方案2:策略模式+虚函数(运行时多态)
如果需要运行时动态切换类型,可以结合虚函数和组合实现:
#include <iostream> #include <memory> // 抽象Stuff接口 class AbstractStuff { public: virtual ~AbstractStuff() = default; virtual void print() const = 0; }; class BaseStuff : public AbstractStuff { public: void print() const override { std::cout << "Base created Stuff" << std::endl; } }; class DerivedStuff : public AbstractStuff { public: void print() const override { std::cout << "Derived created Stuff" << std::endl; } }; class Base { protected: std::unique_ptr<AbstractStuff> my_stuff; explicit Base(std::unique_ptr<AbstractStuff> stuff) : my_stuff(std::move(stuff)) {} public: void output() const { my_stuff->print(); } }; class Derived : public Base { public: Derived() : Base(std::make_unique<DerivedStuff>()) {} }; class OriginalBase : public Base { public: OriginalBase() : Base(std::make_unique<BaseStuff>()) {} }; int main() { OriginalBase the_base; the_base.output(); Derived the_derived; the_derived.output(); return 0; }
这种方式适合需要运行时多态的场景,但需要管理动态内存,开销比模板方案大。
补充问题的最佳实践
对于“A、B、C类分别用X、Y、Z组件但使用方式相同”的场景,常用方案有两种:
- 类模板:如上面的方案,将组件类型作为模板参数,封装通用逻辑;
- CRTP(奇异递归模板模式):利用模板让基类访问派生类的类型或成员,实现编译期多态,避免虚函数开销;
以标准容器的迭代器为例,C++标准库没有公共容器基类包含迭代器基类——标准库更倾向于编译期多态(模板)而非运行时多态(虚函数)。每个容器的迭代器都是各自定义的类型,通过std::begin、std::end等模板函数统一访问,这种方式效率更高。
如果要实现类似结构,可以用模板封装通用逻辑:
#include <iostream> // 通用遍历逻辑模板 template <typename Container> void iterate(const Container& c) { using Iterator = typename Container::iterator; for (Iterator it = c.begin(); it != c.end(); ++it) { std::cout << *it << " "; } std::cout << std::endl; } // 自定义容器1,自带迭代器 class MyContainer1 { public: using iterator = int*; iterator begin() { return data; } iterator end() { return data + 3; } private: int data[3] = {1,2,3}; }; // 自定义容器2,自带迭代器 class MyContainer2 { public: using iterator = double*; iterator begin() { return data; } iterator end() { return data + 2; } private: double data[2] = {4.5, 6.7}; }; int main() { MyContainer1 c1; iterate(c1); // 输出:1 2 3 MyContainer2 c2; iterate(c2); // 输出:4.5 6.7 return 0; }
内容的提问来源于stack exchange,提问作者yetanotherus3r

