每次在std :: map中插入一对,其键是std :: string时,它会生成两个副本。您可以避免使用原始指针,但它是异常不安全的。有没有办法使用智能指针而不是原始指针?
示例代码:
// To compile: g++ -std=c++0x exmaple.cpp -o example
#include <iostream>
#include <string>
#include <map>
#include <memory>
class StringSquealer: public std::string
{
public:
StringSquealer(const std::string s) : std::string(s) {}
StringSquealer(const StringSquealer&)
{
std::cout << "COPY-CONSTRUCTOR" << std::endl;
}
};
int main()
{
// Inefficient
std::map<StringSquealer,int> m1;
m1[StringSquealer("key")] = 1;
std::cout << "---" << std::endl;
// Exception-unsafe
std::map<StringSquealer*,int> m2;
m2[new StringSquealer("key")] = 1;
//Ideal??
std::map<std::unique_ptr<StringSquealer>,int> m3;
std::unique_ptr<StringSquealer> s(new StringSquealer("key"));
//!m3[std::move(s)] = 1; // No compile
}
输出:
COPY-CONSTRUCTOR
COPY-CONSTRUCTOR
---
答案 0 :(得分:7)
这是低效的,因为你写错了你的课。 C ++ 0x提供了右值引用 - 你只是编写了你的类,因此它无法利用它们。
class StringSquealer: public std::string
{
public:
StringSquealer(std::string&& s) : std::string(std::move(s)) {}
StringSquealer(const std::string& s) : std::string(s) {}
StringSquealer(const StringSquealer& s)
: std::string(s)
{
std::cout << "COPY-CONSTRUCTOR" << std::endl;
}
StringSquealer(StringSquealer&& s)
: std::string(std::move(s))
{
std::cout << "MOVE-CONSTRUCTOR" << std::endl;
}
};
unique_ptr
作为关键?这不可能。即使你以某种方式获得相同的指针并从中构造了一个unique_ptr
,你也永远无法找回相同的unique_ptr
- ,一旦比较你就会删除密钥已经完成了。
答案 1 :(得分:2)
在进一步详细说明之前,请确保不要进行任何类似的优化,因为无论您确定制作副本的成本如此之大,以至于您需要解决它。将字符串作为键是很好和直观的,避免它的代码有点毛茸茸。
使用unique_ptr作为地图中的键确实可以工作,但我真的认为这不是一个好主意。这意味着,为了查询映射中的键,您必须将该字符串用作存储为unique_ptr的键。这意味着除非您将所有字符串存储为unique_ptrs,否则您需要复制每个要查找的字符串。由于插入往往不如查找常见,因此这似乎以牺牲常见情况为代价来优化不常见的情况。我强烈反对你这样做。
如果您确实想要摆脱不必要的复制,您可能需要考虑选择执行写入时复制的字符串的实现。这样,制作字符串副本的成本是O(1),并且在插入期间制作的两个副本将是便宜的。这可能需要您在其他地方使用此字符串实现,并且必须小心多线程问题,但如果您愿意,可以使其工作。
答案 2 :(得分:1)
这里有些问题:
您可以使用shared_ptr作为键,然后您需要一个比较类来比较共享指针。
然而,你最好只使用std :: string作为键,除非它们是非常长的字符串,因此复制它们很昂贵。
顺便说一句,复制中最昂贵的部分可能是分配而不是复制本身。为此,您可以考虑将basic_string与自定义分配器一起使用。