1

基本クラス Token があります。これには実装がなく、マーカー インターフェイスとして機能します。これは、呼び出し元が使用する型です。

{
    Token t = startJob(jobId);
    // ... (tasks)
    // t falls out of scope, destructors are called
}

派生クラス LockToken があります。これはミューテックスをラップし、構築中にロックが取得され、破棄中に解放されることを保証します。startJob メソッドは、トークン (ロックを提供しない) または LockToken (ロックを提供する) を返すかどうかを決定するという意味で、ファクトリ メソッドです。

Token startJob(int jobId)
{
    return (jobId>0) ? LockToken() : Token() ;
}

startJob が基本インスタンス (トークン) を返す場合、すべてがうまく機能します。それ以外の場合 (jobId>0)、派生インスタンスからベース インスタンスへのコピーが作成されます。他のワークでは、別のトークンが LockToken からコピー構築され、元の LockToken があまりにも早くスコープから外れ、startJob のスコープ内でロックが解放されます。

どうすればこの問題を解決できますか? startJob を変更して、真の共変トークン (LockToken である可能性があることを意味する) を返すか出力することはできますか?

4

5 に答える 5

9

トークンを値で返しています。つまり、LockToken を返すのではなく、LockToken インスタンスから構築されたトークンのコピーを返します。

より良いアプローチは、boost::shared_ptr を使用することです。そうすれば、クライアントは削除を心配することなくコピーできます。このようなもの:

#include <boost/shared_ptr.hpp>

typedef boost::shared_ptr<void> Token;

Token startJob(int jobId)
{
    if (jobId < 1) return shared_ptr<void>();
    return shared_ptr<void>(new LockToken);
}

void use_it()
{
    Token t = startJob(jobId);
    // ....
    // Destructors are called
}

何もしない Token クラスはもう必要ないことに注意してください。LockToken クラスは、クライアントから完全に隠されている実装の詳細であり、Token がスコープ外になったときに他のあらゆる種類のことを行うためのスコープを提供します。

于 2009-10-15T06:09:41.420 に答える
5

startJob() からポインタを返す必要があります - 生のポインタまたは適切なスマート ポインタのいずれかです。例えば:

Token* startJob(int jobId) 
{ 
    return (jobId>0) ? new LockToken() : new Token(); 
} 

{ 
    std::auto_ptr<Token> t = startJob(jobId); 
    // ... (tasks) 
    // t falls out of scope, destructors are called 
}

範囲外になると、ラップされたポインターをauto_ptr呼び出します。delete

上記の解決策は、あなたのものを直接書き直したものです。この質問に対する別の回答で述べたように、ダミーTokenクラスはまったく必要ありません。null ポインターを返すだけです。

LockToken* startJob(int jobId) 
{ 
    return (jobId>0) ? new LockToken() : 0; 
} 

{ 
    std::auto_ptr<LockToken> t = startJob(jobId); 
    // ... (tasks) 
    // t falls out of scope, destructors are called 
}

auto_ptrnull ポインターを安全に割り当てることができます。デストラクタがこれを処理します。

于 2009-10-15T05:57:33.567 に答える
2

かなり典型的なアプローチの 1 つは、ポインターを使用してヒープ上で Token / LockToken オブジェクトを宣言することです。

Token* startJob(int jobID)
{
    Token* t;
    if (jobID >0)
    {
        t = new LockToken();
    }
    else
    {
        t = new Token();
    }

    return t;
}

もちろん、返された値を使い終わったら、責任を持って削除する必要があります。または、独自の破棄を管理するスマート ポインターを使用することもできます。

于 2009-10-15T05:58:25.907 に答える
1

C++ では、ポリモフィックな動作を実現するには、ポインターまたは参照のいずれかを使用する必要があります。特定のケースでは、Tokenmust の有効期間が関数を超えて拡張startJobされるため、内部スタック割り当てオブジェクトに参照を返すことはできません。これは、使用場所 ( の呼び出し元startJob) ではダングリング参照になるためです。

したがって、動的に割り当てられたメモリが残ります。この時点で、ヒープに割り当てられたオブジェクトの有効期間をどのように処理するかを選択できます。生のポインターは本質的に例外的に安全ではないため、生のポインターを使用しないことをお勧めします。生のポインターを戻り値として使用し、スマートポインター内でポインターを管理する、または既にスマートポインターを返すという別の細かい答えが既にあります。

生のポインターを返し、それをスマート ポインターで外部から管理することの欠点は、ユーザー コードに対して少し壊れやすいことです。呼び出し元はスマート ポインターを使用するか、生のポインターを使用する (または返されたオブジェクトを無視する) ことができ、メモリが失われます。ユーザー インターフェイスでshared_ptrを使用すると、呼び出し元のコードでそのスマート ポインターを使用する必要があります (ユーザーは、別のスマート ポインター型に変更することを決定できません)。

古き良きstd::auto_ptrを戻り値の型として使用することは、現時点では最も柔軟なアプローチのようです。

std::auto_ptr<Token> startJob( int jobId );

void user_code()
{
   std::auto_ptr<Token> job1 = startJob(1);
   boost::shared_ptr<Token> job2( startJob(2) ); // shared_ptr has a constructor taking auto_ptr
   startJob(3); // fine: the temporary auto_ptr dies and releases the memory
   boost::scoped_ptr<Token> job4( startJob(4).release() ); // cumbersome, but feasible
}

( scoped_ptr参照)

返された型が戻り値の型として別の型のスマート ポインターである場合、別の型のスマート ポインターで使用するリソースを生成することはできません。生のポインター ジョブ 3 トークンを返した場合、トークンは解放されません。

議論のためにunique_ptrを考慮していません。これは auto_ptr の良い代替手段のように思えますが、私は一度も使用したことがないので、経験から判断できません。

于 2009-10-15T06:44:22.560 に答える
0

すべての返信をありがとう!std::auto_ptrの代わりにを使用することを除いて、janmのソリューションを使用することを選択しましたboost::shared_ptr。これにより、ソリューションもsharptoothの回答(Matthieu Mのコメント付き)と同様になることに注意してください。私はそれをプロトタイプ化し、他のアプリケーションと統合しました。それがうまく機能していることを報告できてうれしいです。

于 2009-10-15T23:39:14.653 に答える