8

クラスをスタック上で強制的にインスタンス化する方法、または少なくともC ++でグローバルになるのを防ぐ方法はありますか?

コンストラクターが事前の初期化を必要とするCAPIを呼び出すため、グローバルインスタンス化を防ぎたいです。AFAIKには、グローバルオブジェクトの構築順序を制御する方法はありません。

編集:アプリケーションは、動的メモリ割り当ても禁止されている組み込みデバイスを対象としています。ユーザーがクラスをインスタンス化するための唯一の可能な解決策は、スタック上にあるか、配置の新しい演算子を使用することです。

Edit2:私のクラスは、他の外部ライブラリ(C APIの元)に依存するライブラリの一部です。これらのライブラリを変更することはできず、最終的なアプリケーションでライブラリを初期化する方法を制御することもできません。そのため、クラスの使用方法を制限する方法を探しています。

4

6 に答える 6

7

クラスのオブジェクトにいくらか恣意的な制限を課す代わりに、クラスにラップすることでCAPIの呼び出しを安全にしたいと思います。そのクラスのコンストラクターが初期化を行い、デストラクタが取得したリソースを解放します。

次に、このクラスをクラスの引数として要求でき、初期化は常に機能します。

ラッパーに使用される手法はRAIIと呼ばれ、このSOの質問とこのwikiページで詳細を読むことができます。もともとは、カプセル化されたリソースの初期化とオブジェクトへのリリースを組み合わせることを目的としていましたが、他のさまざまな目的にも使用できます。

于 2012-11-21T15:08:42.900 に答える
1

半分の答え:ヒープ割り当てを防ぐために(したがって、スタック割り当てのみを許可する)、演算子newをオーバーライドして、プライベートにします。

void* operator new( size_t size );

編集:他の人は制限を文書化するだけだと言っていますが、それでも私は同意します:ヒープ割り当てなし、グローバル割り当てなし、APIの初期化(コンストラクターでは完全ではありませんが、それでも十分だと思います):

class Boogy
{
public:

    static Boogy* GetBoogy()
    {
        // here, we intilialise the APIs before calling
        // DoAPIStuffThatRequiresInitialisationFirst()
        InitAPIs();
        Boogy* ptr = new Boogy();
        ptr->DoAPIStuffThatRequiresInitialisationFirst();
        return ptr;
    }

    // a public operator delete, so people can "delete" what we give them
    void operator delete( void* ptr )
    {
        // this function needs to manage marking array objects as allocated                        
        // or not
    }

private:

    // operator new returns STACK allocated objects.  
    void* operator new( size_t size )
    {
        Boogy* ptr = &(m_Memory[0]);
        // (this function also needs to manage marking objects as allocated 
        // or not)
        return ptr;
    }

    void DoAPIStuffThatRequiresInitialisationFirst()
    {
        // move the stuff that requires initiaisation first
        // from the ctor into HERE.
    }

    // Declare ALL ctors private so no uncontrolled allocation, 
    // on stack or HEAP, GLOBAL or otherwise, 
    Boogy(){}

    // All Boogys are on the STACK.
    static Boogy m_Memory[10];

};

誇りに思うのか恥ずかしいのかわからない!:-)

于 2012-11-21T15:06:06.517 に答える
1

それ自体、オブジェクトをグローバルとして配置することを防ぐことはできません。そして、私はあなたが試してはいけないと主張します:結局のところ、なぜそれらのライブラリを初期化するオブジェクトを構築し、それをグローバルにインスタンス化し、次にオブジェクトをグローバルにインスタンス化できないのですか?

それでは、質問を言い換えて、その核心にドリルダウンさせてください。

初期化作業が完了する前にオブジェクトが構築されないようにするにはどうすればよいですか?

一般に、応答は次のとおりです

それはすべて、初期化作業が何であるか、具体的には次のように要約されます。

  • まだ呼び出されていないことを検出する方法はありますか?
  • 初期化関数を数回呼び出すことには欠点がありますか?

たとえば、次の初期化子を作成できます。

class Initializer {
public:
    Initializer() { static bool _ = Init(); (void)_; }

protected:
    // boilerplate to prevent slicing
    Initializer(Initializer&&) = default;
    Initializer(Initializer const&) = default;
    Initializer& operator=(Initializer) = default;

private:
    static bool Init();
}; // class Initializer

このクラスが最初にインスタンス化されると、が呼び出さInitれ、その後は無視されます(些細な比較が犠牲になります)。これで、このクラスから(プライベートに)継承して、コンストラクターの初期化子リストまたは本体が呼び出されるまでに、必要な初期化が実行されていることを確認するのは簡単です。

どのInitように実装する必要がありますか?

何が可能で安価であるかに応じて、初期化が行われたことを検出するか、初期化を呼び出します。

そして、C APIが非常にくだらない場合、実際にはどちらも実行できませんか?

あなたは乾杯です。ウェルカムドキュメント。

于 2012-11-21T18:42:23.577 に答える
0

クラスをスタック上で強制的にインスタンス化する方法、または少なくともC ++でグローバルになるのを防ぐ方法はありますか?

あまり。コンストラクターをプライベートにし、ファクトリメソッドのみを使用して上記のオブジェクトを作成することもできますが、上記のメソッドを使用してグローバル変数を作成することを実際に妨げるものは何もありません。

アプリケーションが「main」に入る前にグローバル変数が初期化された場合、「main」がフラグを設定する前にコンストラクターから例外をスローできます。ただし、グローバル変数をいつ初期化するかを決定するのは実装次第です。したがって、アプリケーションが「メイン」に入った後に初期化できます。つまり、未定義の動作に依存することになりますが、これは良い考えではありません。

理論的には、呼び出しスタックを調べて、そこから呼び出されていることを確認できます。ただし、コンパイラーはコンストラクターまたはいくつかの関数をインライン化する可能性があり、これは移植性がなく、C++での呼び出しスタックのウォークは苦痛になります。

「this」ポインタを手動でチェックして、それがどこにあるかを推測することもできます。ただし、これは、この特定のコンパイラ、OS、およびアーキテクチャに固有の移植性のないハックになります。

ですから、私が考えることができる良い解決策はありません。

結果として、他の人がすでに示唆しているように、プログラムの動作を変更するのが最善の方法です。コンストラクターでC apiを初期化し、デストラクタで非初期化するシングルトンクラスを作成し、必要に応じてファクトリメソッドを介してこのクラスを要求します。これは、問題に対する最も洗練された解決策になります。

または、プログラムの動作を文書化することもできます。

于 2012-11-21T17:31:04.317 に答える
0

シングルトンパターンを使ってみることができます

于 2012-11-21T15:18:27.417 に答える
-2

スタックにクラスを割り当てるには、単に次のように言います。

FooClass foo; // NOTE no parenthesis because it'd be parsed 
              // as a function declaration. It's a famous gotcha.

ヒープに割り当てるには、次のように言います

std::unique_ptr<FooClass> foo(new FooClass()); //or
FooClass* foop = new FooClass(); // less safe

プログラムスコープで宣言した場合にのみ、オブジェクトはグローバルになります。

于 2012-11-21T15:13:05.257 に答える