std :: map、参照、ポインター、メモリ割り当て
質問
mapとvaluetypeの割り当てに苦労しています。
この単純なクラスを検討してください:
class Column {
private:
char *m_Name;
public:
// Overrides
const char *Name(){
return this->m_Name;
}
// Ctors
Column(const char *NewName){
this->m_Name = new char[strlen(NewName) + 1];
strcpy(this->m_Name, NewName);
}
// Dtors
~Column(){
cout << "wtf?\n";
delete this->m_Name;
}
};
今、私はこのマップを持っています:
// Typedefs
typedef std::map<int, Column> ColumnContainer;
ColumnContainer *m_Container;
これを呼び出すとき:
Column *c = new Column("Test");
cout << "CREATED: " << c->Name() << "\n";
it = this->m_Container->insert(std::make_pair(0, *c)).first;
cout << "AGAIN: " << c->Name() << "\n";
コンソールは<!> quot; wtf?<!> quotを出力しています。マップへの挿入後。
列を破壊しているようです。これは正しいですか?
または何か間違ったことをしていますか?
value_type
のstd::map
が、PODまたはPODの配列のように、定義されたメモリサイズの構造体型である必要があるかどうか疑問に思っていましたか?
cout << AGAIN
<!> quot; Test <!> quot;
必要なのは、intキーに基づく列へのマップです
解決
文字列m_Nameが2回目に印刷されない根本的な理由は、STLがマップを作成する方法のためです。挿入中に値のさまざまなコピーを作成します。このため、m_Nameは元の列のコピーの1つで破棄されます。
もう1つのアドバイスは、オブジェクトがマップ内の値である場合、オブジェクトへのポインターを使用することです。そうしないと、オブジェクトのパフォーマンスが大幅に低下する可能性があります。
他のヒント
make_pair(0, *c)
は、(一時的な、名前のない)pair<int, Column>
を作成します。このペアは参照によってmap::insert
に渡され、std::map
はその要素を所有しているため、ペアのコピーを作成します。次に、一時的なペアが破棄され、それに含まれるColumnオブジェクトが破棄されます。
これが、標準のコンテナ要素として使用するタイプのコピー構築を適切に定義する必要がある理由です。
コードで何が起こっているのか:
ここでは、オブジェクトを動的に作成しています。 (これはコードで解放されません)。
なぜポインターを使用しているのですか? (スマートポインターはあなたの友人です。)
しかし、通常のオブジェクトが望ましいでしょう。
Column *c = new Column("Test");
オブジェクトがstd :: pair <!> lt; int、Column <!> gt;型の一時オブジェクトにコピーされると、オブジェクトのコピーが作成されます。 (これは、M_nameメンバーのコピーを作成するColumnのコピーコンストラクターを使用します(同じメモリ位置への2つのポインターがあります)。
std::make_pair(0, *c)
ペアを挿入します<!> lt; int、Column <!> gt;マップに。これは、再度列コピーコンストラクターを使用して実行されます(列コピーコンストラクターを呼び出すmake_pairコピーコンストラクターを使用します)。再び、M_nameポインターがこのオブジェクトにコピーされます。これで、動的に割り当てられた文字列に3つのオブジェクトが配置されました。
m_Container->insert( pairObject )
この時点で、std :: make_pair()の呼び出しで作成した一時オブジェクトは不要になったため、破棄されます。これは、デストラクタが呼び出される場所です。もちろん、これにより、現在リリースされているメモリにポインタを持つ2つのオブジェクトが残ります。
大きな問題があります。
std :: stringを使用する必要があります(推奨ソリューション)
または、クラスでRAWが所有するポインターを正しく処理する必要があります。つまり、デフォルトで生成される4つのメソッドすべてを実装する必要があります。
- コンストラクタ
- デストラクタ
- コピーコンストラクター
- 割り当て演算子
小さな問題:
Javaプログラマーのようにコーディングを停止する必要があります。
Column *c = new Column("Test");
it = this->m_Container->insert(std::make_pair(0, *c)).first;
次のようになります。
m_Container[0] = Column("Test");
すべてを動的に割り当てる必要はありません。
非常に危険な事実。
RAWが所有するポインターを持つことが悪い考えである理由の説明。
class X
{
char* m_name;
public:
X(char const* name) {m_name new char[strlen(m_name) +1];strcpy(m_name,name);}
~X() {delete [] m_name;}
};
これは問題ありません。ただし、コンパイラは2つのメソッドを生成しています。ほとんどの場合、これは問題ありませんが、RAWが所有するポインターがある場合はそうではありません。
X::X(X const& copy)
:m_name(copy.m_name)
{}
X& X::operator=(X const& copy)
{
m_name = copy.m_name;
}
コードのイメージを今:
X x("Martin");
X y(x);
「x」と「y」の両方が、同じメモリを指すコンタンポインター(m_name)になりました。 「y」が範囲外になると、メモリ上でdelete []を呼び出すデストラクタを呼び出します。ここで、「x」はスコープ外に移動し、同じメモリでdeleteを呼び出します。
Z z("Bob");
z = x;
別の演算子を使用した上記のjsutと同じ問題。
これはどのように当てはまりますか?
列へのポインターのマップを使用しています。マップには実際にColoumnオブジェクトが格納されます。したがって、上記のCopyコンストラクターを使用してオブジェクトのコピーを作成しています。だから問題があります。しかし、コードでも、一時オブジェクトが作成されて渡される場合が多くあります。
doWork(Column const& x) { /* Do somthing intersting */
doWork(Column("Hi There"));
ここでは、doWork()に渡される一時的なColumnオブジェクトを作成します。 doWork()が完了すると、temporayはスコープから外れて削除されます。しかし、doWork()がコピーコストラクタまたは代入演算子を使用してオブジェクトのコピーを作成するとどうなりますか?これは、オブジェクトをマップに挿入するときに発生します。一時的なペアを作成してから、この値をマップにコピーしています。その後、この一時的なペアは破棄されます。
コンテナ内のm_Nameは文字列である必要があるため、マップにコピー構築できます。
現在、適切なコピーコンストラクタを定義していないため、m_Nameをポインタとしてコピーしているだけです。これは無効です。
実行することで単純化を試みます
class Column {
private:
std::string m_Name;
public:
// Overrides
const char *Name(){
return m_Name.c_str();
}
};
C ++とすべてのメンバーがコピー構築可能であるため、コピーコンストラクターを無料で取得できます。
表示されているのは、Columnの一時コピーが破壊されていることです。コンストラクターをインストルメントすると、コピーが作成されるのが見えるはずです。
列名にstd::string
を使用しないのはなぜですか?本当に ?そして、すべてが大丈夫です。
ここでは、デストラクタから始めて多くの問題が発生しているため(delete[]
によって割り当てが行われる場合はnew[]
を使用してください)
また、本当に新しい列を作成する必要がありますか?
次のように書き換えましょう:
class Column
{
public:
Column() : m_name() {}
Column(const std::string& name) : m_name(name) {}
const std::string& getName() const { return m_name; }
private:
std::string m_name;
};
そして今、あなたの挿入コード:
std::map<int,Column> m_container;
Column myColumn = Column("Test");
std:cout << "CREATED: " << myColumn.getName() << std::endl;
m_container[0] = myColumn; // COPY the column
std::cout << "AGAIN: " << myColumn.getName() << std::endl;
そして、ここですべてが順調です。さらに洗練された構文
m_container[0] = Column("Test");
C ++はすでにかなりの量のコードを必要としているため、可能な限りショートカットを使用してみましょう。