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

如何实现非模板Aquarium类存储泛型模板Fish子类?

解决方案

核心问题分析

原代码中Fish<T>是模板类,不同T对应的Fish<T>是完全独立的类型,无法直接存入同一容器。因此必须先抽离出非模板的抽象基类,让所有鱼类共享统一接口,这样非模板的Aquarium才能通过多态指针存储各类鱼。


问题解答

  1. 存储方式

    • 定义非模板抽象基类AbstractFish,包含所有鱼类的公共虚接口(如swim()、虚析构函数);
    • 让原模板类Fish<T>继承AbstractFish,保留其特有的模板逻辑;
    • Aquarium内部使用std::vector<std::shared_ptr<AbstractFish>>存储鱼类,借助智能指针自动管理内存,同时支持多态调用。
  2. addFish/removeFish的参数与返回值

    • addFish:实现为模板方法,参数接受std::shared_ptr<FishDerived>(任意Fish派生类的智能指针),通过向上转换存入容器;
    • removeFish:提供两种版本:
      • 模板版本:返回指定类型的std::shared_ptr<FishDerived>,通过dynamic_pointer_cast做类型安全检查;
      • 基类版本:返回std::shared_ptr<AbstractFish>,适用于不需要具体类型的场景。

完整实现代码

#include <iostream>
#include <vector>
#include <memory>
#include <stdexcept>

// 非模板抽象基类:所有鱼类的统一接口
class AbstractFish {
public:
    virtual ~AbstractFish() = default;
    virtual void swim() const = 0;
    virtual void breathe() const {
        std::cout << "Fish is breathing." << std::endl;
    }
};

// 模板化Fish类,继承自AbstractFish
template<typename T>
class Fish : public AbstractFish {
public:
    void breathe() const override {
        std::cout << "Fish<T> is breathing with value = " << T{} << std::endl;
    }
};

// 模板化Salmon类
template<typename T>
class Salmon : public Fish<T> {
public:
    void swim() const override {
        std::cout << "Salmon is swimming with value = " << T{} << std::endl;
    }
};

// 模板化Goldfish类
template<typename T>
class Goldfish : public Fish<T> {
public:
    void swim() const override {
        std::cout << "Goldfish is swimming with value = " << T{} << std::endl;
    }
};

// 非模板化Aquarium类
class Aquarium {
private:
    std::vector<std::shared_ptr<AbstractFish>> fishTank;

public:
    // 添加任意类型的鱼
    template<typename FishDerived>
    void addFish(std::shared_ptr<FishDerived> fish) {
        fishTank.push_back(std::static_pointer_cast<AbstractFish>(fish));
    }

    // 移除并返回指定类型的第一条鱼
    template<typename FishDerived>
    std::shared_ptr<FishDerived> removeFish() {
        for (auto it = fishTank.begin(); it != fishTank.end(); ++it) {
            auto derivedFish = std::dynamic_pointer_cast<FishDerived>(*it);
            if (derivedFish) {
                fishTank.erase(it);
                return derivedFish;
            }
        }
        throw std::runtime_error("该类型的鱼不存在");
    }

    // 移除并返回任意一条鱼
    std::shared_ptr<AbstractFish> removeAnyFish() {
        if (fishTank.empty()) {
            throw std::runtime_error("水族箱是空的");
        }
        auto fish = fishTank.back();
        fishTank.pop_back();
        return fish;
    }

    // 让所有鱼游泳
    void makeFishSwim() {
        for (const auto& fish : fishTank) {
            fish->swim();
        }
    }

    // 获取当前鱼的数量
    size_t getFishCount() const {
        return fishTank.size();
    }
};

int main() {
    Aquarium aquarium;

    // 创建不同类型的鱼
    auto salmonInt = std::make_shared<Salmon<int>>();
    auto goldfishDouble = std::make_shared<Goldfish<double>>();
    auto salmonString = std::make_shared<Salmon<std::string>>();

    // 添加到水族箱
    aquarium.addFish(salmonInt);
    aquarium.addFish(goldfishDouble);
    aquarium.addFish(salmonString);

    std::cout << "所有鱼开始游泳:" << std::endl;
    aquarium.makeFishSwim();

    // 移除一条Salmon<int>
    auto removedSalmon = aquarium.removeFish<Salmon<int>>();
    std::cout << "\n移除了Salmon<int>,剩余鱼数量:" << aquarium.getFishCount() << std::endl;

    // 移除任意一条鱼
    auto removedAny = aquarium.removeAnyFish();
    std::cout << "移除了一条鱼,剩余鱼数量:" << aquarium.getFishCount() << std::endl;

    return 0;
}

关键细节说明

  • 抽象基类的作用:AbstractFish是实现多态的核心,让不同模板实例的鱼类能被统一管理;
  • 智能指针选型:std::shared_ptr自动处理对象生命周期,避免手动内存管理的风险;
  • 类型转换安全:static_pointer_cast用于安全的向上转换,dynamic_pointer_cast在向下转换时做类型校验,确保移除操作的类型安全;
  • 接口灵活性:同时支持按类型和任意移除,适配不同业务场景需求。

内容的提问来源于stack exchange,提问作者Moish

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 16:55:12