1

このサンプルコードがあるとします:

class A
{
public:
     static void* operator new(size_t sz);

private:
int xA;
float yA;
};

class B : public A
{
private:
int xB;
float yB;
};

void* A::operator new(size_t sz)
{
   void* ptr = (void*)new B();
   return ptr;
}

int main()
{
B* b = (B*) new A();
// Use b ..
delete b;
return 0;
}

ここでは、コンストラクターがその順序で呼び出されます (VS2012 でテスト済み)。

  • コンストラクター
  • B コンストラクター
  • コンストラクター

最初の 2 つのコンストラクター呼び出しはnew B()、オーバーロードされた演算子 new 関数によるものです。ただし、オーバーロードされた演算子 new は (オブジェクトを作成せずに) 空きメモリへのポインターを返すことになっているため、関数によって返されたポインターで A コンストラクターが再度呼び出され、コンストラクターが再度呼び出されます。

この例でポインターbを使用した場合、これは未定義の動作ですか?

4

3 に答える 3

1

一般に、operator new()オブジェクトを作成するのではなく、オブジェクト用のスペースを作成する必要があります。あなたのコードはBオブジェクトをオブジェクトで上書きしA、それをBオブジェクトとして使用します。そうです、それは「未定義」になります(おそらく、「オブジェクトを最初に作成されなかった別のタイプにキャストする」の下のドキュメントで説明されています)。

これはこの特定のケースでは機能するように見えるかもしれませんが、 のコンストラクターBがより複雑な場合 (たとえば、 に仮想関数がある場合B)、すぐに正しく機能しなくなります。

オブジェクトにメモリを割り当てたい場合は、次のようにします:L

void* A::operator new(size_t sz)
{
   void* ptr = (void*)::new unsigned char[sz];
   return ptr;
}

これで、同じオブジェクトに対して 2 つの異なるコンストラクターを呼び出すことはなくなりました!

于 2013-08-27T13:29:32.550 に答える
1

のコントラクトoperator newは単なるメモリ割り当てであり、初期化はnew-expression (コンストラクターを呼び出すことによって) によって、またはオペレーターを直接呼び出す場合はプログラム コードによって後で行われます。

あなたがやろうとしていることはできません。Bたとえば、オブジェクトへのポインターを返すファクトリメンバー関数を使用するように再設計できます...

于 2013-08-27T13:30:13.003 に答える
1

あなたが投稿したコードは、 ;A::operator new内から呼び出すため、無限の再帰があります。A::operator newクラスはfromB を継承します。operator newA

それを超えると、コンパイラに嘘をつき、未定義の動作が発生します。の後new A、型が であるオブジェクトへのポインターがありますA。合法的にそのアドレスを に変換B*できますが、それでできることは、それを;B*に戻すことだけです。A*それ以外は未定義の動作です。

new Bまた、 in で何を達成しようとしているのかは明確ではありませんA::operator new。コンパイラは、 から返されたすべてのメモリoperator newを raw メモリと見なします。この場合、その中にAオブジェクトが構築され、それ以降はオブジェクトだけになりAます。それをオブジェクトとして使用しようとする試みB は、未定義の動作です。(そしてもちろん、実際に でB作成されたを破棄する必要A::operator newがある場合は、上書きしてしまっているためできません。

operator new最後に、次のように宣言する必要はありませんstatic。それは暗示的でありstatic、この場合は the を書かないのが慣用的です。同様に、 の結果new Bを aに代入する場合void*、変換は慣用的であり、明示的にしないのは慣用的です。(あまりにも多くのエラーを隠してしまうため、C スタイルのキャストも避けるのが最善です。)

于 2013-08-27T13:47:45.683 に答える