1

我想做的是:

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*>from 代码,其中我在编译时允许使用的所有信息都是DB. 这种情况可能是由于通过list<D>指向抽象基类的指针传递的,或者其自身位于基类中的代码。

如果仅list<D*>::iterator源自list<B*>::iterator

4

4 回答 4

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

于 2009-09-24T01:08:43.433 回答
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 上运行的虚拟方法)。

于 2009-09-24T03:42:09.583 回答
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);
    }
}

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

编辑:

如果您真的不想使用虚函数并希望在课堂上完成所有工作Bb,那么Bb需要了解list<T>它应该访问的内容。因此,它需要以某种方式了解类型T,并且不再适用于不同类型T

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

于 2009-09-24T01:18:00.663 回答
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);
    }
};

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

于 2009-09-24T02:09:46.960 回答