You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

嵌套类设计模式疑问:成员类为何无法像成员函数一样工作

成员类无法像成员函数一样工作的原因及解决方案

问题描述

我想知道为什么成员类不能像成员函数那样工作,以下是最简示例代码:

#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组件但使用方式相同”的场景,常用方案有两种:

  1. 类模板:如上面的方案,将组件类型作为模板参数,封装通用逻辑;
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 06:25:06