1

クラス定義のプライベート部分で何かが変更されたという理由だけで、パブリック ヘッダー ファイルを含むすべての再コンパイルを回避したいと考えています。PIMPL以外のオプションを調査しています。

これは私が試したものです:

クラス A を含むライブラリを作成しました。

A_p.hには、クラス A のプライベート部分が含まれています

void PrivateMethod(int i);

ああ、公開ヘッダー ファイル:

class A
{
public:
    A();
    virtual ~A();
    // other public members
private:
#ifdef A_PRIVATE
#include "A_p.h"
#endif
};

A.cpp

#define A_PRIVATE
#include "A.h"

A::A() {}
A::~A() {}
void A::PrivateMethod(int i) { }

次に、パブリック ヘッダー (Ah) と .lib ファイルに対するリンクを含む Win32 コンソール プロジェクトを作成しました。

すべてが機能しているように見えますが、途中で落とし穴がないか疑問に思っています。誰でもこれについて詳しく説明できますか?

4

4 に答える 4

6

「すべてがうまくいっているようだ」 -そこにあるように見えることが不可欠です。未定義の動作が発生しているだけです。これは不正なプログラムです。クラス定義は、そのクラスを使用するコンパイル単位全体で同一でなければなりません。

virtualこれは UB であるため、機能しているように見えますが、private セクションでメソッドを宣言してみると、目に見える問題が発生する可能性が高くなります。

于 2013-04-10T15:47:31.317 に答える
1

抽象クラスでは、抽象クラスをサブクラス化することにより、パブリック インターフェイスを宣言できますが、プライベート データと関数を使用できます。

これが説明どおりに機能せず、したがって C++ 標準でサポートされない主な理由は、提案された public 宣言によって のサイズを知ることができなくなるためですA。public 宣言では、プライベート データに必要な容量は明らかにされません。したがって、 public 宣言のみを参照するコードは、 を実行new Aできず、 の配列の定義にスペースを割り当てることができずA、 へのポインタを使用して算術演算を実行できませんA

仮想関数メンバーへのポインターが配置されている場所など、何らかの方法で解決される可能性のある他の問題があります。ただし、これは不必要な複雑さを引き起こします。

抽象クラスを作成するには、クラスで少なくとも 1 つの仮想関数を宣言します。仮想関数は= 0、関数本体の代わりに で定義されます。これは、実装がないことを示しているため、抽象クラスから派生したクラスのサブオブジェクトを除いて、抽象クラスのオブジェクトは存在できません。

次に、別のプライベート コードで、Bから派生したクラスを宣言および定義しますA。オブジェクトを作成および破棄する方法を提供する必要があります。おそらく、へのポインターを返し、Aの宣言を確認できるプライベート関数とBへのポインターを受け取るパブリックの「削除」関数を呼び出すことによって機能するパブリックの「新しい」関数を使用します。Aの宣言を見ることができるプライベート関数を呼び出すことで機能しますB

于 2013-04-10T16:18:53.073 に答える
1

この種の情報を隠すには、次の 3 つの良い方法があります。

  1. クラスを前方宣言するだけです。これは、クライアント コードを介して (ポインターおよび/または参照を介して) 渡され、ライブラリ内でのみ使用される場合にのみ機能します。ライブラリは、最初にポインター/参照を返すためにファクトリ関数などを提供する必要があります。クライアントは呼び出したり、呼び出しnewたりすることはできません。delete

  2. 抽象基本クラスを公開し、再びファクトリ関数を提供します (ライブラリ内でのみ表示される具体的な派生クラスをインスタンス化します)

  3. ニキビを使う

また、非常に大きなクラスを再検討して非表示にする必要があることにも同意しますが、本当に分割できない場合は、それがオプションです。


ODR 違反が実際に問題を起こす方法と理由については、次のとおりです。

  • ライブラリとそのクライアント コードは、インスタンスのサイズについて異なる意見を持つことができます。これにより、割り当て/割り当て解除などの問題が発生する可能性があります。
  • また、異なるデータ メンバー オフセット、vtable レイアウトなども期待できます。
    • PS。インライン化されたメソッドは、新しい動的ライブラリ ビルドをドロップしても更新されないため、これらの目的ではクライアント コードとしてカウントされます。
    • PPS。新しいオプティマイザは、知らず知らずのうちにいくつかのアウトオブラインメソッドをインライン化できる可能性があります
于 2013-04-10T16:31:16.297 に答える
0

書かれているように、適切な部分クラスは C++ では不可能であり、場合によってはこれが実際に面倒です。継承と Pimpl パターンは代替手段を提供しますが、オブジェクトごとに 1 つのポインターのオーバーヘッドがあり、RAM が限られている組み込みソフトウェアでは高くなる可能性があります。

この問題を解決するために、ISO への公式の「部分クラス」提案がありました。

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0309r0.pdf

残念ながら提案は却下されました。

于 2021-01-11T14:50:10.717 に答える