将结构存储在地图中;检查地图中是否存在结构

时间:2012-04-25 05:27:09

标签: c++ search map constructor struct

Q#1)下面的结构不希望被复制并给出编译错误 - 为什么以及如何处理它?<​​/ p>

#include <iostream>
#include <string>
#include <map>

using namespace std;

struct person
{
    person(string n)
        :name(n)
    {}

    string name;
};

int main()
{
    map<string, person> my_map;

    my_map["one"] = person("Tom");

    return 0;
}

Q#2)我们可以通过省略结构构造函数“person(const string&amp; n)”并逐个分配结构值来避免上述问题:

#include <iostream>
#include <string>
#include <map>

using namespace std;

struct person
{
    string name;
};

int main()
{
    map<string, person> my_map;

    person p;
    p.name = "Tom";
    my_map["one"] = p;

    return 0;
}

所以,让我说我这样做,并且在地图中存储了很多人之后,我想检查地图中是否存在某个特定的人。据我所知,正确的做法是:

if(my_map.find("one") == my_map.end()) { //it doesn't exist in my_map }  
else {//it exists}

但据我所知,这将逐一遍历整个地图,不是吗?如果是,那么可以这样做:

using namespace std;

struct person
{
    string name;
    string identifier; // <--
};

int main()
{
    map<string, person> my_map;

    person p;
    p.name = "Tom";
    p.identifier = "something"; // <--
    my_map["one"] = p;

    if(my_map["unknown"].identifier == "something") // <--
        cout << "Found" << endl;
    else 
        cout << "Not found" << endl;

    return 0;
}

通过这样做我们避免迭代,并且内存中的垃圾与我们的标识符匹配的可能性是......我想,特别是如果我们使用一些哈希。 那样可以(安全)这样做吗?

2 个答案:

答案 0 :(得分:8)

1)由于以下表达式,第一个示例中的代码无法编译:

my_map["one"]

my_map["one"]std::string构建"one",并将其传递给std::map::operator[]map::operator[]确保将值映射到提供的键(通过将键与默认构造的值相关联,如果它尚未与值相关联)并返回对该值的引用。

这不编译,因为person没有默认构造函数(“默认构造函数”是不带参数的构造函数。)

有几种方法可以解决这个问题。

一种方法是你采取的方式 - 删除构造函数。它的工作原理是,如果您不提供任何构造函数,则将隐式定义默认构造函数。

另一种方法是明确定义person的默认构造函数:

struct person
{
    person():name(){} //or person()=default; if your compiler supports this
    person(string n)
        :name(n)
    {}
    string name;
};

另一种方法是根本不使用operator[],而是使用map::insert,如下所示:

auto pair(my_map.insert(std::make_pair(std::string("one"),person("Tom"))));
if (!pair.second) {
    *pair.first = person("Tom");
}

2)在地图中找到元素的正确方法是(如你所说)使用:

if(my_map.find("one") == my_map.end()) {/*it doesn't exist in my_map*/}
else {/*it exists*/}

检查地图中的每个元素 - 实际上它只能检查O(log(map.size()))个元素。

你的恐惧是完全没有根据的,这是 在地图中找到元素的正确方法,但是你继续的方式暗示了对operator[]所做的事情的严重误解。 / p>

您问“如果地图中不存在my_map["unknown"].identifier == "something""unknown"将返回true的概率是多少?”。

对此的答案是,没有任何机会返回true,因为如果地图中不存在密钥std::string("unknown")的值,则operator[]会将std::string("unknown")person相关联默认构造identifier,因此{{1}}将为空字符串。

答案 1 :(得分:2)

首先,由于你有一个构造函数,你需要提供一个默认的构造函数。这是因为C ++标准库容器使用值语义。因此,地图需要能够复制值,分配它们,并默认构造它们。由于您提供了构造函数,因此编译器不会合成默认构造函数。这是一个什么都不做的默认构造函数:

person() {} // default constructs string, so no special aciton required.

特别是在std::map的情况下,operator[]在地图中不存在具有该键的元素时返回对默认构造值的引用:

my_map["one"] = p; // creates *default* person, then *assigns* it the value of p.

其次,关于搜索地图的问题,std::map,搜索具有对数复杂度,通常实现为自平衡二叉树。因此,当您搜索时,不要遍历整个地图。并且,由于当搜索到的密钥不存在时,通过operator[]访问会引入新元素,因此使用find()的表单是规范的方式。

由于您提到哈希,C ++ 11提供std::unordered_maptr1boost提供hash_map。这些使用哈希函数执行搜索是恒定时间。是否值得使用它取决于诸如地图大小等因素。恒定时间可能大于搜索小地图所用的对数时间。

注意 的: 如果您想将结构用作键,或者想将其插入标准库sets之一,则还有其他要求:

地图:您需要为密钥提供strict weak ordering,方法是通过类的小于运算符或自定义比较器仿函数。如果你使用person作为关键词,那你就会犯下这样的错误:

bool operator<(const person& rhs) const { return name < rhs.name; }

unordered_或hash maps :您必须通过operator==或仿函数为密钥提供hash函数和相等比较。