模板中的多态类

时间:2011-03-20 14:51:18

标签: c++ templates inheritance

假设我们有一个类层次结构,我们有一个通用的Animal类,它有几个直接从它继承的类(例如DogCatHorse等等。)。

在此继承层次结构上使用模板时,使用SomeTemplateClass<Animal>然后将Dogs and Cats and Horses推入此模板化对象是否合法?

例如,假设我们有一个模板Stack类,我们想要托管各种动物。我可以简单地说明Stack<Animal> s; Dog d; s.push(d); Cat c; s.push(c);

4 个答案:

答案 0 :(得分:5)

如果是,请回答您的问题。但您可以使用SomeTemplateClass<Animal*>并将派生类对象的指针传递给它。

例如,如果你有一个模板化的Stack类,你想要托管各种动物。您可以简单地执行以下操作:

Stack<Animal*> s; 
Dog d; 
s.push(&d); 
Cat c; 
s.push(&c)

答案 1 :(得分:3)

不,你必须使用指针,即Stack<Animal*>(或某种智能指针)。原因是DogCatHorse等不一定是相同的大小,因为它们可能会添加成员变量。

容器可能会分配足够大的空间来存储Animal。如果Dog大于Dog,则容器将尝试复制构造{{1}},该{{1}}在太小的空间中被推入其中,可能导致内存损坏。

答案 2 :(得分:0)

Stack<Animal>Stack<Dog>是完全不同的类。

您甚至无法在Stack<Animal>Stack<const Animal>之间进行投射。

编辑:但正如@Mihran指出的那样,您可以尝试使用Stack<Animal* >代替Stack<Animal>

答案 3 :(得分:0)

这取决于模板对传递类型的用处。如果您指的是标准容器(例如std::vectorstd::map等),那么答案就是否定。 std::vector<Animal>std::vector<Dog>之间没有任何关系,即使你的班级等级中的狗来自动物。

你不能在Dog中放置std::vector<Animal> ... C ++使用复制语义,你会在所谓的“slicing”中发出意味着你的Dog实例将丢失任何不存在于基础Animal类中的成员。

然而,一般来说,模板很可能以不同的方式使用该类型,因此允许接受派生类的实例。例如,在以下代码中,模板MethodCaller可以使用类型实例化,但使用派生类型的实例并正确处理后期绑定调度。这是可能的,因为MethodCaller实例仅包含引用,并且不会复制该对象。

#include <stdio.h>

template<typename T>
struct MethodCaller
{
    T& t;
    void (T::*method)();
    MethodCaller(T& t, void (T::*method)())
        : t(t), method(method)
    {}
    void operator()() { (t.*method)(); }
};

struct Animal { virtual void talk() = 0; };
struct Dog : Animal { virtual void talk() { printf("Bark\n"); } };
struct Cat : Animal { virtual void talk() { printf("Meow\n"); } };
struct Crocodile : Animal { virtual void talk() { printf("??\n"); } };

void makenoise(Animal *a)
{
    MethodCaller<Animal> noise(*a, &Animal::talk);
    noise(); noise(); noise();
}

int main()
{
    Dog doggie;
    Cat kitten;
    Crocodile cocco;
    makenoise(&doggie);
    makenoise(&kitten);
    makenoise(&cocco);
}

也可以根据需要实现Stack课程。

#include <vector>

template<typename T>
struct Stack
{
    std::vector<T *> content;
    ~Stack()
    {
        for (int i=0,n=content.size(); i<n; i++)
            delete content[i];
    }

    template<class S>
    void push(const S& s)
    {
        content.push_back(new S(s));
    }

    template<class S>
    S pop()
    {
        S result(dynamic_cast<S&>(*content.back()));
        content.pop_back();
        return result;
    }

private:
    // Taboo
    Stack(const Stack&);
    Stack& operator=(const Stack&);
};

int main()
{
    Dog doggie;
    Cat kitten;
    Crocodile cocco;

    Stack<Animal> s;
    s.push(doggie);
    s.push(kitten);
    s.push(cocco);

    Crocodile cocco2 = s.pop<Crocodile>();
    Cat kitten2 = s.pop<Cat>();
    Dog doggie2 = s.pop<Dog>();
}

请注意,在实现中,我使用std::vector指针保留给动物,从而避免切片问题。我一直在使用模板方法来接受推送调用中的派生类型。

另请注意,当弹出动物时,你必须提供什么是类,如果它是错误的(例如,当堆栈中的顶部元素是Crocodile时弹出Dog),你将在运行时获得bad_cast异常。