模板中的多态类

发布于 2024-10-23 15:42:16 字数 354 浏览 1 评论 0原文

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

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

例如,假设我们有一个模板化的 Stack 类,我们想要在其中托管各种动物。我可以简单地说一下Stack吗? s;狗d; s.push(d);猫c; s.push(c);

Let's say we have a class hierarchy where we have a generic Animal class, which has several classes directly inherit from it (such as Dog, Cat, Horse, etc..).

When using templates on this inheritance hierarchy, is it legal to just use SomeTemplateClass<Animal> and then shove in Dogs and Cats and Horses into this templated object?

For example, assume we have a templated Stack class, where we want to host all sorts of animals. Can I simply state Stack<Animal> s; Dog d; s.push(d); Cat c; s.push(c);

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

寒江雪… 2024-10-30 15:42:16

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

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

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

Answer of your question if No. But you can use SomeTemplateClass<Animal*> and pass pointers of objects of derived classes to it.

For example, if you have a templated Stack class, where you want to host all sorts of animals. You can simply do following:

Stack<Animal*> s; 
Dog d; 
s.push(&d); 
Cat c; 
s.push(&c)
年少掌心 2024-10-30 15:42:16

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

容器可以分配仅足以存储动物的空间。如果 Dog 大于该值,容器将尝试复制构造一个 Dog,并将其推入太小的空间中,从而可能导致内存损坏。

No, you'd have to use pointers, i.e. Stack<Animal*> (or some kind of smart pointer). The reason is that Dog, Cat, Horse etc. are not necessarily the same size, since they might add member variables.

The container may allocate space that is only large enough to store an Animal. If a Dog is larger than that, the container will try to copy-construct a Dog that is pushed into it in too small a space, potentially causing memory corruption.

小嗷兮 2024-10-30 15:42:16

StackStack 是完全不同的类。

您甚至无法在 StackStack 之间进行转换。

编辑:但正如 @Mihran 指出的,您可以尝试使用 Stack 代替 Stack

NO Stack<Animal> and Stack<Dog> are completely different classes.

You can't even cast between Stack<Animal> and Stack<const Animal>.

Edit: But as @Mihran pointed you can try to use Stack<Animal* > instead Stack<Animal>

仲春光 2024-10-30 15:42:16

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

您不能将 Dog 放入 std::vector...C++ 使用复制语义,您会遇到所谓的“切片”,这意味着您的 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 来保存指针/strong> 对动物来说,因此可以避免切片问题。我一直在使用模板方法来接受推送调用中的派生类型。

另请注意,在弹出动物时,您必须提供类是什么以及是否是错误的类(例如,当堆栈顶部元素是 Dog 时,您弹出 Crocodile) )你会在运行时得到一个 bad_cast 异常。

It depends on what use the template does with the passed type. If you mean standard containers (e.g. std::vector, std::map and so on) then the answer is no. There is no relation at all between std::vector<Animal> and std::vector<Dog> even if in your class hierarchy dogs derive from animals.

You cannot put a Dog in an std::vector<Animal>... C++ uses copy semantic and you would incur in what is called "slicing" that means that your Dog instance will lose any member that is not also present in the base Animal class.

However in general it's of course well possible for a template to use the type in different ways that will allow therefore accept instances of derived classes. For example in the following code the template MethodCaller can be instantiated with a type but using an instance of a derived type and correctly handling late binding dispatch. This is possible because the MethodCaller instance only holds a reference and doesn't make a copy of the object.

#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);
}

It is also possible to implement the Stack class as you want...

#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>();
}

Note that in the implementation I've used an std::vector for keeping pointers to animals and therefore to avoid the slicing problem. I've been using a template method to be able to accept derived types in the push call.

Also note that when popping animals you must provide what is the class and if it's a wrong one (e.g. you pop out a Crocodile when the top element on the stack is a Dog) you will get a bad_cast exception at runtime.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文