C++で全てにポインターを使わない理由とは?
疑問点
あるクラスを定義したとします。
class Pixel {
public:
Pixel(){ x=0; y=0;};
int x;
int y;
}
そして、それを使っていくつかのコードを書いてください。なぜ次のようなことをするのでしょうか?
Pixel p;
p.x = 2;
p.y = 5;
Javaの世界から来た私はいつも書いています。
Pixel* p = new Pixel();
p->x = 2;
p->y = 5;
基本的に同じことをするんですよね? 一方はスタック上にあり、もう一方はヒープ上にあるので、後で削除する必要がありますね。この 2 つの間に何か根本的な違いがあるのでしょうか?なぜ、どちらかを選ぶべきなのでしょうか?
どのように解決するのですか?
はい、一方はスタック上にあり、もう一方はヒープ上にあります。2 つの重要な違いがあります。
- まず、明白ですが、あまり重要ではないもの。ヒープの割り当ては遅いです。スタックアロケーションは高速です。
-
第二に、より重要なのは
RAII
. スタックに確保されたバージョンは自動的にクリーンアップされるため
有用
. そのデストラクタは自動的に呼び出されるので、クラスによって割り当てられたすべてのリソースがクリーンアップされることを保証することができます。これは、C++でメモリリークを回避するための基本的な方法です。メモリリークを避けるには、決して
delete
を自分で呼ばず、スタックに割り当てられたオブジェクトでラップし、そのオブジェクトがdelete
を内部的に、典型的にはそのデストラクタの中で呼び出します。もし、手動ですべての割り当てを追跡してdelete
を適切なタイミングで呼び出そうとすると、少なくとも 100 行のコードにつき 1 つのメモリ リークが発生することが保証されます。
小さな例として、次のコードを考えてみましょう。
class Pixel {
public:
Pixel(){ x=0; y=0;};
int x;
int y;
};
void foo() {
Pixel* p = new Pixel();
p->x = 2;
p->y = 5;
bar();
delete p;
}
かなり無難なコードでしょう?ピクセルを作成し、無関係な関数を呼び出し、そしてそのピクセルを削除しています。メモリ リークがあるのでしょうか?
答えは、「おそらく」です。次のような場合はどうなりますか?
bar
が例外を投げたらどうなるでしょうか?
delete
が呼び出されることはなく、ピクセルは削除されず、メモリがリークします。次に、これを考えてみましょう。
void foo() {
Pixel p;
p.x = 2;
p.y = 5;
bar();
}
これならメモリリークしませんね。もちろんこの単純なケースでは、すべてがスタック上にあるので、自動的にクリーンアップされますが、仮に
Pixel
クラスが内部で動的な割り当てを行っていたとしても、それもリークしません。そのため
Pixel
クラスは単にそれを削除するデストラクタを与えられ、このデストラクタは私たちがどのように
foo
関数からどのように離れても、このデストラクタは呼び出されます。たとえ
bar
は例外を投げました。次の、少し工夫した例はこれを示しています。
class Pixel {
public:
Pixel(){ x=new int(0); y=new int(0);};
int* x;
int* y;
~Pixel() {
delete x;
delete y;
}
};
void foo() {
Pixel p;
*p.x = 2;
*p.y = 5;
bar();
}
Pixel クラスは内部でヒープメモリを確保していますが、デストラクタがそれを掃除してくれます。 を使用して を使うときに、それを気にする必要はありません。(ここで最後の例は、一般原則を示すためにかなり単純化されていることを述べておく必要があるでしょう。このクラスを実際に使用する場合、いくつかのエラーの可能性も含んでいます。y の割り当てに失敗すると x は解放されませんし、Pixel がコピーされると、両方のインスタンスが同じデータを削除しようとすることになります。ですから、この最後の例は大目に見てください。実際のコードは少しトリッキーですが、一般的な考えを示しています)
もちろん、同じ手法をメモリ割り当て以外のリソースに拡張することも可能です。たとえば、ファイルまたはデータベース接続が使用後に閉じられること、あるいはスレッドコードの同期ロックが解放されることを保証するために使用することができます。
関連
-
[解決済み】C++エラーです。"配列は中括弧で囲まれたイニシャライザーで初期化する必要がある"
-
[解決済み】fpermissiveフラグは何をするのですか?
-
[解決済み】クラステンプレートの使用にはテンプレート引数リストが必要です
-
[解決済み] using namespace std;」はなぜバッドプラクティスだと言われるのですか?
-
[解決済み] 要素ごとの加算は、結合ループよりも分離ループの方がはるかに高速なのはなぜですか?
-
[解決済み] スマートポインターとは何ですか?
-
[解決済み] なぜ、オブジェクトそのものではなく、ポインタを使用しなければならないのですか?
-
[解決済み] 配列の場合、なぜ a[5] == 5[a] になるのでしょうか?
-
[解決済み】画像処理。コカ・コーラ缶」認識のためのアルゴリズム改良
-
[解決済み】高放射能環境下で使用するアプリケーションのコンパイルについて
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】C++ 非推奨の文字列定数から「char*」への変換について
-
[解決済み] string does not name a type Errorが発生するのはなぜですか?
-
[解決済み】C++エラー:の初期化に一致するコンストラクタがありません。
-
[解決済み】fpermissiveフラグは何をするのですか?
-
[解決済み】C++プログラムでのコンソールの一時停止
-
[解決済み】C++の余分な資格エラー
-
[解決済み】エラー:不完全な型へのメンバーアクセス:前方宣言の
-
[解決済み】C++ - 適切なデフォルトコンストラクタがない [重複]。
-
[解決済み】システムが指定されたファイルを見つけられませんでした。
-
[解決済み】'std::cout'への未定義の参照