4

ヒープが割り当てられているかどうかを示すフラグを設定できるようにしたいクラスがあるので、それ自体を適切にクリーンアップし、スタック上にある場合はそれ自体を削除しようとしません。new問題は...両方とコンストラクターを同時にオーバーライドできないようです。したがって、フラグnewを設定するオーバーロードから、isHeapAllocatedフラグをリセットするコンストラクターに入ります。

void* String8::operator new(size_t size)
{
    String8* string = (String8*)malloc(size);
    if(string == null)
        Exception("allocation fail : no free memory");
    string->isHeapAllocated = true;
    return string;
}

String8::String8() 
{
    isHeapAllocated = false;
}

そのnew String8()ため、isHeapAllocatedフラグを設定してから にリセットしfalseます。これを行う方法はありますか?

4

4 に答える 4

3

意図したとおりに動作しません:

new 演算子は、コンストラクターに渡される単位化されたメモリを返します。あなたは正しくそうしますString8* string = (String8*)malloc(size);*string、この段階ではまだ String8 オブジェクトではありません。

したがってstring->isHeapAllocated = true;、実際には、まだ構築されていないオブジェクト (UB) 内にフラグを設定します。

String8* ptr = new String8;これを認めても OS プロセスが危険にさらされることはないので、プログラムがクラッシュすることはありません (結局のところ、すでにあなたに属しているメモリを書き込みます...)。コンストラクターが呼び出され、新しい演算子のオーバーロードで行ったこととは無関係に、メンバーが「false」に戻されます。

C++ オブジェクトを管理するための慣用的な方法は、誰が割り当てを解除する責任があるかということです。(そして、「誰」がスタックである場合、定義によりそれを行うだけです)。

于 2013-04-06T21:21:20.503 に答える
1

これは悪い考えですが、未定義の動作を引き起こさない方法を次に示します。

#include <iostream>
#include <memory>
#include <set>

using namespace std;

class C {
public:

  void* operator new(size_t size) {
    C* c = static_cast<C*>(::operator new(size));
    heap_instances.insert(c);
    return c;
  }

  C() : heap_allocated(heap_instances.find(this) != heap_instances.end()) {}

  const bool heap_allocated;

private:
  static set<const C*> heap_instances;
};

set<const C*> C::heap_instances;

int main(int argc, char** argv) {
  cout << boolalpha;

  C stack;
  cout << stack.heap_allocated << '\n'; // false

  C* heap_nozero = new C;
  cout << heap_nozero->heap_allocated << '\n'; // true
  delete heap_nozero;

  C* heap_zero = new C();
  cout << heap_zero->heap_allocated << '\n'; // true
  delete heap_zero;
}

もちろん、使い終わったらポインターを削除heap_instancesし、マルチスレッド環境で実行している場合は、より適切なコンテナーを使用できます。繰り返しになりますが、実際にこれを行うことはお勧めしません。割り当てに基づいて動作を決定することは、オブジェクトが行うべきことではありません。

これについて私が考えることができる唯一の正当な理由は、有効にすることdelete thisです。オブジェクトの自殺後にメンバーにアクセスしないように注意すれば安全ですが、通常は、オブジェクトに他のオブジェクトの寿命を管理させる方が賢明です。

于 2013-04-06T21:43:28.177 に答える
0

コンストラクターは、スタックまたはヒープに割り当てられている場合に呼び出され、オブジェクトがスタックまたはヒープに割り当てられているかどうかを検出する方法がないことに注意してください。

スタックでオブジェクトを作成するには、このようなメモリ割り当て関数を使用しません

String8 myString;

ヒープ上に作成するには

String8 *myString = new String8();

オブジェクトを使用しなくなった後は、手動でクリーンアップを行う必要があることに注意してください。

スタック スコープにバインドされたヒープ オブジェクトの使用については、C++ プログラムで頻繁に使用される RAII 原則を確認できます (ヒープ割り当てとスタック割り当ての違いのより良い説明については、こちらを参照してください)。

于 2013-04-06T21:05:02.517 に答える
0

なぜこれが必要なのか、本当にわかりません。必要に応じて呼び出すのは呼び出し元の責任でdeleteあり、クラスのデストラクタは、スタック上のオブジェクトで呼び出されるかヒープで呼び出されるかにかかわらず、異なるべきではありません...しかし、おそらく、あなたはいくつかの特別な目的のクラスを行っています...これが私の簡単な見解ですそれ。

編集:また、グローバルがカスタム演算子で使用する割り当て関数と一致する割り当て解除関数を呼び出すことがわかっていない限り、おそらくカスタムdelete演算子をクラスに追加する必要があります。deletenew

#include <cstdlib>
#include <iostream>

namespace so
{

class _test_
{
 private:
  static bool flag_allocation_heap;
  bool flag_heap;

 public:
  _test_()
      : flag_heap( flag_allocation_heap )
  {
   flag_allocation_heap = 0;
   std::cout << flag_heap << std::endl;
  }

  void * operator new( std::size_t _size )
  {
   _test_ * test_ = static_cast< _test_ * >( std::malloc( _size ) );
   flag_allocation_heap = 1;
   return ( test_ );
  }
};

bool _test_::flag_allocation_heap = 0;

} // namespace so

int main()
{

 so::_test_ test_stack_;
 so::_test_ * test_memory_ = new so::_test_;

 delete test_memory_;

 return( 0 );
}

出力:

0
1
于 2013-04-06T21:35:45.053 に答える