2009-08-05 15 views
1

別のライブラリでは、我々は、構造体とを有しています。共有オブジェクト:呼び出しプログラムとライブラリとの間の(c)において

bs *new_bs(int n, double **d); 

2つの使用例があります。

(a)のメインアプリケーションは、複数の二重の行列を割り当て、構造を構築するためのライブラリを呼び出します。代替的に、オブジェクトは関数の呼び出しの結果として、ライブラリーによって生成されてもよい

b = new_bs(5, p) 

(B):第2のケースで

bs *add(bs *a, bs *b); 

、ライブラリは** Dを所有することができ必要なときにそれを解放してください。最初のケースでは、アプリケーションは配列を割り当てており、ライブラリはその配列を読み書きできます。誰に何をいつ解放すればいいのかは私には分かりません。

メインアプリケーションがstruct-memberを所有できるようにするのは問題ですか?このアプローチの問題点は何ですか?代替案は何ですか?私たちは、多数の配列を前後にコピーすることを避けようとしています。

TR

一般に

答えて

3

おかげで、それはライブラリは、そのサービスを使用しているアプリケーションを作る「契約」を文書化することが重要です。ライブラリライターは、ライブラリによって行われたすべての割り当てと、アプリケーションによって解放される必要があるものについて文書化します。どんな契約と同様、シンプルで一貫性があるのは良いことです。この特定のケースで

、私は、ライブラリも提供すべきであるとします構造体を解放します

void free_bs(bs *b) 
{ 
    if (b->d) { 
      /* free b->d here */ 
    } 
    /* free rest of the structure */ 
} 

を。ここでd配列を解放することも意味があります。アプリケーションはすべてのbs構造体へのポインタを持ち、必要がなくなったときにfree_bsを呼び出します。

たとえば、将来の使用のためにdを保持したい場合は、ライブラリ操作が完了した後、契約がもう少し複雑になります。ライブラリはまた、提供することができます:dへのポインタを返し、自由なルーチンはそれをスキップするために知っているように、空のフィールドを残し

double** bs_get_data(bs *b) 
{ 
    double **d = b->d; 
    b->d = NULL; 
    return b->d; 
} 

を。

bs_get_data()のドキュメントでは、1回だけ呼び出すことができ、この場合はアプリケーションが配列を解放する責任があることを説明する必要があります。

UPDATE:以下のあなたのコメントへの答えで :まず第一に、私は(少なくとも)次のことを仮定することによって、問題を単純化していることに注意してください。dは、単一のbs構造によって、またはアプリケーションのいずれかによって参照されています。アプリケーションとライブラリは、配列への単一の参照を相互に「渡し」ます。同じd配列をより多くのbs構造に入れたい場合は、私のアプローチでは十分ではありません。

コメント内で提案しているフラグは役に立つかもしれませんが、標準的な方法であるFWIKでは使用できません。この場合、私はいくつか簡単な参照カウントを実装することを提案します。 new_bs()では1と

typedef struct { 
     double **d; 
     int ref; 
} d_t; 

初期refdの「コピー」を受信するすべての機能では、参照カウントをインクリメント:dが周りに共有する必要があるリソースがある場合は、それを「オブジェクト」を作ります。 bs構造体が削除されたり、アプリケーションにdが不要になった場合は、参照カウントを減らしてください。ゼロになったら、解放してください。ある意味では、それは高水準言語があなたのために行うものであり、リソース管理を正常に保つ上で非常に効率的です。

だれも誰もアレイを所有していませんが、最後にそれを必要とする者は誰でもそれを解放します。

もちろん、これにより多くの作業が必要となり、コードが複雑になるため、バランスをとってみてください。それはあなたが持っているすべての構造に必要なわけではありませんが、複数の参照を保持する必要がある場合にのみ必要です。

+0

超高速応答に感謝します。 ** dの所有権をフラグする "storageMode"(内部/外部)フィールドを含めることは意味がありますか? free_bsは、内部的に所有されている場合にのみ解放されます(b-> d)。これは、呼び出し元が** dを解放する責任があるという点で、契約を明示的にするでしょうか? 構造体内に共有オブジェクトを使用する際に別の問題がありますか? OOの人たちは、このような練習に嫌になるだろうと思う!!合理的な選択肢はありますか?データのコピーは必要ありませんか? もう一度ありがとう、tr – user151410

+0

私は応答で私の答えを編集したので、私はもっと書くことができます:-)。要するに、参照カウントを考慮する必要があります。 – tsg

1

この問題については、参照カウントが過剰です。しかし、あなたのインターフェイスは*bを所有する人をより正確に指定する必要があり、ただb->dではありません。ライブラリが「必要なとき」(あなたがそれを置いたときに)いつb->dを無料にするかを私にはっきりとはわかりません。次のように

私はインターフェイスを拡張したい:どちらか外部管理が内部管理

  1. タイプbs *のすべてのポインタがあります。関数new_bsは、外部管理のポインタを返します。関数addは、内部的に管理されているものを返します。

  2. タイプbs *のポインタを返すすべての関数は、結果の管理が内部か外部かを言う必要があります。

  3. おそらく
    void bs_free_ext(bs **); // free *bs and set *bs = NULL (external mgmt) 
    void bs_free_int(bs **); // free *bs and set *bs = NULL (internal mgmt) 
    

    bs_free_extだけ*bs解放機能

    を提供する必要があります。 bs_free_int*bs(*bs)->dの両方を解放します。

  4. 安全のため、各構造体のストレージ管理を記録するフィールドを追加して、割り当てまたは解放する関数にアサーションを付けることにします。 (理由:アサーションチェックが割り当てや解放のコストに比べて安価である。)

あなたがLinux上でアプリケーションを実行できる場合は、valgrindでメモリチェックがプラスです。

P.S.ほぼすべてのインターフェイスにメモリ管理の詳細が漏れるという事実は、C言語でプログラミングすることのメリットを得るために支払うコストの1つです。

+0

こんにちはノーマン、応答ありがとう。私の小さな物はすべて、アプリケーションまたは図書館のいずれかによって同時に作成され、殺されるようです。したがって、構造体にstorage_mode(内部/外部)フィールドを追加することは、単純な解決策のようです。このソリューション自体は、Thoriannが提案したように、図書館利用者に契約書に同意させることで安全対策を追加しています。アプリケーションがライブラリにb-> dを割り当てることを許可するインスタンスがあります。したがって、別々のコンストラクタbs_new_ext&bs_new_intを持つか、storage_modeをパラメータとして渡すことは理にかなっています。両方に感謝します。 – user151410

+0

私は以前のコメントで正確に600文字を使用しました! Cの新機能であるため、ダイナミックメモリ管理の裸さと、アプリケーションとライブラリの間のメモリ位置の潜在的な非共通の共有は恐ろしいものです。 私はbsの生活の間に心配する必要があることはありますか?そして、お互いのつま先を踏み間違えて2つの当事者についてb-> d?私は、アプリケーションの要求に応じて、アプリケーションがB-> dとライブラリを計算でBSを使用するように更新することを期待しています。しかし、私は、非合意的なデータ交換が起こる予期しない方法があると確信しています!それを防ぐための安全機構はありますか? – user151410

関連する問題