0

假设我有以下数据结构:

struct Base
{
    Base(const int id, const std::string &name, const std::string &category):
        id(id), name(name), category(category) {}

    int         id;
    std::string name;
    std::string category;
};

struct A : public Base
{
    A(const int id, const std::string &name, const std::string &category,
      const int x, const int y) :
     Base(id, name, category), x(x), y(y) {}
    int x, y;
};

我想创建一个返回派生类向量的单一工厂方法,其中 id、名称和类别在函数中是已知的。我遇到的问题是切片......

std::vector< Base* > getVector(...)

结构 A 的数据成员丢失了!(dynamic_cast 回 A 在生产代码中可以接受?)

所以我有这个模板方法,但我仍然认为它不是最好的解决方案:

template< class T >
std::vector< T > getVector()
{
    std::vector< T > retVal;

    retVal.push_back(T(45, "The Matrix", "Science Fiction"));
    retVal.push_back(T(45, "Good Luck Chuck", "Comedy"));
    ...
    return retVal;
}

除了模板方法,还有更好的解决方案吗?

4

2 回答 2

1

你所要求的似乎有问题,因为你想要:

  • 抽象对象创建直到运行时
  • 在编译时创建对象后访问特定成员

所以没有办法让它体面地工作。您的模板代码仍然需要在调用者站点静态知道对象的类型,因此对象的创建不会被抽象掉。

我的建议是重新考虑你的问题:你为什么想要一个动态工厂?为什么Base类中的所有对象不能共享访问器?

如果你不能清楚地回答这两个问题,那可能意味着你一开始就不应该有这个类层次结构。

于 2013-02-18T22:50:09.343 回答
1

我实际上认为您的模板可能是最好的解决方案,至少它是惯用的 C++。

使用 RTTIdynamic_cast也可以,但它的安全性较低(运行时与编译时类型检查)并且通常效率较低。然而,有时——或者实际上经常——你只需要在运行时决定多态性(例如,当你需要不同的派生对象std::vector并且不能使用固定长度时std::tuple)。然后,您可以像您已经准备好的那样,通过使用基类指针向量而不是对象来解决切片问题。(普通)指针的问题在于它们在某种程度上规避了 C++ 的自动内存管理:当std::vector<Base*>超出范围时,指向的派生对象不会留在人迹罕至的地方闲逛而被删除:您有内存泄漏。这就是为什么像 Java 这样更多地依赖这种使用继承方式的语言会被垃圾回收。这个问题在别处讨论过,推荐的解决方案是用Base*替换std::unique_ptr<Base>

一旦你运行了这个,你就可以使用运行时多态函数。您通常不需要dynamic_cast,而是将不同派生实例不同的所有内容编写为虚拟成员函数。

于 2013-02-18T22:58:40.780 回答