每次在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
---
3 回答
这是低效的,因为你写错了你的课 . C 0x提供右值引用 - 您只是编写了类,因此无法利用它们 .
和
unique_ptr
作为关键?这不可能 . 你永远无法收回相同的unique_ptr
- 即使你以某种方式获得了相同的指针并从中构造了一个unique_ptr
,你也会在比较完成后立即删除密钥 .在进一步详细说明之前,请确保不要进行任何类似的优化,因为无论您确定制作副本的成本如此之大,以至于您需要解决它 . 将字符串作为键是很好和直观的,避免它的代码有点毛茸茸 .
使用unique_ptr作为 Map 中的键确实可以工作,但我真的认为这不是一个好主意 . 这意味着,为了查询映射中的键,您必须将该字符串用作存储为unique_ptr的键 . 这意味着除非您将所有字符串存储为unique_ptrs,否则您需要复制每个要查找的字符串 . 由于插入往往比查找少得多,这似乎优于不常见的情况,而牺牲了常见情况 . 我强烈反对你这样做 .
如果您确实希望摆脱不必要的复制,您可能需要考虑选择执行copy-on-write的字符串实现 . 这样,制作字符串副本的成本是O(1),并且在插入期间制作的两个副本将是便宜的 . 这可能需要您在其他地方使用此字符串实现,并且必须小心多线程问题,但如果您愿意,可以使其工作 .
这里有些不对劲:
您不应该从std :: string派生类
您不应将unique_ptr用作 Map 中的键
您可以使用shared_ptr作为键,然后您需要一个比较类来比较共享指针 .
但是你最好只使用std :: string作为键,除非它们是非常长的字符串,因此复制它们很昂贵 .
顺便说一下,复制中最昂贵的部分可能是分配而不是复制本身 . 为此,您可以考虑将basic_string与自定义分配器一起使用 .