2011-03-20 65 views
5

比方说,我们有一个类层次结构,其中有一个通用的Animal类,它有几个直接从它继承的类(如DogCat,Horse等)。模板中的多态类

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

例如,假设我们有一个模板化的Stack类,我们想要承载各种动物。我可以简单地指出Stack<Animal> s; Dog d; s.push(d); Cat c; s.push(c);

回答

5

回答你的问题,如果不可以,但你可以使用SomeTemplateClass<Animal*>并通过派生类对象的指针给它。

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

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

对于指针容器,我只能推荐使用Boost指针容器作为基础。它就像智能指针一样,但是对于多个对象以及STL所期望的通常混合的属性。 – 2011-03-20 16:33:00

3

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

容器可能分配的空间只能容纳一个Animal。如果一个Dog比这个大,那么容器会尝试复制构建一个Dog,这个Dog被放入太小的空间中,可能会导致内存损坏。

0

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

你甚至不能在Stack<Animal>Stack<const Animal>之间施放。

编辑:但@Mihran指出,你可以尝试使用Stack<Animal* >代替Stack<Animal>

0

它取决于模板用于传递类型的用途。如果你指的是标准容器(例如std::vector,std::map等),那么答案是否定的。在std::vector<Animal>std::vector<Dog>之间没有任何关系,即使在你的阶级层次结构中,狗来源于动物。

你不能把一个Dogstd::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例外。