0

我在使用继承和 STL 列表库时遇到问题...

比如说,我有一个带有两个派生类的抽象基类(其中定义了所有比较运算符)。该列表被声明为

list<StoreItem*> items;

我正在插入一个名为 Food 或 Clothing 的派生类(抽象基类 StoreItem)。我创建了一个即将插入的新 StoreItem 指针:

StoreItem* item = new Food(arguments here);

现在,我想将这个新项目(按顺序)插入到列表中,我的尝试是这样的:

list<StoreItem*>::iterator iter;
for (iter = inventory.begin(); iter != inventory.end(); iter++)
{
    if (*item < **iter)
        break; // break out to insert
}

inventory.insert(iter, item);

有什么我做错了吗?另外,我将如何从库存中提取信息?(例如:使用复制构造函数的 Food tempFruit(**iter))。

先感谢您!祝你有美好的一天。

4

5 回答 5

1

如果您已定义,这将起作用,StoreItem::operator<但还有另一种方法可能会更好一些。STL 有冷分类。您可以定义<for StoreItem*,然后使用list<...>::sort().

(而且您可能已经考虑过定义自己的SortedItemList类来处理内部排序。)

是的tempMovie(**iter),除其他外,它会起作用。

编辑:

我想我谈到从库存中抽出一些东西为时过早。这有效:

list<StoreItem *>::iterator citr = items.begin();

Food *fp = dynamic_cast<Food *>(*citr);

Food ff(*fp);

请注意,您必须知道这StoreItem*实际上指向 a Food-- 如果它指向 a Clothing,您将遇到分段错误或更糟的情况。要找出答案,您可以实现自己的StoreItem::whatTypeAmI(),或使用 C++ 的运行时类型标识:

#include <typeinfo>
...
Food a;
StoreItem *sp = *citr;
if(typeid(*sp)==typeid(a))
{
  // it's a Food
}

StoreItem*(请注意,你可以用或不知道它的类型做很多事情——StoreItem&多态是你的朋友。)

于 2011-10-19T04:25:50.213 回答
1

您假设您从列表中提取的项目是一个Food实例;但是,编译器不知道这一点。Food当您从列表中的项目(具有明显类型的项目)构造新实例时StoreItem,您正在尝试调用Food::Food(const StoreItem)或兼容的东西。为什么?因为迭代器指向的 a可以是对象的实例StoreItem*,也可以是从 派生的任何类的实例,例如.StoreItemStoreItemFood

正如其他海报所评论的那样,多态性是成功关键。您真的需要知道该项目是一个Food吗?如果没有,则访问所有商店商品共享的界面(如价格、序列号等)。如果您需要了解有关该项目的特定信息,则可以尝试推断其类型:

Food *food = dynamic_cast<Food*>(*iter);
if (food != NULL) {
   // perform food related logic
   std::cout << "Ingredients: " << food->ingredients() << std::endl;
}
else {
   std::cout << "I can't eat that!" << std::endl;
}
于 2011-10-19T17:46:15.100 回答
0

如果您可以在指向基类的两个指针之间定义比较运算符,则无需编写任何其他代码即可获得有序集合。根据您的应用程序,您可能需要一个集合或一个堆,甚至可能需要一个映射。这是做它的成语......(基础是从字符串公开派生的)。

template<>
struct std::less<base*>
{
   bool operator()(const base* lhs, const base* rhs) const
   {
      return *lhs < *rhs;
   }
};

typedef set<base*> S;

int _tmain(int argc, _TCHAR* argv[])
{
    base able(std::string("able"));
    base baker(std::string("baker"));
    base charlie(std::string("charlie"));

    S inventory;
    inventory.insert(&charlie);
    inventory.insert(&able);
    inventory.insert(&baker);

    for (S::iterator i = inventory.begin(); i != inventory.end(); ++i)
        std::cout << **i << endl;
    return 0;
}

输出:
able
baker
charlie

在发现这个成语之前,可能会花上一段时间。发生的事情是您正在专门针对 T=base*; 的库模板 std::less 然后 this 就像魔法一样插入 std::set (或其他有序容器)的默认比较器参数。

于 2011-10-19T04:23:13.477 回答
0

而不是自制任何解决方案,您可以求助于boost::ptr_list. 如果您打算将指针存储在像容器一样的 STL 中,这会使生活变得更加轻松。那么您所需要的就是定义operator<您要插入的任何项目。请记住,这ptr_list不适用于共享所有权。std::shared_ptrS在 a 中实现此用途std::list并专门std::less针对您的shared_ptr类型。

于 2011-10-19T13:34:03.933 回答
0

使用为指向StoreItem您的指针定义的比较函数可以缩短插入代码,如下所示:

bool less_ptr( const StoreItem*& lhs, const StoreItem*& rhs )
{
    return *lhs < *rhs;
}

插入:

StoreItem* item = new Food(arguments here);
inventory.insert( std::upper_bound( inventory.begin(), inventory.end(), item, less_ptr ), item);

std::upper_bound( #include <algorithm>) 假定您的列表已排序,因此如果您始终保持列表排序,则适用。

至于拉回数据,有两点需要考虑:

  1. 如果您使用复制构造函数重新创建对象,则您正在创建新对象并且更改它们不会更改列表中的对象,因此最好使用指针
  2. 您必须根据存储的对象类型拆分代码路径

你可以这样做:

Food* foodObj = NULL;
Clothing* clothesObj = NULL;

list<StoreItem *>::iterator it = inventory.find( /* something */ );
StoreItem* item = *it;

item->DoSomethingWithAnyStoreItem(); // It's best to only use such methods

// But if you need something only a derived class has...
foodObj = dynamic_cast<Food*>(item);
clothesObj = dynamic_cast<Clothes*>(item);

if( foodObj != NULL )
{
    foodObj->DoSomethingWithFood();
    Food newFood( *foodObj );
    newFood.DoSomethingWithCopyOfFood();
}
else if( clothesObj != NULL )
{
    clothesObj->DoSomethingWithClothes();
}
else
{
    // It's neither Food, nor Clothes
}
于 2011-10-19T14:14:33.037 回答