我正在编写一个库,其中有一个 BaseClass 类。使用该库的任何人都将创建自己的继承自 BaseClass 的类。我有另一个类,我们称之为 Manager,它保存一个 BaseClass 指针 vector ,它可以保存从 BaseClass 派生的任何对象。

Manager 类必须处理添加到其 BaseClass vector 中的任何对象的创建和销毁。这是因为vector中的任何对象都可以随时删除,Manager本身也可以删除。正因为如此,库的用户不能通过传递一个指向从 BaseClass 派生的现有对象的指针将对象添加到 Manager 的 baseClass vector 。实际上,我可以允许用户这样做。但这将涉及复制一个虚拟对象,我宁愿不这样做。

为了解决这个问题,我正在尝试使用模板函数。当试图将它添加到管理器的 vector 时,用户应该传递从 BaseClass 派生的对象的类型。这就是我目前所拥有的。

//Manager.h
#include <vector>
#include "BaseClass.h"
#include <typeinfo>

class Manager {
    //Vector holding pointers to any objects inherited from BaseClass
    vector<BaseClass*> baseClasses;

    //Template function that needs to add NEW object to baseClass vector
    //This I'm having problems with
    template<class T>
    BaseClass* Add() {
        BaseClass* baseClass = new T();
        baseClasses.push_back(baseClass);
        return baseClass;
    }

    //Template function that gets object from baseClass vector
    //This works fine
    template<class T>
    BaseClass* Get() {
        for (int i = 0; i < baseClasses.size(); i++) {
            if (typeid(*baseClasses[i]) == typeid(T)) {
                return baseClasses[i];
            }
        }
        return NULL;
    }
};

例如,用户应该在从 Manager 的 baseClass vector 中添加或获取对象时执行此操作。 DerivedClass 派生自 BaseClass
Manager manager;
//Add a new DerivedClass object to Manager's vector
manager.Add<DerivedClass>();
//Get a pointer to the DerivedClass object that was just added
DerivedClass* derivedClass = (DerivedClass*)manager.Get<DerivedClass>();

我的 Get() 函数工作正常。我需要知道的是,我怎样才能让我的 Add() 函数工作?帮助将不胜感激。

最佳答案

您的设计有很多不清楚的地方,但是如果问题是您是否可以强制成员函数 templa T 中的类型 Add 可以强制从 BaseClass 派生,则选项很简单:

  • 什么都不做,编译器会很乐意在 BaseClass* baseClass = new T();
  • 线上提示
  • 添加一个静态断言使这更明显
  • 使用花哨的 SFINAE 技巧从重载集中删除函数

  • 我会选择前两个中的任何一个。静态断言可以拼写为:
    static_assert(std::is_base_of<BaseClass,T>::value);
    

    SFINAE 技巧,我真的会避免它,因为它会使代码更加困惑,但可以实现为:
    template <class T>
    typename std::enable_if<std::is_base_of<BaseClass,T>::value,T*>::type
    Add() {
       baseClasses.push_back(new T());
       return baseClasses.back();
    }
    

    (请注意,我将返回类型更改为 T* ,对象是 T ,为什么返回 BaseClass* ?同样适用于 Get 函数,当您知道对象确实是 BaseClass* 时,返回 T 没有意义)

    现在实际问题要复杂得多,因为您的设计正在积极避免所有权的考虑,而您不应该这样做。考虑对象的所有权并确保有明确的所有者(或资源是共享的)。一旦你知道谁拥有这些对象,就为其余的代码创建一个协议(protocol),以在需要删除对象时通知所有者。如果您允许任何代码删除您持有的指针,您很快就会遇到未定义的行为。

    其他较小的问题可能包括您正在强制所有组件都有一个默认构造函数,这可能合适也可能不合适。您可以通过使用带有指针的非模板化 Add 并让调用者按照他们喜欢的方式创建对象来简化此限制。
    typeid 的使用通常是一种代码味道,我不认为这是异常(exception),也许您最好设计一个类型层次结构,您可以询问对象是什么,而不是运行 typeid 。如果你真的下定决心要做基于类型的接口(interface),那么考虑dynamic_cast 是否可以更好。会比较低效,但是如果你有多层继承,它会让你将派生最多的对象作为中间对象返回

    关于带有派生类的 C++ 模板,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/19824070/

    10-13 08:22