0

我正面临设计问题,可以通过一些外部输入来解决。我试图避免抽象基类转换(因为我听说这很糟糕)。

问题归结于这种结构:

class entity... (base with pure virtual functions)
class hostile : public entity... (base with pure virtual functions)
class friendly : public entity... (base with pure virtual functions)
// Then further derived classes using these last base classes... 

最初我以为我会逃脱:

const enum class FactionType : unsigned int
{ ... };

std::unordered_map<FactionType, std::vector<std::unique_ptr<CEntity>>> m_entitys;

并且...我做到了,但这会给我带来问题,因为我需要专门从敌对或友好的角度访问“独特”功能。

我已经不光彩地尝试过(工作但不喜欢它也不觉得安全):

// For-Each Loop: const auto& friendly : m_entitys[FactionType::FRIENDLY]
CFriendly* castFriendly = static_cast<CFriendly*>(&*friendly);

我希望/试图维护用作基本抽象类类型的键的unordered_map设计......无论如何,非常感谢输入。FactionType

如果有任何语法错误,我深表歉意。

4

1 回答 1

0

关于选角我同意@rufflewind。演员表意味着不同的东西,并且在不同的时间有用。

要在编译时强制一个内存区域(无论如何,键入的决定发生在编译时)使用 static_cast。T* 另一端的内存量等于 sizeof(T) 将被解释为 T,而不管行为是否正确。

dynamic_cast 的决定完全在运行时做出,有时需要 RTTI(运行时类型信息)。它做出一个决定,如果可以的话,它将返回一个空指针或一个指向 T 的有效指针。


不过,这个决定不仅仅是演员表的类型。使用数据结构来查找类型和方法(成员函数)会施加时间限制,与相对快速和强制的强制转换相比,这些时间限制本来不会存在。有一种方法可以跳过数据结构,但如果没有重大重构(通过重大重构你可以做任何事情)就不能跳过。

您可以将强制转换移动到实体类中,正确完成它们,然后将它们封装在那里。

class entity
{
    // Previous code
    public:

    // This will be overridden in hostiles to return a valid 
    // pointer and nullptr or 0 in other types of entities
    virtual hostile* cast_to_hostile() = 0
    virtual const hostile* cast_to_hostile() const = 0

    // This will be overridden in friendlies to return a valid 
    // pointer and nullptr or 0 in other types of entities
    virtual friendly* cast_to_friendly() = 0
    virtual const friendly* cast_to_friendly() const = 0


    // The following helper methods are optional but
    // can make it easier to write streamlined code in
    // calling classes with a little trouble.

    // Hostile and friendly can each implement this to return
    // The appropriate enum member. This would useful for making
    // decision about friendlies and hostiles
    virtual FactionType entity_type() const = 0;

    // These two method delegate storage of the knowledge
    // of hostility or friendliness to the derived classes.
    // These are implemented here as non-virtual functions
    // because they shouldn't need to be overridden, but 
    // could be made virtual at the cost of a pointer
    // indirection and sometimes, but not often a cache miss.
    bool is_friendly() const
    {
        return entity_type() == FactionType_friendly;
    }
    bool is_hostile() const
    {
        return entity_type() == FactionType_hostile;
    }
}

由于各种原因,这种策略有好有坏。

优点:

它在概念上很简单。如果您了解多态性,这很容易快速理解。

它似乎与您现有的代码相似,似乎与您现有的代码表面上相似,从而使迁移更容易。你的类型中包含敌意和友好是有原因的,这保留了这个原因。

您可以安全地使用 static_casts,因为所有类型转换都存在于使用它们的类中,因此除非有效,否则通常不会被调用。

您可以返回 shared_ptr 或其他自定义智能指针而不是原始指针。你可能应该。

这避免了完全避免强制转换的潜在成本高昂的重构。铸造在那里被用作工具。

缺点:

它在概念上很简单。这并没有提供一套强大的词汇表(方法、类和模式)来构建一套智能工具来构建高级类型机制。

很可能某物是否敌对应该是数据成员或实现为控制实例行为的一系列方法。

有人可能会认为 this 返回的指针传达了所有权并删除了它们。

每个调用者必须在使用前检查指针的有效性。或者你可以添加方法来检查,但是调用者需要在强制转换之前调用方法来检查。像这样的检查对于类的用户来说是令人惊讶的,并且使得它更难正确使用。

它是多态密集的。这会让那些对多态性感到不舒服的人感到困惑。即使在今天,仍有许多人对多态性感到不舒服。

完全避免强制转换的重构是可能的。铸造是危险的,不是随便使用的工具。

于 2015-02-15T05:54:57.457 回答