有限的一组课程的容器

时间:2011-12-09 13:37:17

标签: c++ variables pointers

修改

感谢大家花时间回答我的问题。我完全同意我的问题不清楚。

简而言之:我的问题是如何创建一个有限的指针容器,它只能容纳有限的类组中的类。我称之为 enum ,这是一个很大的错误。正如我所写,多态性不是主要问题,它只是一个例子。

3 个答案:

答案 0 :(得分:2)

编辑这回答了海报的原始问题,而不是当前的问题。享受。

看起来你想要做的就是这个。

class Animal
{
    virtual void jump() = 0;
};

class Dog : public Animal
{
    virtual void jump() { do stuff here; }
    void bark() { other stuff; } // method peculiar to dogs
};

class Cat : public Animal
{
    virtual void jump() { do stuff here; }
};

void main()
{
    Animal *a = new Dog(); // abstract types have to be referred to by pointers
    a->jump();
    delete a;

    Dog d;
    d.bark(); // if the compiler knows it's a dog, it can do dog things
    Animal *b = &d;
    b->jump(); // although b came from d, it's an animal so can only jump not bark
}

现在您真的需要知道Animal在运行时是Dog还是Cat?这会破坏封装 - 大概是你是Animal的子类,因为你想隐藏动物类型,而不是揭示它。

如果是这样,您可以使用dynamic_cast和/或运行时类型识别http://en.wikipedia.org/wiki/Typeid,它将告诉您每个班级是猫还是狗。

答案 1 :(得分:2)

就像我在评论中已经说过的那样,您可以使用Boost.Variant执行此操作。还有另一种方式,更自然,那就是通常具有多态性的类层次结构。

由于层次结构版本已有答案,我将在此处关注变体版本。

首先是代码,然后是解释:

#include <iostream>
#include <boost/variant.hpp>

struct Cat{
  void speak() const{ std::cout << "meow\n"; }
  void cat_extra() const{ std::cout << "Special cat move!\n"; }
  ~Cat(){ std::cout << "Cat died\n"; }
};

struct Dog{
  void speak() const{ std::cout << "wuff\n"; }
  void dog_extra() const{ std::cout << "Special dog move!\n"; }
  ~Dog(){ std::cout << "Dog died\n"; }
};

struct Bird{
  void speak() const{ std::cout << "chirp\n"; }
  void bird_extra() const{ std::cout << "Special bird move!\n"; }
  ~Bird(){ std::cout << "Bird died\n"; }
};

struct Fish{
  void speak() const{ std::cout << "blub\n"; }
  void fish_extra() const{ std::cout << "Special fish move!\n"; }
  ~Fish(){ std::cout << "Fish died\n"; }
};

struct speak_visitor
    : boost::static_visitor<void>
{
    template<class Animal>
    void operator()(Animal const* p) const{
        p->speak();
    }
};

struct extra_visitor
    : boost::static_visitor<void>
{
    void operator()(Cat const* p) const{
        p->cat_extra();
    }
    void operator()(Dog const* p) const{
        p->dog_extra();
    }
    void operator()(Bird const* p) const{
        p->bird_extra();
    }
    void operator()(Fish const* p) const{
        p->fish_extra();
    }
};

struct delete_visitor
    : boost::static_visitor<void>
{
    template<class Animal>
    void operator()(Animal const* p) const{
        delete p;
    }
};

int main(){
    typedef boost::variant<Cat*, Dog*, Bird*, Fish*> variant_type;
    variant_type var;
    speak_visitor sv;
    delete_visitor dv;
    extra_visitor ev;

    var = new Cat();
    var.apply_visitor(sv);
    var.apply_visitor(ev);
    var.apply_visitor(dv);

    var = new Dog();
    var.apply_visitor(sv);
    var.apply_visitor(ev);
    var.apply_visitor(dv);

    var = new Bird();
    var.apply_visitor(sv);
    var.apply_visitor(ev);
    var.apply_visitor(dv);

    var = new Fish();
    var.apply_visitor(sv);
    var.apply_visitor(ev);
    var.apply_visitor(dv);
}

Running example on Ideone

现在进行解释。 boost::variant基本上是增强且标记为union的{​​{1}}。 union可以存储不同的内容,但一次只能存储一个。变体也是如此。标记boost::variant,因为您可以找出当前与which成员函数一起存储的类型,该函数将根据当前存储的类型返回基于0的索引。链接的文档将比我可能更好地解释变体。

为了安全地“访问”储值,需要一个名为“visitor”的构造。访客概念是一个伟大的概念,并有许多资源描述它。链接的文档也在这里介绍了一些细节。基本上,变体在内部对存储的类型(switch)执行which并使用适当的参数调用变体的operator(),如下所示:

// something similar to this happens internally in `apply_visitor`
// contrived for our example classes here
template<class V>
void variant::apply_visitor(V& v){
  switch(this->which()){
    case 0: v((Cat*)&(this->internal_storage)); break;
    case 1: v((Dog*)&(this->internal_storage)); break;
    case 2: v((Bird*)&(this->internal_storage)); break;
    case 3: v((Fish*)&(this->internal_storage)); break;
  }
}

由于这个原因,调用了具有适当参数的适当operator()。这也允许编译器在提供的访问者未处理其中一个变体类型时捕获可能的错误,因为operator()没有适合的重载可用。

使用通用访问者(speak_visitordelete_visitor)时,这个概念可能并不完全清楚,但extra_visitor明确了这一概念。如果你注释掉其中一个operator()重载,编译器就会向你发出barf。

如果您有任何疑问,请在评论中告诉我。最后但并非最不重要的是,我只能重复一遍,你应该阅读文档。 :)

答案 2 :(得分:1)

为什么要枚举?为什么不这样做?

class animal {public: virtual void jump() = 0;}; 
class DOG:public animal{public: virtual void dog::jump(){}}; 
class CAT:public animal{public: virtual void cat::jump(){}}; 
DOG dog; CAT cat;
animal &a1 = dog; // Using a reference
a1.jump();
animal *a2 = &dog; // Using a pointer
a2->jump();