使用派生类

时间:2016-08-15 14:02:46

标签: c++ templates

我有一个提供纯虚拟接口的基类。我需要它在指向基类的指针列表中存储指向派生类对象的指针。

使用模板机制创建派生类。现在的问题是,如果我想要一个虚拟接口来返回一个只有派生类知道的类型,我还需要将它作为模板参数传递。这就是困境开始的地方......

template <typename ITEM>
class base {
public:
virtual ITEM* get() = 0;
};

template <typename ITEM>
class derived : public base<ITEM>{
public:
ITEM* get() {...};
};

但是当在base中使用模板时,即使在创建基本指针列表时我也需要知道它:

base* myList[10] = {derived1, derived2,...}

当我定义列表时,我当然不知道那种类型。所以我需要以某种方式摆脱基类中的模板。

编辑:摆脱这种方法,因为它根本不是一种有用的方法。所以没有解决这个问题的方法。

1 个答案:

答案 0 :(得分:1)

您编写的代码无效;没有一个base类型,然后像Java一样参数化,但是有许多base<T>类型。有一种方法可以获得真正通用对象的包装器,它被称为“类型擦除”。例如,在boost :: any。

的实现中使用它

基本上,你有一个带有虚函数的非模板基类,然后你创建一个实现它们的模板派生类。请注意,如果您希望拥有base个对象数组,则此处显示的简化版本可以正常工作,因为base具有纯虚函数,因此无法实例化(,因为派生类型的T成员将被切掉)。

struct base;
template<typename T>
struct derived;

struct base {
    virtual ~base();

    // In this class we don't know about T, so we cannot use it
    // Other operations that delegate to the derived class are possible, though
    virtual std::size_t sizeofT() const = 0;
    virtual const std::type_info& typeofT() const = 0;

    // Since all you want is a pointer in "get", you could write it as a void*
    virtual void* getPtr() = 0;

    // Otherwise, we can implement this template function here that calls the virtual.
    // Note that function templates cannot be virtual!
    template<typename U>
    U& getAs() {
        // Verify that the type is the _same_ (no up/downcasts allowed)
        // std::bad_cast is thrown here if U is not the same T used to build this object
        derived<U>& meAsU = dynamic_cast<derived<U>&>(*this);
        return meAsU.obj;
    }
};

template<typename T>
struct derived : public base {
    T obj;
    // A couple of ctors to initialize the object, and the default copy/move ctors/op=
    virtual ~derived();
    derived(const T& o) : obj(o) {}
    derived(T&& o) : obj(std::move(o)) {}

    std::size_t sizeofT() const override {
        return sizeof(T);
    }
    const std::type_info& typeofT() const override {
        return typeid(T);
    }
    void* getPtr() override {
        return static_cast<void*>(&obj);
    }
};

如果您想直接使用base类型作为变量,或者在数组或容器(向量,列表等)中使用,则需要动态分配 - 没有两种方式围绕它。您有两个选择,这些选择在动态分配的责任位置上有所不同:

  • 如果您将指针数组限制为base,则可以使用上述解决方案。例如。一个std::unique_ptr<base>的数组。指向对象的类型为derived<something>

    base err1; // Error, abstract class (what would it contain?)
    base err2 = derived<int>(2); // Still abstract class, and the int would be sliced off
    std::unique_ptr<base> ok(new derived<int>(3)); // Works
    
    std::vector<std::unique_ptr<base>> objects;
    objects.push_back(std::make_unique(new derived<int>(5)));
    objects.push_back(std::make_unique(new derived<std::string>(2)));
    int& a = objects[0].getAs<int>(); // works
    std::string& b = objects[1].getAs<std::string>(); // works too
    std::string& bad = objects[1].getAs<double>(); // exception thrown
    
  • 否则,您必须在基本/派生类本身中实现动态分配。这就像boost :: any或std :: function这样的类。最简单的any对象只是我在这里展示的base类的unique-ptr的包装器,具有适当的运算符实现等。然后,你可以得到一个{{1}类型的变量在构造函数中,类将执行所需的any x = y;