質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.31%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

2950閲覧

基底クラスをいじらずにアップキャストを禁止したい

youp

総合スコア12

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

1クリップ

投稿2021/07/23 11:29

編集2021/07/23 13:14

環境

言語:c++11以降
コンパイラは問いません

質問

クラスAをpublic継承したクラスBを定義します
このままではBはAに暗黙にキャスト可能です
クラスAをいじらずに BからAへのアップキャストを禁止する方法はありますか

cpp

1 2struct A { 3 A() = default; 4 ~A() = default; 5 A(A&) = default; 6 A& operator=(A&) = default; 7 A(A&&) = default; 8 A& operator=(A&&) = default; 9 10public://関数 11 void a() {} 12 void b() {} 13 14}; 15 16struct B :public A { 17 18}; 19 20int main() { 21 B b; 22 A a; 23 24 b.a();//Aのメンバ関数は使いたい 25 26 a = b;//upcastを禁止したい 27}

試した方法

protected継承にして関数をusingで一つずつ使えるようにする

デメリット

  • Aに関数が追加されたときにB側も変える必要がある
  • 書くのがめんどくさい
  • 視覚的に複雑に見える

cpp

1 2struct A { 3 A() = default; 4 ~A() = default; 5 A(A&) = default; 6 A& operator=(A&) = default; 7 A(A&&) = default; 8 A& operator=(A&&) = default; 9 10public://関数 11 void a() {} 12 void b() {} 13 /* 14 . 15 . 16 . 17 */ 18}; 19 20struct B :protected A { 21 using A::a; 22 using A::b; 23 /* 24 using A::c; 25 using A::d; 26 using A::e; 27 . 28 . 29 . 30 */ 31}; 32 33int main() { 34 B b; 35 A a; 36 37 b.a(); 38 //a = b;//禁止できた 39}

もっと簡潔に書く方法がないかと思い質問させていただきました
よろしくお願いいたします

追記

質問欄より

なぜ禁止したいのでしょうか?

次のようなことを考えていました

クラスAは他者のコードをライブラリとして使っているため いじりたくありません(ライセンスによっては編集できない場合もある)

そのクラスAにはcopy moveコンストラクタがdefault宣言されています
しかし、私はcopy moveコンストラクタも使われたくありません

なので、継承してcopy moveコンストラクタをdelete宣言しました

誤ってアップキャストしてしまうと↑目的が達成できません
また、classAがvirtualデストラクタでもないためclassBが解放されない事故も起きてしまうように思います

cpp

1 2struct A { 3 A() = default; 4 ~A() = default; 5 A(A&) = default; 6 A& operator=(A&) = default; 7 A(A&&) = default; 8 A& operator=(A&&) = default; 9 10public://関数 11 void a() {} 12 void b() {} 13}; 14 15//copy move を delete 16struct B : public A{ 17 B() = default; 18 ~B() = default; 19 B(B&) = delete; 20 B& operator=(B&) = delete; 21 B(B&&) = delete; 22 B& operator=(B&&) = delete; 23}; 24 25int main(){ 26 27 { 28 std::unique_ptr<A> test1 = std::make_unique<B>(); 29 }//B::~Bが呼ばれない 30 31 A* test2 = new B(); 32 delete test2;//B::~Bが呼ばれない 33} 34

よく考えたらアップキャストを禁止したところで抜け穴はありますね


回答欄より

is a関係の派生クラスではなく、has a関係のネストクラスを使うのはどうですか?

is aとhas aについてあまり理解していませんが、それとなく機能しそうです
ありがとうございます

cpp

1struct A { 2 A() = default; 3 ~A() = default; 4 A(A&) = default; 5 A& operator=(A&) = default; 6 A(A&&) = default; 7 A& operator=(A&&) = default; 8 9public://関数 10 void a() {} 11 void b() {} 12 13 14}; 15 16struct B { 17 B() = default; 18 ~B() = default; 19 B(B&) = delete; 20 B& operator=(B&) = delete; 21 B(B&&) = delete; 22 B& operator=(B&&) = delete; 23 24public: 25 A* operator ->() noexcept { 26 return &a; 27 } 28 29private: 30 A a{}; 31 32}; 33 34int main() { 35 B b; 36 b->a(); 37} 38

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2021/07/23 11:47

なぜ禁止したいのでしょうか?
guest

回答2

0

ベストアンサー

また、classAがvirtualデストラクタでもないためclassBが解放されない事故も起きてしまうように思います

それは、もとから継承できる設計になっていないクラスということです。「きちんと継承できるように準備する」か「finalを付与して継承禁止を明示するか」、態度をはっきりさせるべきと考えます。

投稿2021/07/23 13:28

編集2021/07/23 13:32
maisumakun

総合スコア146536

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

is a関係の派生クラスではなく、has a関係のネストクラスを使うのはどうですか?

以下追記です。

is a関係は派生クラスと基底クラスの関係です。has a関係は入れ子関係です。has a関係の例は下のとおりです。

C++

1class A 2{ 3//中身 4}; 5class B 6{ 7 A a;//class A型のインスタンスをメンバとして持つ。 8//中身 9};

こうすれば、クラスBはクラスAの機能を利用できる構成となります(派生でない方法で実現)

投稿2021/07/23 12:24

編集2021/07/24 07:07
HogeAnimalLover

総合スコア4853

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.31%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問