オブジェクトがヒープ上に作成されるのを防ぐにはどうすればよいですか?
質問
プラットフォームに依存しない C++ コードで、ヒープ上にオブジェクトが作成されないようにする方法を知っている人はいますか?つまり、クラス「Foo」の場合、ユーザーが次のことを実行できないようにしたいのです。
Foo *ptr = new Foo;
そして、次のことのみを許可します。
Foo myfooObject;
何かアイデアがある人はいますか?
乾杯、
解決
ニックの答え これは開始点としては適していますが、実際にはオーバーロードする必要があるため不完全です。
private:
void* operator new(size_t); // standard new
void* operator new(size_t, void*); // placement new
void* operator new[](size_t); // array new
void* operator new[](size_t, void*); // placement array new
(適切なコーディング方法では、delete 演算子と delete[] 演算子もオーバーロードすることをお勧めします。私はそうしますが、これらは呼び出されないため、オーバーロードされません。 本当に 必要。)
ポールドゥー これは、Foo からの継承では存続しますが、Foo での集約では存続しないという点でも正しいです。これを防ぐためにテンプレートのメタプログラミングの魔法を実行することもできますが、「邪悪なユーザー」の影響を受けないわけではないため、おそらく複雑にする価値はありません。使用方法を文書化し、適切に使用されていることを確認するコード レビューが、ほぼ 100% の方法です。
他のヒント
Foo の new をオーバーロードして非公開にすることができます。これは、コンパイラがうめき声を上げることを意味します...ただし、Foo 内からヒープ上に Foo のインスタンスを作成する場合は除きます。このケースを捉えるには、単純に Foo の新しいメソッドを記述しないと、リンカが未定義のシンボルについてうめき声を上げます。
class Foo {
private:
void* operator new(size_t size);
};
PS.はい、これは簡単に回避できることはわかっています。本当にお勧めしているわけではありません - 悪い考えだと思います - 私は質問に答えただけです。;-)
確実かつ移植可能な方法でそれを行う方法がわかりません。しかし..
オブジェクトがスタック上にある場合は、コンストラクター内で、「this」の値が常にスタック ポインターに近いことをアサートできる可能性があります。この場合、オブジェクトがスタック上にある可能性が高くなります。
すべてのプラットフォームが同じ方向にスタックを実装しているわけではないと思うので、アプリが開始するときに 1 回限りのテストを実行して、スタックがどの方向に成長するかを確認するとよいでしょう。あるいは、ちょっとしたごまかしをしてみましょう。
FooClass::FooClass() {
char dummy;
ptrdiff_t displacement = &dummy - reinterpret_cast<char*>(this);
if (displacement > 10000 || displacement < -10000) {
throw "Not on the stack - maybe..";
}
}
@ニック
これは、Foo から派生または集約するクラスを作成することで回避できます。私が提案したことは (堅牢ではありませんが) 派生クラスや集約クラスでも機能すると思います。
例えば:
struct MyStruct {
Foo m_foo;
};
MyStruct* p = new MyStruct();
ここでは、Foo の隠れた new 演算子をバイパスして、ヒープ上に「Foo」のインスタンスを作成しました。
デバッグ ヘッダーは演算子の新しい署名をオーバーライドできるため、 ... を使用するのが最善です。完全な救済策としての署名:
private:
void* operator new(size_t, ...) = delete;
void* operator new[](size_t, ...) = delete;
Foo クラス内で「operator new」という関数を宣言すると、new の通常形式へのアクセスがブロックされます。
これはあなたが望んでいる種類の動作ですか?
これをインターフェースとして宣言し、独自のコードからより直接的に実装クラスを制御できます。
これは、コンストラクターをプライベートにし、スタック内にオブジェクトを作成するための静的メンバーを提供することで防ぐことができます。
Class Foo
{
private:
Foo();
Foo(Foo& );
public:
static Foo GenerateInstance() {
Foo a ; return a;
}
}
これにより、オブジェクトの作成は常にスタック内に行われます。
これがコンパイル時の機会を提供するかどうかはわかりませんが、クラスの「new」演算子のオーバーロードを検討したことがありますか?