在向量中使用现有对象或在C ++中创建新的(如果不存在)

时间:2017-10-23 06:27:02

标签: c++ pointers

假设我有一个名为Store的类,其中包含产品。为简单起见,函数内联。

class Store
{
public:
    Store(string name)
        : _name(name)
    {}

    string getName() const
    { return _name; };

    const std::vector<string> getProducts()
    { return _products; };

    void addProduct(const string& product)
    { _products.push_back(product); }

private:
    const string _name;
    std::vector<string> _products;
};

然后我有一个二维字符串数组,其中包含store-product -pairs。同一个商店可以多次排列。

string storeListing[4][2] = {{"Lidl", "Meat"},
                             {"Walmart", "Milk"},
                             {"Lidl", "Milk"},
                             {"Walmart", "Biscuits"}};

现在我想遍历数组,为数组中的每个商店创建Store-object,并将其产品添加到object。所以我需要使用现有的Store-object或者如果没有任何正确的名称则创建一个新的。有什么方法可以实现这个?目前我正在尝试使用指针并将其设置为相关对象,但是当我稍微修改代码时,我有时会遇到分段错误,有时会出现其他令人讨厌的问题。我想我在这里称之为一些未定义的行为。

std::vector<Store> stores;
for (int i = 0; i < 4; ++i) {
    string storeName = storeListing[i][0];
    string productName = storeListing[i][1];

    Store* storePtr = nullptr;
    for (Store& store : stores) {
        if (store.getName() == storeName) {
            storePtr = &store;
        }
    }

    if (storePtr == nullptr) {
        Store newStore(storeName);
        stores.push_back(newStore);
        storePtr = &newStore;
    }

    storePtr->addProduct(productName);
}

5 个答案:

答案 0 :(得分:1)

使用std::unordered_set<Store>,其中哈希类型是商店的字符串名称。使用类似map的类型会导致商店名称的重复存储(一次作为地图的键,一次作为Store对象本身)。

template <>
struct std::hash<Store> {
    using Store = argument_type;
    using result_type = std::size_t;
    result_type operator()(const argument_type& s) const noexcept {
       return result_type{ std::hash<std::string>{}(s._name) }();
    }
};

std::unordered_set<Store> stores;
for (int i = 0; i < 4; ++i) {
   string storeName = storeListing[i][0];
   string productName = storeListing[i][1];

   auto iter = stores.find(storeName);
   if(iter == stores.end()) iter = stores.emplace(storeName);
   iter->addProduct(productName);
}

答案 1 :(得分:1)

最有可能,因为您将“存储”副本插入到矢量中:

if (storePtr == nullptr) {
    Store newStore(storeName);   //create Store on stack
    stores.push_back(newStore);  //Make a COPY that is inserted into the vec
    storePtr = &newStore;       // And this is where it all goes wrong.
}

newStore在if结束时超出范围而StorePtr丢失。

尝试使用:

storePtr = stores.back();

或者将你的矢量设为std::vector<Store*>

然后:

if (storePtr == nullptr) {
Store * newStore = new Store(storeName);   //create Store on stack
stores.push_back(newStore);  //Make a COPY that is inserted into the vec
storePtr = newStore;       // And this is where it all goes wrong.
}

当然,正如评论所暗示的那样,std :: map会更适合这里。

简而言之,std :: map存储键值对。密钥很可能是您的商店名称,以及产品的价值。

快速举例:

std::map<std::string, std::string> myMap;
myMap["Lidl"] = "Milk";
myMap["Billa"] = "Butter";
//check if store is in map:
if(myMap.find("Billa") != myMap.end())
  ....

注意,您当然可以使用Store对象作为值。要将其用作关键,您必须处理以下事项:

std::maps with user-defined types as key

对于您的具体示例,我建议您使用std::string作为关键字,并使用Products的向量作为值。

答案 2 :(得分:0)

您的方法存在一些问题。

问题1:

Store有一个const数据成员。这将使得无法重新排序商店的矢量。这需要纠正。

问题2:

插入后,您需要指向正确的商店。这是一种方法:

// decompose the problem:
// first step - get a pointer (iterator) to a mutable store *in the vector*
auto locate_or_new(std::vector<Store>& stores, std::string const& storeName)
-> std::vector<Store>::iterator
{
    auto iter = std::find_if(begin(stores), end(stores),
                             [&](Store& store)
                             {
                                 return store.getName() == storeName;
                             });
    if (iter == end(stores))
    {
        iter = stores.emplace(end(stores), storeName);
    }
    return iter;
}

//
// 2 - insert the product in terms of the above function.    
auto addProduct(std::vector<Store>& stores, std::string const& storeName, std::string const& productName)
-> std::vector<Store>::iterator
{
    auto istore = locate_or_new(stores, storeName);
    istore->addProduct(productName);
    return istore;
}

注意:

由于将对象插入到向量中会导致迭代器失效,因此您需要注意不要在可以创建新存储的代码段中保持向量内对象的引用。

答案 3 :(得分:0)

if (storePtr == nullptr) {
        Store newStore(storeName);
        stores.push_back(newStore);
        storePtr = &newStore;
    }

一旦if结束newStore消失,您将留下悬空指针storePtr。 你可以在这里使用std::set<Store *>

std::set<Store *> myset;
Store *c = new Store("store3");
std::set<Store *>::iterator iter = myset.find(c);
if(iter!=myset.end())
{
   (*iter)->addProduct("Product1");
}
else
{
   c->addProduct("Product1");
   myset.insert(c);
}

答案 4 :(得分:0)

使用向量和迭代器有一个解决方案。别忘了包含&#34;算法&#34;头!

std::vector<Store> stores;
for (int i = 0; i < 4; ++i) {
    string storeName = storeListing[i][0];
    string productName = storeListing[i][1];

    auto storeIter = std::find_if(stores.begin(), stores.end(), [storeName](Store store) -> bool {
        return store.getName() == storeName;
    }); //Find the store in the vector

    if (storeIter == stores.end()) //If the store doesn't exists
    {
        stores.push_back(Store(storeName)); //Add the store to the vector
        storeIter = prev(stores.end()); //Get the last element from the vector
    }
    Store* storePtr = &(*storeIter); //You can convert the iterator into a pointer if you really need it
    storeIter->addProduct(productName);
    //storePtr->addProduct(productName);
}