2

抱歉,如果我在这里遗漏了一个相当基本的概念,但我正在尝试解决如何维护多个类类型的集合(所有类都派生自同一个父类),并且在检索它们时仍然可以访问它们的子类特定方法从收藏。

作为上下文,我有一个基类(“BaseClass”)和许多类(比如“SubClassA”到“SubClassZ”),它们都派生自这个基类。我还需要维护一个包含所有子类对象的中央索引集合,这些对象是我创建的

typedef unordered_map<std::string, BaseClass*> ItemCollectionType;
ItemCollectionType Items;

该列表将只包含子类对象,但我已将列表成员类型定义为“BaseClass”指针,以便它可以存储任何子类的实例。到目前为止,我假设这种方法没有什么特别糟糕的(但如果我错了,请纠正我)。

我遇到的问题是,有时我需要创建这些集合及其内容的副本。我为此拥有的(简化)代码如下:

ItemCollectionType CopiedItems;
ItemCollectionType::const_iterator it_end = Items.end();

for (ItemCollectionType::const_iterator it = Items.begin(); it != it_end; ++it) 
{
    BaseClass *src = it->second;
    BaseClass *item = new BaseClass(src);
    NewItems[item->code] = item;
}

这里明显的问题是编译器只会调用 BaseClass 复制构造函数,而不是“src”实际指向的 SubClass,因为它不知道它是什么类型的 SubClass。

对于这种情况,推荐的方法是什么?我是否应该通过添加到 BaseClass 类型的集合来“丢失”这个子类类型信息?我看不到任何其他方式来维护收藏,但请让我知道任何更好的选择。

每个 SubClass 对象都包含一个数字 ID,表示它是哪种风格的子类;因此,是否可以编写一个函数将该数字 ID 转换为“类型”对象,然后在调用复制构造函数之前使用该对象转换源对象?这是模板的有效用途吗?

当然,这可能是糟糕的编程实践和/或不可能。我只是确定应该有比最简单和最难维护的解决方案更好的选择

switch (src->code)
{
    case SubClassTypes::VarietyA:
        SubClassA *item = new SubClassA( *((SubClassA*)src) );
        Items[item->code] = item;
        break;
    case SubClassTypes::VarietyB:
        SubClassB *item = new SubClassB( *((SubClassB*)src) );
        Items[item->code] = item;
        break;
    ...
    ...
}
4

2 回答 2

3

您可以定义Clone将在每个子类中实现的抽象方法。

virtual BaseClass* Clone() = 0;

不仅仅是打电话:

for (ItemCollectionType::const_iterator it = Items.begin(); it != it_end; ++it) 
{
    BaseClass *src = it->second;
    NewItems[item->code] = src->Clone();
}

另请注意,C++ 支持返回值协方差,因此您的派生类可以返回确切的类型,而不仅仅是基类,例如

// in DerivedClass (which derives from BaseClass)
DerivedClass* Clone() { ... } 

尽管返回类型与基类中的不同,但这被认为是适当的覆盖。

基于属性将基类指针转换为派生类不被认为是良好的 OOP 实践。

于 2012-09-16T13:59:06.827 回答
2

在您的子类中创建一个新函数(也许在您的基类中使其成为纯虚拟函数),类似于以下内容,

BaseClass * SubClassA::copy(){ /* Copy construtor like code. */ }

当您调用 BaseClass->copy() 并在子类上调用复制构造函数时,这将调用动态绑定。

于 2012-09-16T13:58:32.460 回答