首页 文章

避免在没有原始指针的情况下复制 Map 的密钥

提问于
浏览
3

每次在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 回答

  • 1

    这是低效的,因为你写错了你的课 . 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 ,你也会在比较完成后立即删除密钥 .

  • 2

    在进一步详细说明之前,请确保不要进行任何类似的优化,因为无论您确定制作副本的成本如此之大,以至于您需要解决它 . 将字符串作为键是很好和直观的,避免它的代码有点毛茸茸 .

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

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

  • 7

    这里有些不对劲:

    • 您不应该从std :: string派生类

    • 您不应将unique_ptr用作 Map 中的键

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

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

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

相关问题