3

キーがstd::stringであるstd::mapにペアを挿入するたびに、2つのコピーが作成されます。生のポインタの使用を避けることはできますが、例外として安全ではありません。生のポインターの代わりにスマートポインターを使用する方法はありますか?

コード例:

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

于 2011-02-06T21:41:58.643 に答える
2

詳細に入る前に、コピーを作成するコストが非常に大きく、それに対処する必要があることが確実でない限り、これらのような最適化を行わないようにしてください。キーとして文字列を使用することは問題なく直感的であり、それを回避するために必要なコードは少し複雑です。

マップのキーとして unique_ptr を使用することは確かに機能しますが、それは良い考えだとは思いません。これは、マップ内のキーを照会するには、キーとして使用する文字列を unique_ptr として格納する必要があることを意味します。これは、すべての文字列を unique_ptrs として保存しない限り、検索する各文字列のコピーを作成する必要があることを意味します。挿入はルックアップよりもはるかに一般的ではない傾向があるため、これは一般的なケースを犠牲にして珍しいケースを最適化するようです. これを行うことは強くお勧めしません。

不必要なコピーをなくしたい場合は、代わりにコピーオンライトを行う文字列の実装を選択することを検討してください。そうすれば、文字列のコピーを作成するコストは O(1) であり、挿入中に作成される 2 つのコピーは安価になります。これにはおそらく、この文字列の実装を別の場所で使用する必要があり、マルチスレッドの問題に注意する必要がありますが、必要に応じて機能させることができます。

于 2011-02-06T21:23:11.930 に答える
1

ここでいくつか間違っています:

  • std::string からクラスを派生させてはなりません
  • マップのキーとして unique_ptr を使用しないでください

shared_ptr をキーとして使用すると、共有ポインターを比較するための比較クラスが必要になります。

ただし、コピーにコストがかかるような非常に長い文字列でない限り、 std::string をキーとして使用する方がよいでしょう。

ところで、コピーの最もコストのかかる部分は、コピー自体ではなく割り当てである可能性があります。そのためには、basic_string をカスタム アロケータと共に使用することを検討できます。

于 2011-02-06T21:26:53.447 に答える