モーダルを閉じる工作HardwareHub ロゴ画像

工作HardwareHubは、ロボット工作や電子工作に関する情報やモノが行き交うコミュニティサイトです。さらに詳しく

利用規約プライバシーポリシー に同意したうえでログインしてください。

工作HardwareHub ロゴ画像 (Laptop端末利用時)
工作HardwareHub ロゴ画像 (Mobile端末利用時)

インタフェースクラス (C++をもう一度)

モーダルを閉じる

ステッカーを選択してください

モーダルを閉じる

お支払い内容をご確認ください

購入商品
」ステッカーの表示権
メッセージ
料金
(税込)
決済方法
GooglePayマーク
決済プラットフォーム
確認事項

利用規約をご確認のうえお支払いください

※カード情報はGoogleアカウント内に保存されます。本サイトやStripeには保存されません

※記事の執筆者は購入者のユーザー名を知ることができます

※購入後のキャンセルはできません

作成日作成日
2014/12/23
最終更新最終更新
2021/10/07
記事区分記事区分
一般公開

目次

    低レイヤーのプログラミングとOS開発が趣味。C言語を使っています。

    0
    ステッカーを贈るとは?

    C++にはJavaなどと異なりインタフェースという機能が存在しません。C++では純粋仮想関数と仮想デストラクタしかメンバをもたないクラスをインタフェースとして利用します。その際、多重継承や仮想継承の知識が必要になります。

    多重継承

    #include <iostream>
    using namespace std;
    
    class MyClass {
    public:
        virtual ~MyClass() {} // 仮想デストラクタ (インラインメンバ関数として)
        virtual void Show() = 0;
        virtual void SShow() = 0;
        void Set(int intval) {
            m_intval = intval;
        }
    
    protected:
        int m_intval;
    };
    
    class MySubClassA :
        public MyClass
    {
    public:
        virtual ~MySubClassA() {}
        virtual void Show() {
            cout << "MySubClassA::Show(" << m_intval << ")" << endl;
        }
        virtual void SShow() {
            cout << "MySubClassA::SShow" << endl;
        }
    };
    
    class MySubClassB :
        public MyClass
    {
    public:
        virtual ~MySubClassB() {}
        virtual void Show() {
            cout << "MySubClassB::Show(" << m_intval << ")" << endl;
        }
        virtual void SShow() = 0;
    };
    
    class MySubSubClass : // 多重継承。MySubClassA, MySubClassB を介して
        public MySubClassA, // MyClass を二つ継承している。MyClass の実体が二つ。
        public MySubClassB
    {
    public:
        virtual void SShow() {
            cout << "MySubSubClass::Show("
                 << MySubClassA::m_intval << ","
                 << MySubClassB::m_intval << ")" << endl;
        }
    };
    
    int main() {
        MySubSubClass ss;
        MySubClassA& sA = ss; // アップキャスト
        MySubClassB& sB = ss;
        MyClass& bA = static_cast<MySubClassA&>(ss); // アップキャスト (二段飛び)
        MyClass& bB = static_cast<MySubClassB&>(ss); // (曖昧さ回避のため経由する一段目にまずキャスト)
    
        // 実体は二つあるため別々の値を設定可能
        sA.Set(1); // 「MySubClassA経由のMyClass」
        sB.Set(-1); // 「MySubClassB経由のMyClass」
    
        sA.Show(); //=> MySubClassA::Show(1)
        sB.Show(); //=> MySubClassB::Show(-1)
        bA.Show(); //=> MySubClassA::Show(1)
        bB.Show(); //=> MySubClassB::Show(-1)
    
        // SubSubで実装すると二つの実体の両方がオーバライドされる
        ss.SShow(); //=> MySubSubClass::Show(1,-1)
        sA.SShow(); //=> MySubSubClass::Show(1,-1)
        sB.SShow(); //=> MySubSubClass::Show(1,-1)
        bA.SShow(); //=> MySubSubClass::Show(1,-1)
        bB.SShow(); //=> MySubSubClass::Show(1,-1)
    
        return 0;
    }
    

    仮想継承

    #include <iostream>
    using namespace std;
    
    class MyClass {
    public:
        MyClass(int intval) : m_intval(intval) {}
        virtual ~MyClass() {}
        virtual void Show() = 0;
    
    protected:
        int m_intval;
    };
    
    class MySubClassA :
        virtual public MyClass // 仮想継承
    {
    public:
        MySubClassA() : MyClass(1) {} // 継承における引数のあるコンストラクタ
        virtual ~MySubClassA() {}  
        virtual void Show() {
            cout << m_intval << flush;
        }
    };
    
    class MySubClassB :
        virtual public MyClass // 仮想継承
    {
    public:
        MySubClassB() : MyClass(-1) {}
        virtual ~MySubClassB() {}
    };
    
    class MySubSubClass :
        public MySubClassA, // 多重継承
        public MySubClassB  // (MyClassの実体は共有され一つ。ダイヤモンド継承)
    {
    public:
        MySubSubClass() : MyClass(0) {} // MySubClassA, MySubClassB のどちらで MyClass()
        // を実行したらよいか曖昧なため MySubSubClass で実行。仮想継承のはまりどころです。
    
        virtual void Show() { // 仮に MySubClassA, MySubClassB の両方で Show を実装したと
            // すると一つの MyClass に対して Show の実体が二つになりエラーです。
            // MySubSubClass で Show を実装するとエラー回避できます。
            cout << "MySubSubClass::Show(" << flush;
            MySubClassA::Show();
            cout << "," << MySubClassB::m_intval << ")" << endl;
        }
    };
    
    int main() {
        MySubSubClass ss;
    
        MySubClassA& sA = ss;
        MySubClassB& sB = ss;
        MyClass& b = ss; // 実体は一つであるため直接二段飛びのアップキャストが可能
    
        // sA経由でもsB経由でも同じ実体
        ss.Show(); //=> MySubSubClass::Show(0,0)
        sA.Show(); //=> MySubSubClass::Show(0,0)
        sB.Show(); //=> MySubSubClass::Show(0,0)
        b.Show(); //=> MySubSubClass::Show(0,0)
    
        // こうすると、MyClassのコンストラクタの引数が異なることに注目
        MySubClassA _sA;
        // MySubClassB _sB; // MySubClassBは抽象クラスなためエラー (Show が未実装)
        _sA.Show(); cout << endl; //=> 1 (← 0でないことがポイントですね)
    
        return 0;
    }
    

    インタフェースクラス

    純粋仮想関数と仮想デストラクタしかメンバをもたないクラスをインタフェースとして利用します。インタフェースクラスを仮想継承しているクラスには所定のメンバ関数が実装されていることが保証されます。

    #include <iostream>
    using namespace std;
    
    class IMyClass { // インタフェースクラス
    public:
        virtual ~IMyClass() {}
        virtual void Show() const = 0;
    };
    
    class IMySubClassA : // インタフェースクラス
        virtual public IMyClass
    {
    public:
        virtual ~IMySubClassA() {}
        virtual void ShowA() const = 0;
    };
    
    class IMySubClassB : // インタフェースクラス
        virtual public IMyClass
    {
    public:
        virtual ~IMySubClassB() {}
        virtual void ShowB() const = 0;
    };
    
    class MyClass :
        virtual public IMySubClassA, // 仮想継承でインタフェースクラスを利用
        virtual public IMySubClassB // (実体が一つであることを保証できるため)
    {
    public:
        virtual void Show() const {
            cout << "Show" << endl;
        }
        virtual void ShowA() const {
            cout << "ShowA" << endl;
        }
        virtual void ShowB() const {
            cout << "ShowB" << endl;
        }
    };
    
    void MyFunc(const IMyClass& obj, // アップキャスト
                const IMySubClassA& objA,
                const IMySubClassB& objB) {
        objA.ShowA(); //=> ShowA
        objB.ShowB(); //=> ShowB
    
        // インタフェース間の継承関係のため
        // ISub は IBase の関数を知っている
        obj.Show(); //=> Show
        objA.Show(); //=> Show
        objB.Show(); //=> Show
    }
    
    int main() {
        MyClass obj;
        MyFunc(obj, obj, obj);
        return 0;
    }
    
    0
    詳細設定を開く/閉じる
    アカウント プロフィール画像 (本文下)

    低レイヤーのプログラミングとOS開発が趣味。C言語を使っています。

    記事の執筆者にステッカーを贈る

    有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。

    さらに詳しく →
    ステッカーを贈る コンセプト画像

    Feedbacks

    Feedbacks コンセプト画像

      ログインするとコメントを投稿できます。

      関連記事