4

モジュール(.c / .hファイルペア)をCでカプセル化することについて質問があります。

人々は通常、getTime(char * time)などのアクセス関数を使用して、モジュールの内部変数にアクセスするために、それらを混乱させる機能を提供しません。

現在、私は定数データへの定数ポインターを使用しており、同じことを行うために内部構造を指しています。他のモジュールはデータを変更せずに構造内のデータを見ることができ、関数呼び出しのオーバーヘッドを節約できるため、これはうまく機能すると思います。(これは埋め込みのもののためのものなので、関数呼び出しは「高価な」ものです)。

これが有効なカプセル化であるかどうかなどについて意見をお願いします。定数ではないポインタを設定して、データを混乱させる可能性があると思いますか?例:blah.h

typedef struct {
    unsigned char data;
    unsigned int intdata;
} myStruct;

extern const myStruct * const ptrConstMyStruct;

blah.c

static myStruct thisIsMyModulesData;
const myStruct * const ptrConstMyStruct = &thisIsMyModulesData;

anotherFile.c

variable = ptrConstMyStruct->data;
ptrConstMyStruct->data = variable; //compile error!
4

4 に答える 4

2

不完全な型を使用し、ヘッダー ファイルで構造体を宣言するだけが推奨される方法です。

カプセル化の観点からすると、一貫性は、特定の関数が特定のオブジェクトを変更しないことを宣言する方法です。あなたのソリューションでは、直観に反しているように見える constness を捨てる必要があります。それとも、変更関数への引数として const ポインターを使用していませんか?

さらに、カプセル化は実装と詳細を隠すことであり、実装である構造を公開する場合はできません。

前方宣言を説明するために編集します。

MyCode.h:

struct my_struct;

MyCode.c:

struct my_struct { .... };

上記は、MyCode のユーザーが my_struct へのポインターを使用できるが、そのメンバーを検査できないことを意味します。

于 2012-05-15T16:55:21.340 に答える
1

カプセル化により、内部メカニズムを変更でき、ユーザーが内部データを変更できないようにします。これを行う 1 つの方法は、ゲッター関数とセッター関数を使用することです。これにより、カプセル化され、const を実行できるようになります。

于 2012-05-15T17:28:50.087 に答える
1

あなたの質問には、問題の真の原因を示唆する可能性のあるステートメントがいくつかあります。

他のモジュールがデータを見ることができるので、これはうまくいくと思います...

他のモジュールがデータを見ることができる場合、そのデータはカプセル化されません。他のモジュールが (生の) データを見る必要がある場合、なぜ非公開にしようとしているのですか? これは、プログラム設計の根本的な欠陥を示唆しています。

外部

C 言語でグローバル変数を使用する必要はありません (おそらく MCU ハードウェア ペリフェラル レジスタを除いて)。私は過去 10 年間、グローバル変数を使用しておらず、ほとんど専ら組み込みリアルタイム システムを扱っています。繰り返しますが、これはプログラムの設計に何か問題があることを示唆しています。

...関数呼び出しのオーバーヘッドを節約します。(これは埋め込み用であるため、関数呼び出しは「高価な」ものです)。

いいえ、そうではありません。C 言語は関数のインライン化を 13 年間サポートしてきました。古い C90 コンパイラを使用している場合でも、インライン化オプション#pragma inlineなどがあるに違いありません。インライン化されていない組み込みコンパイラはまだ見たことがありません。その上、古いコンパイラでも、最適化を有効にするだけであれば、プログラマからの明示的なヒントがなくてもインライン化を実行できます。

また、ベンチマークやオシロスコープでの測定でわかった関数呼び出しのオーバーヘッドがプログラムのボトルネックになっていませんか?そうでない場合、プライベート データを公開するグローバル変数と奇妙なポインターでコードを難読化するのはなぜですか? それは時期尚早の最適化です。

于 2012-05-15T19:49:54.887 に答える
0

構造の「ユーザー」がそれを読み取る場所とデータがある場所の間にコードを挿入する機能がない限り、カプセル化ではありません。このようなシムを使用すると、構造の外部使用を変更せずに、構造の内部を変更できます。

あなたのソリューションはCで通常行われる方法の改善ですが、それをカプセル化するには、「外部」コードを変更せずにフィールドを単一の値から構築された値に変更できる必要があります。本当にカプセル化されています。

C では、これは通常、データを void ポインターの背後に隠すか、カプセル化されたコードの外部部分で宣言された (ただし未定義の) 構造体を使用することによって実行されます。

何とか

struct myStruct_t;
typedef struct myStruct_t myStruct;

extern myStruct * const ptrConstMyStruct;

// if you want a constructor, you need to declare the
// method here, because only blah.c will know the struct
// size with this solution.
myStruct * new_myStruct();

// methods
myStruct_setData(myStruct* obj, char c);
char myStruct_getData(myStruct* obj);

何とかc

#include "blah.h"

struct myStruct_t {
    unsigned char data;
    unsigned int intdata;
};

static myStruct thisIsMyModulesData;

// no need to make the struct const, just the pointer
// otherwise, we would have to break the const just to change
// data, which now will only be done within this file.
myStruct * const ptrConstMyStruct = &thisIsMyModulesData;

anotherFile.c

#include "blah.h"
// anotherFile "knows" of a struct called myStruct, but
// it doesn't know what it contains, or even it's size.

// this is no longer possible
// now "data" is encapsulated, and can be changed
// by reimplementing `myStruct_getData(...)`
// (as long as we don't change the method signature).
variable = ptrConstMyStruct->data;

// this is the "new" way
variable = myStruct_getData(ptrConstmyStruct);

// with the old, compiler error because of 
// assigning a value to a const value.
ptrConstMyStruct->data = variable; //compile error!
                       ^
              (error occurs here)

// with the new, compiler error because of
// dereferencing a pointer to a unknown / incomplete type.
ptrConstMyStruct->data = variable; // compile error!
                ^
        (error occurs here)

ご覧のとおり、エラーの場所によって、カプセル化の有無が異なります。ptrConstMyStruct->data逆参照ではなく、代入でエラーをチェックすると、ポインタとデータの関係を変更できません。

于 2012-05-15T17:08:29.347 に答える