【问题标题】:Polymorphic classes in templates模板中的多态类
【发布时间】:2011-03-20 14:51:17
【问题描述】:

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

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

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

【问题讨论】:

    标签: c++ templates inheritance


    【解决方案1】:

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

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

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

    【讨论】:

    • 对于指针容器,我只能推荐使用 Boost Pointer Containers 作为基础。它类似于智能指针,但适用于多个对象,并且具有您对 STL 所期望的属性的通常组合。
    【解决方案2】:

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

    容器可能分配的空间仅足以存储Animal。如果Dog 大于此值,则容器将尝试复制构造一个Dog,该Dog 在太小的空间内被推入其中,可能会导致内存损坏。

    【讨论】:

      【解决方案3】:

      NO Stack&lt;Animal&gt;Stack&lt;Dog&gt; 是完全不同的类。

      你甚至不能在 Stack&lt;Animal&gt;Stack&lt;const Animal&gt; 之间转换。

      编辑:但正如@Mihran 指出的那样,您可以尝试使用Stack&lt;Animal* &gt; 而不是Stack&lt;Animal&gt;

      【讨论】:

        【解决方案4】:

        这取决于模板对传递的类型的用途。如果您指的是标准容器(例如std::vectorstd::map 等),那么答案是否定的。 std::vector&lt;Animal&gt;std::vector&lt;Dog&gt; 之间根本没有任何关系,即使在您的类层次结构中,狗来自动物。

        您不能将Dog 放在std::vector&lt;Animal&gt; 中... 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 来保持指针 指向动物,从而避免切片问题。我一直在使用模板方法来接受推送调用中的派生类型。

        还要注意,当弹出动物时,您必须提供类是什么,如果它是错误的(例如,当堆栈上的顶部元素是 Dog 时,您会弹出 Crocodile),您将得到 @987654338 @运行时异常。

        【讨论】:

          猜你喜欢
          • 2012-12-26
          • 2010-12-05
          • 1970-01-01
          • 2019-05-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-03-02
          相关资源
          最近更新 更多