【问题标题】:Vector of templates -> use member of child class模板向量 -> 使用子类的成员
【发布时间】:2016-01-26 08:35:14
【问题描述】:

我想创建一个模板项向量

我的做法是:

class InterfaceItem
{
}

template <typename T>
class Item : public InterfaceItem
{
 T value;
 T GetValue();
}

std::vector<InterfaceItem*> items;
items.push_back(new Item<int>());

这种方法到目前为止有效,但现在我遇到了访问我的项目价值的问题。最好的方法是什么?

我曾考虑将子类的类型保存在基类中,然后强制转换为该子类,但我只能使用默认类型或已知类型。

你的方法是什么

auto value = items[0].GetValue(); // will not compile

【问题讨论】:

  • 你似乎完全误解了继承的目的。顾名思义,您的InterfaceItem 应该提供一个接口。
  • 在访问派生类型的成员之前,您需要将InterfaceItem* 转换为其派生类型。
  • @HappyCoder 他不应该这样做并改正他的设计。
  • 是的,这是正确的方法。我只是在说明访问派生类型成员的正确方法。
  • 我想在同一个向量中存储多种类型的项目,否则没问题

标签: c++ templates vector


【解决方案1】:

访问我的物品价值时出现问题。最好的方法是什么?

与继承一起使用的典型设计是通过接口访问值。要通过接口访问某些东西,它必须有一个成员函数。并且为了从接口获得多态行为,成员函数应该是虚拟的。而且,由于接口本身不能实现成员函数,而任何派生类都应该实现,所以它应该是一个纯虚成员函数。此外,如果您打算使用基指针存储派生对象,请不要忘记使用虚拟析构函数。这是一个例子:

class InterfaceItem {
    virtual int GetValue() = 0;
    virtual ~InterfaceItem();
};

class Item: public InterfaceItem {
    int GetValue() override;
}

现在,这将起作用:

auto value = items[0]->GetValue();

请注意,接口函数未模板化。编译时多态性(模板)和运行时多态性(继承)并不总是混合得很好。您不能覆盖模板函数。通常,您必须选择要使用的多态类型。

【讨论】:

  • OP说补充说vector可能包含多种类型的项目。所以这种方法不起作用,因为GetValue 现在被硬编码为int。 (但他/她可能误解了auto 的工作原理)。
  • @user694733 好吧,OP 可以将向量中的指针分配给派生InterfaceItem 的不同类型就好了。但是如果他们想从GetValue 获得不同的类型,那么他们想要的东西在静态类型语言中就没有意义了。他们正在尝试重新实现 Boost.Any 或 Boost.Variant,但在不知道他们需要它的情况下,我怀疑他们所拥有的是 XY 问题。
【解决方案2】:

在这种情况下,您不需要继承。只需这样做:

#include <vector>

template <typename T>
struct Item {
    T value;
    T GetValue(){return value;}
};

int main() {
    std::vector<Item<int>*> items;
    items.push_back(new Item<int>());
    auto value = items[0]->GetValue(); // will compile
}

不要忘记删除项目,或者使用std::unique_ptr&lt;&gt;的向量

【讨论】:

    【解决方案3】:

    你可以做一些访客模式。

    首先,创建一个可以访问您将要使用的所有类型的基本访问者类:

    class ItemVisitor {
    public:
         virtual void visit (int a) = 0;
         virtual void visit (std::string a) = 0;
         virtual void visit (double a) = 0;
    };
    

    然后让你的InterfaceItem接受ItemVisitors:

    class InterfaceItem
    {
    public:
        virtual void accept(ItemVisitor& visitor) = 0;
    };
    

    现在不使用GetValue,而是在Item 内部的访问者上调用visit

    template <typename T>
    class Item : public InterfaceItem
    {
    public:
        Item (T t) : value{std::move(t)} {}
        void accept(ItemVisitor& visitor) override { visitor.visit(value); }
    
    private:
        T value;
    };
    

    现在当你想访问你的项目时,写一个访问者来完成所有的工作:

    class PrintVisitor : public ItemVisitor {
        void visit (int a) override { std::cout << "int " << a << '\n'; }
        void visit (std::string a) override { std::cout << "std::string " << a << '\n'; }
        void visit (double a) override { std::cout << "double " << a << '\n'; } 
    };
    

    然后创建一个访问者实例并使用它来访问具有已擦除类型的元素:

    int main() {
        std::vector<InterfaceItem*> items;
        items.push_back(new Item<int>(42));
        items.push_back(new Item<std::string>("Hi there"));
        items.push_back(new Item<double>(42.12));
        items.push_back(new Item<int>(24));
    
        PrintVisitor visitor{};
    
        for (auto i : items) {
            i->accept(visitor);
        }
    }
    

    这将为每个项目在PrintVisitor 内调用visit 的正确重载。

    Live Demo

    请注意,您应该为向量使用std::unique_ptr,而不是原始指针。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-07-25
      • 2012-05-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-31
      相关资源
      最近更新 更多