33

私は経験豊富な C 開発者で、C++ を始めたばかりですが、C++ オブジェクトを作成、保持、および破棄する方法がいくつあるかについて非常に混乱していることを認めなければなりません。C では、人生は単純です:=スタック上のコピーを割り当て、ヒープ上のデータを管理しますmallocfreeC++ はそれとはかけ離れているか、そう思われます。

それを踏まえて、ここに私の質問があります:

  1. C++ オブジェクトを作成するすべての方法は何ですか? 直接/コピーコンストラクター、代入など。どのように機能しますか?
  2. これらすべてのタイプのオブジェクト作成に関連するさまざまな初期化構文は何ですか? T f = xT f(x);、などの違いは何T f{x};ですか?
  3. 最も重要なのは、いつC++でコピー/代入/何かをコピーするのが正しいのか=、そしていつポインタを使いたいのか? C では、ポインタの代入は安価ですが、構造体のコピーはそれほど多くないため、ポインタを頻繁に投げることに慣れました。C++ のコピー セマンティクスはこれにどのように影響しますか?
  4. shared_ptr最後に、 、 などはどのようなものweak_ptrですか?

これがやや広範な質問である場合は申し訳ありませんが、いつ何を使用するかについて非常に混乱しています (コレクションとnew演算子のメモリ管理に関する混乱については言及していません)。C メモリ管理について知っていることはすべて知っているように感じます。 C++ では壊れます。それは本当ですか、それとも私のメンタルモデルが間違っているだけですか?

要約すると、C++ オブジェクトはどのように作成、初期化、破棄され、どのような場合に各メソッドを使用する必要があるのでしょうか?

4

3 に答える 3

26

まず第一に、あなたのメモリ管理スキルは C++ で役に立ちます。単に C++ のやり方よりも下のレベルですが、そこにはあります...

あなたの質問については、少し大雑把なので、簡潔にまとめます。

1) C++ オブジェクトを作成するすべての方法は?

C と同じ: グローバル変数、ローカル自動、ローカル静的または動的のいずれかです。コンストラクターに戸惑うかもしれませんが、単純に、オブジェクトを作成するたびにコンストラクターが呼び出されると考えてください。いつも。どのコンストラクターがオブジェクトの作成時にどのパラメーターを使用するかの問題です。

代入は新しいオブジェクトを作成せず、あるオブジェクトから別のオブジェクトにコピーするだけです (memcpyよりスマートに考えてください)。

2) これらすべてのタイプのオブジェクト作成に関連するさまざまな初期化構文は何ですか? T f = x、T f(x);、T f{x}; などの違いは何ですか?

  • T f(x)は古典的な方法であり、引数としてT取るコンストラクターを使用して型のオブジェクトを作成するだけです。x
  • T f{x}集約型 (配列など) の初期化に使用できるため、新しい C++11 統一構文ですが、それ以外は前者と同等です。
  • T f = xxがタイプかどうかによって異なりTます。である場合は前者と同等ですが、異なるタイプの場合は と同等T f = T(x)です。コンパイラは余分なコピーを最適化して取り除くことができるため (コピーの省略)、実際には重要ではありません。
  • T(x). あなたはこれを忘れました。type の一時オブジェクトTが作成され (上記と同じコンストラクターを使用)、コード内で使用され、現在の完全な式の最後で破棄されます。
  • T f. これは、T可能な場合、デフォルトのコンストラクターを使用して型の値を作成します。これは、パラメーターを取らない単純なコンストラクターです。
  • T f{}. デフォルトで構築されていますが、新しい統一された構文を使用しています。T f()は type のオブジェクトではなく、!Tを返す関数であることに注意してください。T
  • T(). デフォルトのコンストラクターを使用する一時オブジェクト。

3) 最も重要なのは、C++ で = をコピー/代入/何でもするのが正しいのはいつですか? また、ポインターを使用するのはいつですか?

C と同じものを使用できます。コピー/代入は、memcpy. 参照を渡すこともできますが、それらに慣れるまでしばらく待つこともできます。あなたがすべきことは、ポインタを補助ローカル変数として使用せず、代わりに参照を使用することです。

4) 最後に、shared_ptr、weak_ptr などのこれらすべてのものは何ですか?

それらは C++ ツール ベルトのツールです。経験といくつかの間違いから学ぶ必要があります...

  • shared_ptrオブジェクトの所有権が共有されている場合に使用します。
  • unique_ptrオブジェクトの所有権が一意で明確な場合に使用します。
  • weak_ptrのツリーでループを分割するために使用されますshared_ptr。それらは自動的に検出されません。
  • vector. これを忘れないでください!これを使用して、あらゆるものの動的配列を作成します。

PS: destructorsについて尋ねるのを忘れていました。IMO、デストラクタは C++ に個性を与えるものなので、必ずそれらをたくさん使用してください!

于 2013-06-15T08:25:19.287 に答える
6

これはかなり幅広い質問ですが、出発点を教えてください。

C で「スタック変数」と呼ばれるものは、「自動ストレージ」を持つオブジェクトとも呼ばれます。自動ストレージを備えたオブジェクトの存続期間は非常に簡単に理解できます。制御が定義されたポイントに到達すると作成され、スコープ外になると破棄されます。

int main() {
  int foo = 5; // creation of automatic storage
  do_stuff();
  foo = 1;

  // end of function; foo is destroyed.
}

ここで注意すべきことは、= 5は初期化構文の一部と見なされ、一方= 1は代入操作と見なされることです。=言語の文法で 2 つの異なるものに使用されて混乱してほしくありません。

とにかく、C++ は自動ストレージをもう少し進めて、そのオブジェクト (コンストラクターとデストラクター) の作成および破棄中に任意のコードを実行できるようにします。これにより、 RAIIと呼ばれる素晴らしいイディオムが生まれます。これは、可能な限り使用する必要があります。RAII を使用すると、リソース管理が自動化されます。

shared_ptr、weak_ptrなどのこれらすべてのものは何ですか?

RAIIの良い例。動的リソース (malloc/free 呼び出し) を自動ストレージ オブジェクトとして扱うことができます。

最も重要なのは、C++ で = をコピー/代入/何でもするのが正しいのはいつですか? また、ポインターを使用するのはいつですか? C では、ポインターの代入は安価ですが、構造体のコピーはそれほど多くないため、私はポインターを頻繁に投げることに慣れていました。C++ のコピー セマンティクスはこれにどのように影響しますか?

const特に関数パラメーターについては、どこでも参照します。constrefs はコピーを回避し、オブジェクトの変更を防ぎます。refを使用できない場合constは、通常の参照が適している可能性があります。何らかの理由で参照をリセットしたり、null に設定したりする場合は、ポインターを使用します。

C++ オブジェクトを作成するすべての方法は何ですか? 直接/コピーコンストラクター、代入など。どのように機能しますか?

つまり、すべてのコンストラクターがオブジェクトを作成します。割り当てはしません。そのために本を読んでください。

于 2013-06-15T08:15:31.127 に答える
2
  1. 明示的な方法とは別に、C++ で暗黙的なオブジェクトを作成する方法は多数あります。それらのほとんどすべては、オブジェクトのクラスのコピー コンストラクターを使用します。注意:暗黙的なコピーでは、コピーが発生する場所に応じて、型のコピー コンストラクターや代入演算子をスコープ内Tで宣言する必要がある場合があります。したがって、もちろん: a)スタック内の真新しいオブジェクトの明示的な作成:public


    T object(arg);

b) 既存のオブジェクトの明示的なコピー:

T original(arg);
...
T copy(original);

クラスにコピー コンストラクターが定義されていない場合T、デフォルトの実装がコンパイラーによって作成されます。渡されたオブジェクトの正確なコピーを作成しようとします。これは常にプログラマが望むものではないため、カスタム実装が役立つ場合があります。
c) ヒープ内の新しいオブジェクトの明示的な作成:

T *ptr = new T(arg);

d) コンストラクターがパラメーターを 1 つだけ取り、修飾子を持たない新しいオブジェクトの暗黙的な作成explicit。たとえば、次のようになります。

class T
{
public:
    T(int x) : i(x) {}
private:
    int i;
}
...
T object = 5; // actually implicit invocation of constructor occurs here

e) 値によって関数に渡されたオブジェクトの暗黙のコピー:

void func(T input)
{
    // here `input` is a copy of an object actually passed
}
...

int main()
{
    T object(arg);
    func(object); // copy constructor of T class is invoked before the `func` is called
}

f) 値による例外オブジェクト処理の暗黙のコピー:

void function()
{
    ...
    throw T(arg); // suppose that exception is always raised in the `function`
    ...
}
...
int main()
{
    ...
    try {
        function();
    } catch (T exception) { // copy constructor of T class is invoked here
        // handling `exception`
    }
    ...
}

g) 代入演算子を使用した新しいオブジェクトの作成。この場合、特定の型の代入演算子の実装が重要であるため、「コピー」という言葉は使用していません。この演算子が実装されていない場合、デフォルトの実装はコンパイラによって作成されますが、デフォルトのコピー コンストラクタと同じ動作をします。

class T
{
    T(int x) : i(x) {}
    T operator=() const
    {
        return T(*this); // in this implementation we explicitly call default copy constructor
    }
}
...
int main()
{
   ...
   T first(5);
   T second = first; // assingment operator is invoked
   ...
}

そうですね、Stroustrup の本を調べなくても、私が思い出すことができるのはそのことです。何かが見落とされている可能性があります。
これを書いている間に、いくつかの回答が受け入れられたので、この時点で終了します。私がリストした詳細が役に立ちますように。

于 2013-06-15T08:44:01.373 に答える