C++プログラマへの道 #5 - アクセス指定子

Last Edited: 9/13/2024

このブログ記事では、C++におけるアクセス指定子の仕組みを紹介します。

C++ Access

Public & Private アクセス指定子

前回の記事で、属性やメソッドを定義するときに使用する public キーワードについて簡単に触れましたが、 これはアクセス指定子の1つであり、属性やメソッドを外部からアクセス可能にするものです。したがって、 publicアクセス指定子を使うと、次のようなコードを書くことができます。

class Class {
    public:
        string public_attr;
        void public_print() {
            cout << "Public Method" << endl;
        };
};
 
int main () {
    Class public;
    public.public_attr = "Public";
    cout << public.public_attr << endl; // => Public
    public.public_print(); // => Public Method
 
    return 0;
}

しかし、場合によっては外部からのアクセスを許可したくないこともあります。たとえば、ユーザーのパスワードや暗号化アルゴリズムを公開するのは望ましくありません。 このような場合には、privateアクセス指定子を使用できます。

class BankAccount {
    public:
        string name;
        int set_password(string password) {
            string encrypted = this -> encrypt(password);
            this -> password = encrypted;
            return 1;
        };
    private:
        string password;
        string encrypt(string password) {
            // Encryption
        };
};
 
int main () {
    BankAccount user;
    user.name = "TK"; 
    user.password = "abcdefg"; // => Err
 
    user.set_password("abcdefg");
    user.encrypt("abcdefg"); // => Err
    return 0;
};

privateアクセス指定子は内部の属性やメソッドを外部から隠すため、カプセル化と関心の分離を実現できます。 たとえば、機械学習モデルを構築する際、モデルの重みやバイアスはprivateアクセス指定子を使用して、 トレーニングを通じてのみ変更できるようにすることができます。

ユーザーがモデルの重みやバイアスにアクセスしたい場合は、それらを適切にフォーマットするpublicなgetter関数を介して行うべきです。 初期化するときには、ユーザーが手動で初期化する代わりに、適切に初期化するsetter関数を使用するべきです。

Protected アクセス指定子

BankAccountクラスをABC銀行のABCBankAccountクラスとして継承する際、BankAccountのprivate属性にはアクセスできません。

class ABCBankAccount : public BankAccount {
    public:
        float balance;
    ABCBankAccount(string name, string password) : name(name) {
        string encrypted = this -> encrypt(password); // => Err
        this -> password = encrypted; // => Err
        balance = 0;
    ;}
};

これは、privateアクセス指定子が外部からのアクセスを子クラスも含めて禁止するためです。 子クラスにアクセスを許可しながら外部からのアクセスを防ぐには、protectedアクセス指定子を使用できます。

class BankAccount {
    public:
        string name;
        int set_password(string password) {
            string encrypted = this -> encrypt(password);
            this -> password = encrypted;
            return 1;
        };
    protected:
        string password;
        string encrypt(string password) {
            // Encryption
        };
};
 
class ABCBankAccount : public BankAccount {
    public:
        float balance;
    ABCBankAccount(string name, string password) : name(name) {
        string encrypted = this -> encrypt(password); // => OK
        this -> password = encrypted; // => OK
        balance = 0;
    ;}
};
 
int main () {
    ABCBankAccount user("TK", "abcdefg");
    cout << user.password << endl; // => Err
    user.encrypt("abcdefg"); // => Err
    return 0;
}

基底クラスのアクセス指定子

クラスの継承を使用するときに、class ABCBankAccount : public BankAccountのようにpublicキーワードが使われているのを見たことがあるかもしれません。 ここでのpublic基底クラスのアクセス指定子と呼ばれ、基底クラスの属性やメソッドが子クラスにどのように継承されるかを決定します。 以下の表は、基底クラスのアクセス指定子に応じてメンバーのアクセスレベルがどのように変わるかを示しています。

基底クラスのアクセス指定子とメンバーのアクセスレベルの変化
基底クラスのアクセス指定子基底クラスのPublicメンバー基底クラスのProtectedメンバー基底クラスのPrivateメンバー
publicPublicProtectedアクセス不可
protectedProtectedProtectedアクセス不可
privatePrivatePrivateアクセス不可

ご覧のとおり、基底クラスのprivateメンバーは、基底クラスのアクセス指定子に関係なく、子クラスを含めて誰からもアクセスできません。 しかし、publicとprotectedメンバーのアクセスレベルは、基底クラスのアクセス指定子によって変わります。

基底クラスのアクセス指定子がpublicの場合、基底クラスのpublicおよびprotectedメンバーは子クラスでも同じアクセスレベルを維持します。 一方で、基底クラスのアクセス指定子がprotectedprivateの場合、基底クラスのpublicおよびprotectedメンバーは、 子クラス内でそれぞれprotectedまたはprivateに変更されます。(基底クラスのアクセス指定子を指定しない場合、デフォルトではprivateになります。)

リソース