迭代C ++ list <derived>作为基类</derived>

时间:2009-09-24 01:03:13

标签: c++ templates iterator base-class

我想做的是:

class Ba { }
class Da : public Ba {}
class Db : public Ba {}

class Bb // abstract base class that must not be a template.
{
    void Process()
    {
        list<Ba*>::iterator pos;
        // I known the derived class will also derive
        // from list<Dx*> where Dx derives from Ba
        for(pos = this->begin(); pos < this->end(); pos++)
            something(*pos);
    }
}

template<T> class L : public Bb , list<T*> // T is always derived from Ba
{
}

但那是无效的。 那么下一个最好的东西是什么?

一个交替的公式有一个全局Process得到一个Bb指针,其中的类型不知道直到运行时:

void GlobalProcess(Bb* bb) // don't know what kind of Bb (which L) got passed in.
{
        list<Ba*>::iterator pos;
        // I known the derived class will also derive
        // from list<Dx*> where Dx derives from Ba
        for(pos = bb->begin(); pos < bb->end(); pos++)
            something(*pos);
}

我有几个理由这样做,但大约一半的原因是感觉不到C ++系统的边缘。


我能提出的最简单的问题形式是我需要从代码中迭代list<D*>,其中我在编译时允许使用的所有信息都是D派生的来自B。这种情况可能来自于通过指向抽象基类的指针传递list<D>或者其自身在基类中的代码。

如果仅list<D*>::iterator

导出list<B*>::iterator,那么这一切都会正常工作

4 个答案:

答案 0 :(得分:8)

首先,不要从std::list<>继承,STL中的类(有一些小的例外)并不意味着派生自。他们没有虚拟成员,这样做没有任何好处,只会导致问题。

你的问题有点含糊不清。但假设你想这样做:

struct A {};
struct B : public A {};
std::list<B> l;

for(std::list<A>::iterator it = l.begin(); it != l.end(); ++it) {
    whatever(*it);
}
显然这是无效的。但你可以这样做:

struct A {};
struct B : public A {};
std::list<B> l;


void whatever(A& a) {
    // do something with an A
}

std::list<B> l;
for(std::list<B>::iterator it = l.begin(); it != l.end(); ++it) {
    whatever(*it);
}

或更好:

struct A {};
struct B : public A {};
std::list<B> l;

void whatever(A& a) {
    // do something with an A
}

std::for_each(l.begin(), l.end(), whatever);

修改

要做你想做的事,你只需要做一些额外的模板工作:

class Ba {};
class Da : public Ba {};
class Db : public Ba {};

template <class T>
class Bb {
    void Process() {
        list<T>::iterator pos;
        for(pos = begin(); pos < end(); pos++)
            something(*pos);
    }
}

template <class T>
class L : public Bb<T>, std::list<T> {
}

您可以像这样制作流程和界面:

template <class T>
class Bb {
public:
    virtual void Process() = 0;
}

template <class T>
class L : public Bb<T>, std::list<T> {
public:
    virtual void Process() {
        list<T>::iterator pos;
        for(pos = begin(); pos < end(); pos++)
            something(*pos);
    }
}

但最好不要继承std::list<> ,因为它几乎肯定会导致错误。最好让std::list<T>成为L类的成员。

答案 1 :(得分:3)

在C ++中获得你想要的东西基本上是不可能的。该语言不起作用,X : Y并不意味着C<X> : C<Y>实际上是您要应用的规则。为什么不呢?有几个原因:

  • C可能根本不是容器(没有办法知道)。
  • 模板专精化(C<X>可能与C<Y>完全不同。)
  • 你打开自己的问题(伪代码,因为这个问题不是C ++独有的)

    class Base
    {
       void method();
    };
    
    class Child : public Base
    {
       void newmethod();
    };
    
    list<Base> base_list;
    list<Child> child_list;
    
     void safe(list<Base> list)
     {
        for(it in list)
        { it->method(); }
     }
    
     void still_safe(list<Child> list)
     {
        for(it in list)
        { it->newmethod(); }
     } 
    
     safe(base_list);
     safe(child_list);
     still_safe(child_list);
    
     void unsafe(list<Base> list)
     {
        list.push_back(new Base);
     }
    
     unsafe(base_list); // safe
     safe(base_list);
     unsafe(child_list); // maybe we get away with this
     still_safe(child_list); // KA BOOM! because the last item in child_list can't newmethod()
    

请注意,您可以在某些语言中执行更有限的形式(以某种方式工作且安全),但我无法想到任何能够明确满足您要求的内容。另请参阅http://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29

在C ++中,对于这类问题,真正的解决方案是利用多态性(无论是通过列表/迭代器类型参数化的模板还是在C和D上运行的虚拟方法)。

答案 2 :(得分:0)

这可以通过将处理代码移动到模板化类中来实现:

class Bb
{
    virtual void Process() = 0;
}

template<T> class L : public Bb , list<T> // T is always derived from Ba
{
    virtual void Process()
    {
        list<T>::iterator pos;
        for(pos = begin(); pos < end(); pos++)
            something(*pos);
    }
}

这样您就可以在Process()中指定正确的迭代器类型,并仍然可以从通用*Bb&Bb中调用它。

编辑:

如果您真的不想使用虚拟功能并希望在课程Bb中完成所有工作,那么Bb需要了解它应该访问的list<T>。所以它以某种方式需要知道类型T,它不再是不同类型T的通用。

如果你有几种不同的类型T,你需要在某处使用多态,这意味着要使用一些虚函数或模板。

答案 3 :(得分:0)

如果您的迭代代码在基类中,那么您就是列表 迭代,也必须在基类中。这导致了 问题,列表应该包含什么类型的对象,因为你想要 确定派生类中的类型。要解决这个问题,你可以 存储共享指针,有效地具有可以存储的列表 多态类型。这是代码:

#include <list>
#include <boost/shared_ptr.hpp>

class Ba { };
typedef boost::shared_ptr<Ba> BaSP;

class Da : public Ba {};
class Db : public Ba {};

void something(BaSP& a) {
    // do something with an Ba
}

class Bb // abstract base class that must not be a template.
{
protected:
    std::list<BaSP> list_;
    void Process()
    {
        std::list<BaSP>::iterator pos;
        for(pos = list_.begin(); pos != list_.end(); pos++)
             something(*pos);
    }
};

class L : public Bb 
{
    L() { 
        // the list can store Da and Db objects 
        boost::shared_ptr<Da> da;
        list_.push_back(da);
        boost::shared_ptr<Db> db;
        list_.push_back(db);
    }
};

这是您发布的代码的下一个最佳选择。如果你给我们了 你想要实现的目标更大,我们可能会出现 设计更好。