次のクラスを想像してみてください。
class MyString {
public:
const char* str;
std::size_t str_len;
MyString(const char* str, std::size_t str_len)
: str { str }
, str_len { str_len }
{}
}
のデストラクタの実装について少し混乱していMyString
ます。私の最初の考えは、次のようになるだろうということでした。
~MyString() {
delete [] str;
}
しかし、strが割り当てられているかどうか確信が持てない場合、どうすればstrを削除できますか?たとえば、次のMyString
ようなインスタンスを作成できます。
const char* c_string = "Hello, World!";
MyString my_string(c_string, 13);
この場合、ヒープ上で宣言されていないため削除しないでくださいstr
。ただし、次のMyString
ようなインスタンスを作成した場合は次のようになります。
char* char_array = new char[13]{'H','e','l','l','o',',',' ','W','o','r','l','d','!'};
MyString my_string(char_array, 13);
削除しないstr
と、ヒープ上で宣言されるため、メモリリークが発生します(私は推測します)。しかし、私MyString
がこのようなインスタンスを作成した場合:
char* char_array = new char[13]{'H','e','l','l','o',',',' ','W','o','r','l','d','!'};
MyString my_string(char_array + 3, 10);
私はべきではありません削除str
、それはヒープ上だが、それが割り当てられていないからです。割り当てられている他の何かの一部を指しているだけです。
では、削除してはいけないものを削除したり、削除する必要のあるものを削除できなかったりしないようにするにはどうすればよいでしょうか。MyStringがchar*
sの代わりにconst char*
sを使用した場合、答えは異なりますか?使用した場合はどうなりMyString my_string = new MyString...
ますか?
編集:明確にするために、私は実際には文字列クラスを書いていません。私はバイト配列としてchar配列を使用しています。バイトが0になる可能性があるため、std :: stringは機能しないと思います。
適用されるいくつかの異なるパターンがあります。
常に割り当てるパターン。このアプローチでは、クラスは渡されたリソースの所有権を取得せず、割り当てたバッファーにコピーを作成するため、デストラクタで割り当てを解除する方法を知っています。元のパラメータはクラスを呼び出すコードによって所有されており、クラスインスタンスには独立したコピーがあるため、その呼び出し元は必要なときにいつでも独自のデータをクリーンアップする必要があります。例:std::string
。
呼び出し元が指定した削除者のパターン。このアプローチでは、クラスが所有権を取得し、さまざまなアロケーター/デアロケーターのペアに対応するために、データの割り当て解除方法を知っている関数または関数オブジェクトであるパラメーターを受け入れます。クラスデストラクタは、このデリータ関数/関数オブジェクトを呼び出し、その特定のバッファに必要な正しい割り当て解除(またはまったく割り当て解除)を実行します。例:std::shared_ptr
。
ネストされた所有権のパターン。ここでは、クラスは元のデータブロックへのポインタまたは参照を保持するだけです。呼び出し元には、データを解放する所有権と責任がありますが、作成したクラスインスタンスが存在する限り、そのブロックを有効に保つ必要があります。これは実行時のオーバーヘッドが最も低いですが、追跡するのが最も困難です。例:C ++ 11ラムダでの参照による変数キャプチャ。
これらのいずれをクラスの設計に使用する場合でも、クラスのユーザーが不思議に思うことがないように、必ず文書化してください。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加