6

非常に厳密なセマンティクスを適用し、いくつかの生涯の懸念に対処するために、オブジェクトを強制的にスタックに配置したいと思います。operator newこれを行う方法に関するいくつかの記事を読み、非公開にする(または削除する)ことに到達しました。これは、をnew直接使用すると期待どおりに機能するようですが、正常にmake_sharedコンパイルされます。

#include <boost/smart_ptr.hpp>

class A
{
private:
   void *operator new( size_t );
   void operator delete( void* );
   void *operator new[]( size_t );
   void operator delete[]( void* );
};

int main()
{
//  A* a = new A;      // Correctly produces compile error
    boost::shared_ptr<A> a2 = boost::make_shared<A>();
}

直接使用new Aすると、予想どおりにこのエラーが発生します。

error: ‘static void* A::operator new(size_t)’ is private

配置の新しい演算子を使用しているため、これは機能していると思いますmake_sharedが、これを禁止する方法について説明している記事は見つかりませんでした。私が思いついた最善の解決策は、テンプレートの特殊化を明示的に削除することですmake_shared

namespace boost
{
    template<>
    shared_ptr<A> make_shared<A>() = delete;
};

これは明らかに非常に固有boost::make_sharedです。これは本当に最良の方法ですか?

4

2 に答える 2

4

の配置形式newは非常に扱いやすく、追加の引数が付いているだけです。たとえば、単純な配置フォームは次のとおりです。

void* operator new(std::size_t, void*);

18.6.1.3は、グローバルスコープでこれらを再定義することを禁止していることに注意してください。ただし、特定のタイプに対してそれらを再定義(または削除/アクセス不能にする)しても問題はありません。

残念ながら、make_sharedスコープ付きを使用し::new (pv) T(std::forward<Args>(args)...)ます。そして、私が述べたように、新しいグローバル配置を台無しにすることは許可されていません。したがって、コンパイル時にそれを防ぐことはできず、ランタイムトラップはハックになります(thisポインタをチェックして、スタックのインバウンドであるかどうかを確認します)。

于 2012-12-25T01:52:33.830 に答える
2

演算子のみにアクセスできないようにすることで、クラスのオブジェクトが常にスタック上にあることを強制することはできません。スタック上に構築できるオブジェクトは、メンバーとして別のオブジェクトに埋め込むこともできます。元のクラスがヒープに割り当てられるのに苦労するかもしれませんが、それを含むクラスはそうではありません。これは、次の場合に発生すると思いますboost::make_shared()。内部的には、管理データと実際に割り当てられているオブジェクトの両方を含むレコードが割り当てられている可能性があります。operator new()あるいは、タイプにマップされないが、代わりに独自のoperator new()オーバーロードを使用する、ある種のアロケータからの割り当て関数を使用する場合があります。

ヒープの割り当てを防ぐことができるかどうかはわかりませんが(少なくとも、オブジェクトが別のオブジェクトに埋め込まれている場合)、そうする方法では、コンストラクターにアクセスできなくなり(ほとんどのprivate場合)、ある種のファクトリー関数を使用する必要があります。おそらく移動と組み合わせて。一方、オブジェクトを移動できる場合は、アクセス可能なコンストラクターがあり、オブジェクトがヒープ上のオブジェクトに移動されるのを妨げるものは何もありません。

具体的なタイプの使用を特に禁止したい場合std::make_shared()(またはboost::make_shared()後者を特殊化するためのルールを引用することはできませんが)、特殊化できますstd::make_shared():17.6.4.2.1 [namespace.std]段落1によると、ユーザーはユーザー定義の型が含まれる場合は、(特に指定されていない限り)任意のテンプレートを特殊化できます。したがって、 :Aで使用されないようにすることができます。std::make_shared()

class A
{
public:
    A();
    A(int);
};

namespace std
{
    template <> std::shared_ptr<A> make_shared<A>() = delete;
    template <> std::shared_ptr<A> make_shared<A, int>(int&&) = delete;
}
namespace boost
{
    template <> boost::shared_ptr<A> make_shared<A>() = delete;
    template <> boost::shared_ptr<A> make_shared<A, int>(int&&) = delete;
}

明らかに、複数のコンストラクターがあるA場合は、さらに特殊化を追加する必要があるかもしれません。...そして、型がクラステンプレートであるか、コンストラクターがテンプレートである場合、運が悪いことになります。関数テンプレートを部分的に特殊化することはできません。

配置に関する質問new(によって使用される場合と使用されない場合がありますmake_shared())に関して:配置new(およびdelete)署名は次のとおりです。

void* operator new(size_t, void*) noexcept;
void* operator new[](size_t, void*) noexcept;
void  operator delete(void*, void*) noexcept;
void  operator delete[](void*, void*) noexcept;

(18.6 [support.dynamic]段落1を参照)。ただし、アクセスできないようにすることが何かに役立つとは思えません。

于 2012-12-25T02:15:49.340 に答える