STL List - 作为指针对象的数据类型

发布于 2024-12-10 19:27:59 字数 653 浏览 0 评论 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))。

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

I'm having a problem using inheritance and the STL list library...

Say, I have an abstract base class with two derived classes (where all comparison operators are defined). The list is declared as

list<StoreItem*> items;

I'm inserting a derived class (of the abstract base class, StoreItem) called either Food or Clothing. I make a new StoreItem pointer that's about to be inserted:

StoreItem* item = new Food(arguments here);

Now, I'm wanting to insert this new item (in order) to the list, and my attempt is this:

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

inventory.insert(iter, item);

Is there anything I'm doing wrong? Also, how would I pull the information from the inventory? (ex: Food tempFruit(**iter) using the copy constructor).

Thank you in advance! Have a good day.

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

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

发布评论

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

评论(5

终难愈 2024-12-17 19:27:59

您假设从列表中提取的项目是一个 Food 实例;然而,编译器不知道这一点。当您从列表中的项目(表观类型为 StoreItem 的项目)构造 Food 的新实例时,您正在尝试调用 Food::Food( const StoreItem) 或兼容的东西。为什么?因为迭代器指向 StoreItem*,它可能StoreItem 对象的实例,或者是从 派生的任何类的实例StoreItem,例如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;
}

You are assuming that the item you are pulling from the list is a Food instance; however, the compiler doesn't know that. When you construct a new instance of Food from an item in the list (an item with apparent type StoreItem), you are trying to call Food::Food(const StoreItem) or something compatible. Why? Because the iterator points to a StoreItem* that could be an instance of a StoreItem object, or an instance of any class derived from StoreItem, such as Food.

As other posters have commented, polymorphism is a key to success. Do you really need to know that the item is a Food? If not, then access the interface shared by all store items (like price, serial-number, etc.). If you need to know something specific about the item, then you can try to infer its type:

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;
}
Bonjour°[大白 2024-12-17 19:27:59

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

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

是的,tempMovie(**iter) 可以工作,除其他方式外。

编辑:

我认为我谈论从库存中取出一些东西为时过早。这是有效的:

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

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

Food ff(*fp);

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

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

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

This will work, provided you've defined StoreItem::operator<, but there's another way that might be a little better. The STL has sorting down cold. You could define < for StoreItem*, then use list<...>::sort().

(And you've probably already thought of defining your own SortedItemList class that handles the sorting internally.)

And yes, tempMovie(**iter) would work, among other ways.

EDIT:

I think I spoke too soon about pulling something out of the inventory. This works:

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

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

Food ff(*fp);

Note that you have to know that this StoreItem* actually points to a Food-- if it points to a Clothing you'll get a segmentation fault or worse. To find out, you could implement your own StoreItem::whatTypeAmI(), or use C++'s run-time type identification:

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

(Be aware that you can do a lot with a StoreItem* or StoreItem& without knowing it's type-- polymorphism is your friend.)

夏末的微笑 2024-12-17 19:27:59

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

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

输出:
能够
面包师
charlie

在发现这个习语之前,你可能会闲逛一段时间。发生的事情是您正在专门使用库模板 std::less for T=base*;然后,它就像魔法一样插入到 std::set (或其他有序容器)的默认比较器参数中。

If you can define a comparison operator between two pointers to your base class, you can get an ordered collection without writing any other code. Depending on your application you might want a set or a heap, maybe even a map. Here's the idiom to do it... (base is publicly derived from string).

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

output:
able
baker
charlie

It's possible to mill around for a while before discovering this idiom. What's going on is that you're specializing the library template std::less for T=base*; this then slots as if by magic into the default comparator argument for std::set (or other ordered containers).

独木成林 2024-12-17 19:27:59

您可以使用 boost 来代替自制任何解决方案::ptr_list。如果您打算将指针存储在像容器这样的 STL 中,那么事情会变得容易得多。然后您所需要做的就是为您尝试插入的任何项目定义 operator< 。请记住,ptr_list 不适合与共享所有权一起使用。要实现此目的,请在 std::list 中使用 std::shared_ptrS 并为您的 shared_ptr 专门化 std::less > 类型。

Instead of homebrewing any solution you could resort to boost::ptr_list. It makes life a lot easier if you intend to store pointers in STL like containers. Then all you need is to define operator< for whatever item you are trying to insert. Remember that ptr_list is not intended to be used with shared ownership. To achieve this use std::shared_ptrS in a std::list and specialize std::less for your shared_ptr type.

×纯※雪 2024-12-17 19:27:59

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

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

Insertion:

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

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

至于将数据拉出来,需要考虑两件事:

  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
}

With a comparison function defined for pointers to StoreItem you can shorten your insertion code like so:

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

Insertion:

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

std::upper_bound (#include <algorithm>) assumes your list is sorted, so this applies if you keep your list sorted at all times.

As to pulling the data back out, there are two things to consider:

  1. If you re-create the objects using a copy constructor, you're creating new objects and changing them won't change the objects in the list, so it's better to use pointers
  2. You have to split your code path depending on the type of the object stored

You can do this:

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