3

每次您在键为 std::string 的 std::map 中插入一对时,它都会生成两个副本。您可以避免使用原始指针,但它是异常不安全的。有没有办法使用智能指针而不是原始指针?

示例代码:

// 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
---
4

3 回答 3

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从中构造了 a ,您也会在比较完成后立即删除该键。

于 2011-02-06T21:41:58.643 回答
2

在我进一步详细介绍之前,请确保不要进行任何类型的优化,除非您确定制作副本的成本如此之大以至于您需要解决它。将字符串作为键很好且直观,避免它所需的代码有点麻烦。

在地图中使用 unique_ptr 作为键确实可以工作,但我真的不认为这是一个好主意。这意味着为了查询映射中的键,您必须将字符串用作存储为 unique_ptr 的键。这意味着除非您将所有字符串存储为 unique_ptrs,否则您需要为要查找的每个字符串创建一个副本。由于插入往往比查找少得多,这似乎以牺牲常见情况为代价来优化不常见的情况。我强烈反对你这样做。

如果您确实想摆脱不必要的复制,您可能需要考虑选择一个执行写时复制的字符串的实现。这样,制作字符串副本的成本是 O(1),并且在插入期间制作的两个副本会很便宜。这可能需要您在其他地方使用此字符串实现,并且必须小心多线程问题,但如果您愿意,它可以工作。

于 2011-02-06T21:23:11.930 回答
1

这里有几个问题:

  • 你不应该从 std::string 派生类
  • 您不应该使用 unique_ptr 作为地图中的键

您可以使用 shared_ptr 作为键,然后您需要一个比较类来比较共享指针。

但是,最好只使用 std::string 作为键,除非它们是非常长的字符串,因此复制它们很昂贵。

顺便说一句,复制中最昂贵的部分可能是分配而不是复制本身。为此,您可以考虑将 basic_string 与自定义分配器一起使用。

于 2011-02-06T21:26:53.447 回答