こんにちは 以下では、すべての人によく知られている
newおよび
delete演算子について、より正確には本(少なくとも初心者向けの本)には書かれていないものについて説明します。
この記事を書くために、私は
newと
deleteについてよくある誤解に促されました。これはフォーラムや一部の本(!!!)でもよく見られます。
私たちは皆、
新規および
削除とは何かを知っていますか? それとも私たちが知っていると思う?
この記事は、あなたがそれを理解するのに役立ちます(そして、知っている人は批判することができます:))
注 :以下の説明は、new演算子の他の形式およびdelete演算子のすべての形式について、new演算子のみに専念します。以下で説明することもすべて当てはまり、類推によって適用できます。それで、初心者が
新しいことを説明するときに通常本に書かれているものから始めましょう(テキストは「天井から」取られますが、一般的には真実に対応します):
new演算子は、必要なサイズ以上のメモリを割り当て、C言語関数とは異なり、メモリが割り当てられているオブジェクトのコンストラクターを呼び出します...ニーズに合わせて新しい演算子をオーバーロードできます(どこかに実装するために書き込みます)。
そして、例えば、彼らは新しい演算子のプリミティブなオーバーロード(実装)を示し、そのプロトタイプは次のようになります
void* operator new (std::size_t size) throw (std::bad_alloc);
私が注意したいこと:
1. C ++言語の
新しい キーワードと
新しい演算子を、1つのエンティティとして話されるすべての場所で共有することはありません。
2.作成するすべての場所で、
newがオブジェクトのコンストラクターを呼び出します。
最初と2番目の両方は、一般的な誤解です。
しかし、初心者向けの本を望んでいません。標準、つまりセクション5.3.4と18.6.1に目を向けます。このセクションでは、この記事のトピックが実際に明らかにされています(むしろ公開されています)。
5.3.4
new-expressionは、適用されるtype-id(8.1)またはnew-type-idのオブジェクトを作成しようとします。 / *さらに興味がない* /
18.6.1
void * operator new(std :: size_t size)throw(std :: bad_alloc);
効果:新しい式(5.3.4)によって呼び出され、次のサイズのバイトを割り当てる割り当て関数
そのサイズのオブジェクトを表すために適切に位置合わせされたストレージ/ *これ以上興味はありません* /
ここでは、最初のケースでは
newが
expressionとして参照され、2番目のケースでは
operatorとして宣言されていることがすでにわかりました
。 そして、これらは実際には2つの異なるエンティティです!
これがなぜそうなのかを考えてみましょう。そのためには、
newを使用してコードをコンパイルした後に取得したアセンブラーリストが必要になり
ます。 さて、今ではすべてについて順番に。
new-expressionは、
if 、
whileなどと同じ言語ステートメント
です 。 (ただし
、 while 、などはまだ
statementと呼ばれていますが、歌詞は破棄します)i.e. リストでそれを満たしている場合、コンパイラはこのステートメントに対応する特定のコードを生成します。 また、C ++言語の
キーワードの 1つが
新しく 、
if 's、
for'などとの共通性が確認されています。 そして、
演算子new()は、同じ名前のC ++言語関数であり、その動作は再定義できます。
重要-operator new()は、メモリが割り当てられるオブジェクトのコンストラクターを呼び出し
ません 。 適切なサイズのメモリを割り当てるだけです。 関数関数との違いは、例外をスローしてオーバーライドできることと、特定のクラスに対して演算子を作成できることです。これにより、このクラスに対してのみオーバーライドされます(残りは自分で覚えてください:))。
ただし、
new-expressionはオブジェクトのコンストラクターを呼び出すだけです。 何も呼び出さず、単にそれを満たしていると言う方が正確ですが、コンパイラはコンストラクタを呼び出すためのコードを生成します。
図を完成させるために、次の例を検討してください。
#include <iostream> class Foo { public: Foo() { std::cout << "Foo()" << std::endl; } }; int main () { Foo *bar = new Foo; }
このコードの実行後、予想どおり、「Foo()」が出力されます。 理由を理解しましょう。このためには、アセンブラーを調べる必要がありますが、便宜上、少しコメントしました。
(コードはMSVS 2012で使用されているclコンパイラによって取得されましたが、主にgccを使用していますが、これは適用されません)
/Foo *bar = new Foo; push 1 ; Foo call operator new (02013D4h) ; operator new pop ecx mov dword ptr [ebp-0E0h],eax ; , new, bar and dword ptr [ebp-4],0 cmp dword ptr [ebp-0E0h],0 ; 0 bar je main+69h (0204990h) ; 0, ( main - , ) mov ecx,dword ptr [ebp-0E0h] ; ecx (MSVS this ecx(rcx)) call Foo::Foo (02011DBh) ; ;
何も理解していない人のために、ここに一口のような擬似コードで起こったことの(ほぼ)類似物があります(つまり、これをコンパイルしようとしないでください:))
Foo *bar = operator new (1);
上記のコードは、上記のすべてを確認します。
1.(言語)
operator newと
operator new()は同じものではありません。
2.
operator new()はコンストラクターを呼び出しません
3.コンパイラーがコンストラクターへの呼び出しを生成し、コード内の
キーワード「new」を満たします
結論:この記事が、
new-expressionと
operator new()の違いを理解するのに役立つこと、または誰かが知らない場合でも存在すること(この違い)がわかることを願っています。
PS
delete 演算子と
operator delete()には同様の違いがあるため、記事の冒頭で説明しませんでした。 なぜその説明が意味をなさないのか理解できたと思いますし、
削除のために上に書かれたものの正確性を独立して検証することができます。
更新:ニックネームが
khimで個人的に通信するKhrazhitelは、上記の本質をよく示している次のコードを提案しました。
#include <iostream> class Test { public: Test() { std::cout << "Test::Test()" << std::endl; } void* operator new (std::size_t size) throw (std::bad_alloc) { std::cout << "Test::operator new(" << size << ")" << std::endl; return ::operator new(size); } }; int main() { Test *t = new Test(); void *p = Test::operator new(100); // 100 }
このコードは次を出力します
Test::operator new(1) Test::Test() Test::operator new(100)
予想されることです。