C ++およびCでのカプセル化


定義


カプセル化は、データまたはそのデータを管理する方法へのアクセスを制御するためのツールのセットです。 「カプセル化」という用語の詳細な定義は、このリンクの Habréに関する以前の出版物に記載されています。 この記事では、C ++およびCのカプセル化の例を中心に説明します。


C ++でのカプセル化


デフォルトでは、クラス( class )のデータとメソッドはprivate( private )です。 それらは、所属するクラスによってのみ読み取りおよび変更できます。 アクセスレベルは、C ++が提供する対応するキーワードを使用して変更できます。


C ++ではいくつかの修飾子が使用可能であり、次のようにデータアクセスを変更します。



簡潔にするために、2つのレベル(プライベートおよびパブリック)のみが例で強調表示されます。


カプセル化の例


Contactクラスでは、パブリック変数とメソッドにメインプログラムからアクセスできます。 プライベート変数とメソッドは、クラス自体によってのみ読み取り、呼び出し、または変更できます。


 #include <iostream> using namespace std; class Contact { private: int mobile_number; // private variable int home_number; // private variable public: Contact() // constructor { mobile_number = 12345678; home_number = 87654321; } void print_numbers() { cout << "Mobile number: " << mobile_number; cout << ", home number: " << home_number << endl; } }; int main() { Contact Tony; Tony.print_numbers(); // cout << Tony.mobile_number << endl; // will cause compile time error return 0; } 

メインプログラム( main )からプライベート変数mobile_numberまたは変更しようとすると、クラス内のプライベートデータへのアクセスが制限されるため、コンパイルエラーが発生します。


友人とのカプセル化違反(グッドプラクティス)


C ++には、データにアクセスするための一般的な規則に例外を追加できるキーワード「friend」があります。 関数またはクラスがContactクラスのフレンドと呼ばれる場合、保護されたデータまたはプライベートデータに無料でアクセスできます。


友情には2つの基本的なルールがあります。友情は継承されず、相互ではありません。 また、「友人」の存在はデータセキュリティのレベルを変更しません。「友人」の形を除いて、プライベートデータはプライベートのままです。


 #include <iostream> using namespace std; class Contact { private: int mobile_number; // private variable int home_number; // private variable public: Contact() // constructor { mobile_number = 12345678; home_number = 87654321; } // Declaring a global 'friend' function friend void print_numbers( Contact some_contact ); }; void print_numbers( Contact some_contact ) { cout << "Mobile number: " << some_contact.mobile_number; cout << ", home number: " << some_contact.home_number << endl; } int main() { Contact Tony; print_numbers(Tony); return 0; } 

この例では、 print_numbers()関数はContactクラスのメソッドではなく、通常の関数です。 print_numbers()関数がプライベートデータにアクセスできる唯一の理由は、 print_numbers()関数をContactクラスの「フレンド」 print_numbers()宣言することです。 友人の定義を含む行を削除すると、コードはコンパイルされません。


:友人を虐待しないことが最善です。 友人を追加することは、一般的な慣行としてではなく、例外として考慮されるべきです。


型変換とポインターを使用したカプセル化違反(悪いプラクティス)


まず、この方法でポインターと型変換を使用するのは悪い考えであることは注目に値します。 このメソッドは、必要なデータの受信を保証しません。 読み取りも保守も不十分です。 それにもかかわらず、彼は存在しています。


C ++はCから多くのツールを継承し、そのうちの1つはtypecastingです。 デフォルトでは、クラス内のすべての変数とメソッドはプライベートです。 同時に、構造体( struct )のデータアクセスの標準レベルはパブリックです。 Contactクラスのデータと同じ場所にデータが配置される構造または完全にパブリックなクラスを作成し、型変換を使用してプライベートデータにアクセスすることができます。


 #include <iostream> using namespace std; class Contact { private: int mobile_number; // private variable int home_number; // private variable public: Contact() // constructor { mobile_number = 12345678; home_number = 87654321; } void print_numbers() { cout << "Mobile number: " << mobile_number; cout << ", home number: " << home_number << endl; } }; struct Contact_struct { int mobile_number; int home_number; }; int main() { Contact Tony; Contact_struct * structured_Tony; Tony.print_numbers(); structured_Tony = (Contact_struct *) & Tony; structured_Tony->mobile_number = 20; structured_Tony->home_number = 30; Tony.print_numbers(); return 0; } 

タイプ変換によりプライベートデータが読み込まれ、変更されました


Cカプセル化


カプセル化は、従来、重要なOOP原則の1つであると考えられています。 ただし、これは手続き指向言語でのこの原則の使用を制限するものではありません。 Cでは、「プライベート」および「パブリック」というキーワードがないにもかかわらず、カプセル化が長い間使用されてきました。


プライベート変数


カプセル化のコンテキストでは、Cのすべてのデータはデフォルトでパブリックと見なされます。 構造体( struct )内の変数へのアクセスレベルは、その定義をメインプログラムから分離することでプライベートに変更できます。 個別のヘッダー(ヘッダー、.h)ファイルとソース(ソース、.c)ファイルを使用することで、目的の効果を実現できます。


この例では、構造は別のソースファイル「private_var.c」で定義されています。 Cで構造体を初期化するにはメモリの割り当てと解放が必要なため、いくつかのヘルパー関数が追加されました。


 #include "private_var.h" #include <stdio.h> #include <stdlib.h> struct Contact { int mobile_number; int home_number; }; struct Contact * create_contact() { struct Contact * some_contact; some_contact = malloc(sizeof(struct Contact)); some_contact->mobile_number = 12345678; some_contact->home_number = 87654321; return( some_contact ); } void delete_contact( struct Contact * some_contact ) { free(some_contact); } 

対応するヘッダーファイル「private_var.h」では、 Contact構造が宣言されましたが、その内容はメインプログラムに隠されたままでした。


 #ifndef PRIVATE_VAR #define PRIVATE_VAR struct Contact; struct Contact * create_contact(); void delete_contact( struct Contact * some_contact ); #endif /* PRIVATE_VAR */ 

したがって、「main.c」の場合、構造の内容は不明であり、プライベートデータの読み取りまたは変更を試みると、コンパイルエラーが発生します。


 #include "private_var.h" #include <stdio.h> int main() { struct Contact * Tony; Tony = create_contact(); // printf( "Mobile number: %d\n", Tony->mobile_number); // will cause compile time error delete_contact( Tony ); return 0; } 

ポインターを使用したプライベート変数へのアクセス


型変換を使用して、CおよびC ++のカプセル化を克服できますが、このアプローチについては既に説明しました。 構造体では、データが宣言の順序で配列されていることを知っているため、ポインターとポインターの算術演算は目標を達成するのに適しています。


構造内の変数へのアクセスは制限されています。 ただし、データが保存されるメモリではなく、変数のみが非表示になります。 ポインタはメモリアドレスへの参照と見なすことができ、このメモリがプログラムで使用可能な場合、このメモリに格納されているデータを読み取って変更できます。 構造体がデータを保存するメモリにポインタが割り当てられている場合、それらは読み取り可能です。 同じ構造定義(同じ「.c」および「.h」ファイル)と変更された「main.c」ファイルを使用して、アクセス制限が克服されました。


 #include "private_var.h" #include <stdio.h> int main() { struct Contact * Tony; Tony = create_contact(); int * mobile_number_is_here = (int *)Tony; printf("Mobile number: %d\n", *mobile_number_is_here); int * home_number_is_here = mobile_number_is_here + 1; *home_number_is_here = 1; printf("Modified home number: %d\n", *home_number_is_here); delete_contact( Tony ); return 0; } 

構造内のデータが読み取られ、変更されました


プライベート機能


デフォルトでは外部( extern )である関数は、いわゆるtranslation unit全体で見ることができます。 つまり、複数のファイルが1つのオブジェクトファイルにまとめてコンパイルされている場合、これらのファイルはいずれも、他のファイルから任意の関数にアクセスできます。 関数を作成するときに「static」 staticを使用すると、定義されているファイルへの可視性が制限されるため、関数のプライバシーを確​​保するには、いくつかの手順を実行する必要があります。



この例では、静的関数print_numbers()がファイル「private_funct.c」で定義されています。 ところで、 delete_contact()関数はprint_numbers()が同じファイルにあるため、正常に呼び出します。


 #include "private_funct.h" #include <stdio.h> #include <stdlib.h> struct Contact { int mobile_number; int home_number; }; struct Contact * create_contact() { struct Contact * some_contact; some_contact = malloc(sizeof(struct Contact)); some_contact->mobile_number = 12345678; some_contact->home_number = 87654321; return( some_contact ); } static void print_numbers( struct Contact * some_contact ) { printf("Mobile number: %d, ", some_contact->mobile_number); printf("home number = %d\n", some_contact->home_number); } void delete_contact( struct Contact * some_contact ) { print_numbers(some_contact); free(some_contact); } 

対応するヘッダーファイル「private_funct.h」では、 print_numbers()静的関数として宣言されprint_numbers()います。


 #ifndef PRIVATE_FUNCT_H #define PRIVATE_FUNCT_H struct Contact; struct Contact * create_contact(); static void print_numbers( struct Contact * some_contact ); void delete_contact( struct Contact * my_points ); #endif /* PRIVATE_FUNCT_H */ 

メインプログラム「main.c」は、 delete_contact()を介して間接的にprint_numbers()正常に呼び出します。これは、両方の関数が同じドキュメント内にあるためです。 ただし、メインプログラムからprint_numbers()を呼び出そうとするとprint_numbers()エラーがprint_numbers()ます。


 #include "private_funct.h" #include <stdio.h> int main() { struct Contact * Tony; Tony = create_contact(); // print_numbers( Tony ); // will cause compile time error delete_contact( Tony ); return 0; } 

プライベート機能へのアクセス


メインプログラムからprint_numbers()呼び出すことがprint_numbers()ます。 これを行うには、 gotoキーワードを使用するか、 mainプライベート関数にポインターを渡します。 どちらの方法でも、ソースファイル「private_funct.c」を変更するか、関数自体を直接変更する必要があります。 これらのメソッドはカプセル化をバイパスしてキャンセルしないため、この記事の範囲外です。


おわりに


カプセル化は、OOP言語の外部に存在します。 最新のOOP言語はカプセル化を便利で自然なものにします。 カプセル化を回避するには多くの方法があり、疑わしいプラクティスを回避することで、カプセル化をCとC ++の両方で保持できます。



Source: https://habr.com/ru/post/J444602/


All Articles