Help us understand the problem. What is going on with this article?

[C++] usingを使用したコンストラクタの継承

C++で書かれたライブラリを読解していて初めて知ったので備忘録として残しておきます。
何か間違いがありましたらコメントか編集リクエストをいただけると幸いです。

はじめに

この記事では using Base::Base のような継承コンストラクタの説明記事になります。
前置きがいらない方は コンストラクタの継承 から本題へどうぞ。

継承とコンストラクタ

継承とは親クラスのメソッドやプロパティを受け継ぐというオブジェクト指向を構成する重要な概念の1つです。
C++では以下の様にクラスを継承します。

classBがclassAを継承する関係
class A {};
class B : public A {};

オブジェクト指向では、継承されたクラスを親(スーパー)クラス、継承するクラスを子(サブ)クラスと呼びます。
子クラスからは親クラスの要素にアクセスできるようになります。

継承
class A {
public:
    void display() {
         printf("called display\n");
    }
};

class B : public A {};

int main() {
    B b;
    b.display(); // "called display"
}

また、親子クラスのコンストラクタは「親 -> 子」の順序で実行されます。

コンストラクタの実行順序と引数の引き渡し
class A {
public:
    A (int a, int b) {
         printf("A, %d\n", a + b);
    }

    void display() {
         printf("called display\n");
    }
};

class B : public A {
public:
    B (int a, int b) : A(a + 1, b + 1) {
         printf("B, %d\n", a + b);
    }
};

int main() {
    B b(1, 2); // "A, 5" "B, 3"
    b.display(); // "called display"
}

上記の通り、引数を持つ親クラスのコンストラクタを呼び出す場合は子クラスのコンストラクタに : <SuperClass>(Args) と書くことで引数を引き渡してコンストラクタを呼び出すことが可能です。

コンストラクタの継承

前節で、引数のある親クラスのコンストラクタの呼び出しに : <SuperClass>(Args) を使用する方法を紹介しました。
では、親クラスが以下のような状況を考えてみます。

class A {
public:

    A (int a) {}
    A (int a, char b) {}
    A (std::string str) {}
    A (int a, int b, int c, int d, int e) {}
    A (int a, int b) {
         printf("A, %d\n", a + b);
    }

    void display() {
         printf("called display\n");
    }
};

class B : public A {
public:
    B (int a, int b) : A(a + 1, b + 1) {
         printf("B, %d\n", a + b);
    }

    B (int a) : A(a) {}
    B (int a, char b) : A(a, b) {}
    B (std::string str) : A(str) {}
    B (int a, int b, int c, int d, int e) : A(a, b, c, d, e) {}
};

子クラスでは引数のある親クラスのコンストラクタを呼び出す処理を個別に書かなければいけないため非常に面倒くさいです。
また、子クラスの4つのコンストラクタは引数をそのまま親クラスに引き渡しているだけで、子クラスのコンストラクタが特別に何かの処理をしているわけではありません。

このような子クラスから親クラスのコンストラクタに引数を転送するようなコードを省略して簡単に書けるような機能としてC++11から継承コンストラクタという機能が導入されました。

継承コンストラクタでは using A::A のように using キーワードの後に親クラス名とコンストラクタ名を::区切りで記述することで親クラスのコンストラクタを直接呼び出せるようになります。

継承コンストラクタの例
class A {
public:

    A (int a) {}
    A (int a, char b) {}
    A (std::string str) {}
    A (int a, int b, int c, int d, int e) {}
    A (int a, int b) {
         printf("A, %d\n", a + b);
    }

    void display() {
         printf("called display\n");
    }
};

class B : public A {
public:
    using A::A; // 継承コンストラクタ

    // コンストラクタ毎に個別の処理を書くこともできる。
    B (int a, int b) : A(a + 1, b + 1) {
         printf("B, %d\n", a + b);
    }
};

int main() {
    B b1(1, 2);
    B b2(1, 2, 3, 4, 5);
    b1.display(); // "called display"
}

このように子クラスに using A::A; という記述を追加することで引数を転送するだけのコンストラクタを省略できるようになりました。
ただし、これまでと同じ処理を書くことでコンストラクタ毎に個別の処理を定義することも可能です。

参考文献

https://cpprefjp.github.io/lang/cpp11/inheriting_constructors.html

seriru13
競技プログラミングとかwebとかゲームとか。 C++が好きなC++初心者です。たまにTSも書きます。最近はよくC#を書いてます。 ライブラリとかゲームとか作って見たい。
https://ryo-s1126.hatenablog.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした