2

オブジェクトのさまざまなバージョン、それらのサイズ、および割り当てについて質問があります。プラットフォームはSolaris8(およびそれ以降)です。

すべて共有ライブラリDにリンクするプログラムA、B、およびCがあるとします。あるクラスがライブラリDで定義されているので、それを「classD」と呼び、サイズを100バイトと仮定します。ここで、既存のバイナリBまたはCに影響を与えずに、プログラムAの次のバージョンのclassDにいくつかのメンバーを追加します。新しいサイズは、たとえば120バイトになります。プログラムAでclassDの新しい定義(120バイト)を使用し、プログラムBとCで引き続きclassDの古い定義(100バイト)を使用する必要があります。A、B、およびCはすべて、演算子「new」を使用してDのインスタンスを作成します。

問題は、オペレーターが「新規」に割り当てるメモリの量をいつ知るのかということです。コンパイル時または実行時?私が恐れていることの1つは、プログラムBとCはclassDが100バイトであることを期待しているのに対し、新しい共有ライブラリDはclassDに120バイトを必要とし、この不整合により、プログラムBとCを新しいライブラリD。言い換えると、新しいclassDが必要とする余分な20バイトの領域は、プログラムBおよびCによって他の変数に割り当てられる可能性があります。この仮定は正しいですか?

ご協力いただきありがとうございます。

4

6 に答える 6

6

クラスのサイズの変更はバイナリ互換ではありません。つまり、それclassDを使用するコードを再コンパイルせずにサイズを変更すると、未定義の動作が発生します(ほとんどの場合クラッシュします)。

この制限を回避するための一般的なトリックは、たとえばPimplイディオムclassDを使用して、バイナリ互換の方法で安全に拡張できるように設計することです。

いずれにせよ、異なるプログラムで異なるバージョンのクラスを使用したい場合は、共有ライブラリの複数のバージョンをリリースし、それらのプログラムを適切なバージョンにリンクする以外に選択肢はないと思います。

于 2009-07-06T17:08:27.683 に答える
3

コンパイル時、クライアントの下の共有オブジェクトのサイズを変更しないでください。

そのための簡単な回避策があります:

class foo
{
public:
  // make sure this is not inlined
  static foo* Create()
  {
     return new foo();
  }
}

// at the client
foo* f = foo::Create();
于 2009-07-06T16:48:31.173 に答える
2

正しいメモリサイズはコンパイル時に定義され、アプリケーションB/Cは深刻なメモリ破損の問題の危険にさらされます。

これを言語レベルで明示的に処理する方法はありません。OSと連携して、適切な共有ライブラリをアプリケーションに取り込む必要があります。

ライブラリをバージョン管理する必要があります。

ビルドツールでこれを行う明示的な方法はないため、ファイル名で行う必要があります。ほとんどの製品を見ると、これはおおよその動作です。

libディレクトリ内:

libD.1.00.so
libD.1.so     ->  libD.1.00.so    // Symbolic link
libD.so       ->  libD.1.so      // Symbolic link

ここで、コンパイル時に-lDを指定すると、シンボリックリンクをたどるため、libD.1.00.soに対してリンクします。これはコンパイル対象のバージョンであるため、実行時にこのバージョンを使用することを認識しています。

したがって、libDをバージョン2.0に更新します。

libディレクトリ内:

libD.1.00.so
libD.2.00.so
libD.1.so     ->  libD.1.00.so    // Symbolic link
libD.2.so     ->  libD.2.00.so    // Symbolic link
libD.so       ->  libD.2.so       // Symbolic link

これで、-libDを使用してビルドすると、バージョン2に対してリンクされます。したがって、Aを再ビルドすると、今後はバージョン2のlibが使用されます。BとCは引き続きバージョン1を使用しますが、BまたはCを再構築すると、-libD.1のビルド時に古いバージョンのライブラリを明示的に使用しない限り、新しいバージョンのライブラリが使用されます。

一部のリンカーはシンボリックリンクをうまくたどることを知らないため、役立つリンカーコマンドがあります。gccは「-install_name」フラグを使用します。リンカーの名前付きフラグが少し異なる場合があります。

ランタイムチェックとして、通常、バージョン情報を共有オブジェクト(グローバル変数/関数呼び出しなど)に入れることをお勧めします。したがって、実行時に共有ライブラリのバージョン情報を取得して、アプリケーションに互換性があることを確認できます。そうでない場合は、適切なエラーメッセージを表示して終了する必要があります。

また、注意:Dのオブジェクトをファイルにシリアル化する場合。Dに関するバージョン情報が維持されていることを確認する必要があることを知っています。Libd.2は、バージョン1 Dオブジェクトの読み取り方法を知っている可能性がありますが(明示的な作業が必要です)、その逆は当てはまりません。

于 2009-07-06T17:15:09.323 に答える
1

メモリ割り当ては、コンパイル時に計算されます。Dでクラスのサイズを変更すると、再コンパイルがトリガーされます。

それが当てはまる場合は、問題のクラスから公に派生して拡張することを検討してください。または、別のオブジェクトで作成します。

于 2009-07-06T16:45:42.970 に答える
1

割り当てるメモリの量は、コンパイル時に次のようなことを行うときに決定されます

new Object();

ただし、次のような動的パラメータにすることができます。

new unsigned char[variable];

私は本当にあなたが望むものを達成するためにいくつかのミドルウェアを通過することをお勧めします。C ++は、バイナリインターフェイスに関して何も保証しません。

protobufを見たことがありますか?

于 2009-07-06T16:47:35.167 に答える
0

前述の「アドホック」手法に加えて、新しいクラスAは実際には「古い」クラスAのサブクラスであると言うことで、システムへの互換性をモデル化することもできます。これにより、古いコードは引き続き機能しますが、すべてのコードが機能します。拡張機能が必要な場合は、修正する必要があります。

この設計原理は、特にインターフェイスがバージョン間で変更されることはなく、継承によってのみ拡張されるCOMの世界ではっきりとわかります。その次に、メソッドによってクラスを構築するだけです。これによりCreateInstance、割り当ての問題がクラスを含むライブラリに移動します。

于 2009-07-06T18:06:38.673 に答える