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

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

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

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

Q&A

解決済

4回答

1337閲覧

C++の継承で質問があります!

l-_-l

総合スコア47

C++

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

1グッド

1クリップ

投稿2019/01/29 09:38

##C++の継承で質問があります!
以下、ソースコードです。

c++

1#include <iostream> 2using namespace std; 3class A{ 4public: 5 A(); 6 virtual ~A(); 7private: 8 void out_put_start_log(); 9 void out_put_end_log(); 10 virtual void output_original_log(); 11}; 12 13 14A::A(){ 15 output_original_log(); 16 out_put_start_log(); 17} 18 19A::~A(){ 20 output_original_log(); 21 out_put_end_log(); 22} 23 24void A::out_put_start_log(){ 25 cout << "開始" << endl; 26} 27 28void A::out_put_end_log(){ 29 cout << "終了" << endl; 30} 31 32void A::output_original_log(){ 33 cout << "クラスA"; 34} 35 36 37class B : public A{ 38private: 39 void output_original_log(); 40}; 41 42void B::output_original_log(){ 43 cout << "クラスB"; 44} 45 46 47int main(void){ 48 A* a = new A(); 49 delete a; 50 B* b = new B(); 51 delete b; 52} 53

この実行結果は、

クラスA開始
クラスA終了
クラスA開始
クラスA終了

となります。

しかし、以下のようになって欲しいのですが、
クラスA開始
クラスA終了
クラスB開始
クラスB終了

どうすれば良いでしょうか?
※質問の内容以外でも、アドバイスがあればお願いします!!

bochan2👍を押しています

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

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

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

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

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

guest

回答4

0

ベストアンサー

こんにちは。

そのような時は、一種のデザイン・パターンであるCRTPを使える可能性があります。
これは正に基底クラスから派生クラスのメンバを使う時のパターンですので。(ただし、テンプレートですので、それなりに制約もあります。)

Aを使う時A<>となってしまいますが、それなりに要件を満たしていると思います。

C++

1#include <iostream> 2#include <type_traits> 3 4template<class Derived=void> 5class A 6{ 7 typedef std::conditional_t<std::is_same_v<Derived, void>, A, Derived> Type; 8public: 9 A() 10 { 11 static_cast<Type&>(*this).output_original_log(); 12 out_put_start_log(); 13 } 14 virtual ~A() 15 { 16 static_cast<Type&>(*this).output_original_log(); 17 out_put_end_log(); 18 } 19private: 20 void out_put_start_log() { std::cout << "開始" << std::endl; } 21 void out_put_end_log() { std::cout << "終了" << std::endl; } 22 void output_original_log() { std::cout << "クラスA"; } 23}; 24 25class B : public A<B> 26{ 27 friend class A<B>; 28private: 29 void output_original_log() { std::cout << "クラスB"; } 30}; 31 32int main(void) 33{ 34 A<>* a = new A<>(); 35 delete a; 36 B* b = new B(); 37 delete b; 38}

DrivedにはAを派生したクラスを与えることが前提となります。

wandbox

投稿2019/01/29 18:51

編集2019/01/29 18:52
Chironian

総合スコア23272

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

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

l-_-l

2019/01/30 13:36

お、すごい!! 出来ないのかと思ってました、ありがとうございます! まさにやりたかった事です。 ちなみに、そう言うう情報って、どうやって知りましたか? やはり、現場ですかね?
l-_-l

2019/01/31 01:42

ありがとうございます!! やっぱりteratail最強ですね! これからもよろしくお願いします!
l-_-l

2019/01/31 01:44

あれ、ベストアンサー押し間違えちゃいました、どうしよう
Chironian

2019/01/31 04:53

このコメントだけで十分です。お気になさらないで下さい。
l-_-l

2019/01/31 07:12

すみません、ありがとうございます!
Chironian

2019/01/31 12:27

げっ、+評価まで取り消すとは、流石に萎えますね。
l-_-l

2019/02/01 05:19

取り消したつもりは無かったです。 すみません。 ベストアンサー変える方法が分かったので変えておきました。 本当にごめんなさい(_ _;)
Chironian

2019/02/01 06:52

おっと、私の早とちりだったかようですね。申し訳ない。
l-_-l

2019/02/01 07:31

いえいえ、教えていただいただけで十分です。 これからもよろしくお願いします!
l-_-l

2019/02/01 08:27

早速すみません、すっかり忘れていたのですが、 実際に動かしたい環境が諸事情により、C++11でして、 typedef std::conditional_t<std::is_same_v<Derived, void>, A, Derived> Type; の部分がエラーになってしまいます。 _t と _v の部分を消せばいけるかと思ったんですが、駄目でした。 もう少しでアップデートが入るはずなので、どうしても駄目ならあきらめるんですが、、 なにか分かるのであれば、教えていただきたいです。
guest

0

C++のコンストラクタから仮想関数を呼んでも、仮想関数としての実行はなされません。

というのも、基底クラスのコンストラクタが先に実行されるため、派生クラスのメソッドを呼んでも正しく動くことが保証されないからです。

投稿2019/01/29 09:54

maisumakun

総合スコア145121

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

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

l-_-l

2019/01/29 12:19

回答ありがとうございます! なるほど、理解しました。 解決策としては、 new で生成したあとに、(初期化する関数を作って)その関数を呼ぶという感じですかね?
guest

0

Bのクラスを生成して破棄した場合、以下の順序で関数が呼ばれます。

A:A(); B:B(); B:~B(); A:~A();

つまり、AのコンストラクタではまだBは存在してないのでたとえ仮想関数を使っても呼び出すことは出来ません。
Aのデストラクタでは、Bのクラスはすでに破棄されているのでやはり呼び出すことが出来ません。

追記

一応、こういう処理でAのコンストラクタ、デストラクタをスキップさせることはできます。

class A{ bool base; A(bool _base = true):base(_base){ if(base){ ... } } ~A(){ if(base){ ... } } } class B{ B:A(false){ } }

投稿2019/01/29 09:53

編集2019/01/30 01:00
izmktr

総合スコア2856

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

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

l-_-l

2019/01/29 12:23

回答ありがとうございます!! 最初の方の呼び出せない理由は理解できたのですが、 >上書きで対処する >Aに派生フラグをつけて、Bで上書きする っていうところが、いまいち良くわかりません。 教えて下さい!
l-_-l

2019/01/30 13:39

追記、確認しました! なるほど、そういうやり方もあるんですね、勉強になります。
guest

0

表示したい内容をコンストラクタで渡しとくとか.

class A { public: A( const std::string &ClassName="ClassA" ) : m_ClassName(ClassName) { std::cout << m_ClassName << " 開始" << std::endl; } virtual ~A() { std::cout << m_ClassName << " 終了" << std::endl; } private: std::string m_ClassName; }; class B : public A { public: B() : A( "ClassB" ) {} };

投稿2019/01/29 11:06

fana

総合スコア11632

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

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

l-_-l

2019/01/29 12:16

回答ありがとうございます。 今回ならそれで良いと思うんですが、 あれはあくまでサンプルで、本当はもっと何行かにわたる処理を行いたいのです。
fana

2019/01/30 01:04

今回の例では文字列をコンストラクタ引数に渡していますが, > 何行かにわたる処理 (を実行できるもの)を渡すならば,一応,同様にできますね(その形が良いかどうかは別として).
l-_-l

2019/01/30 13:38

そうなんですね、今回は使わなそうなのですが、 知りたいので、詳しく教えて下さると嬉しいです!
fana

2019/01/31 01:10

私の回答では,コンストラクタで文字列を受け取って→それをメンバに覚えておいて→表示が必要になった時点で用いています. それと同様に,コンストラクタで処理({関数ポインタ,ファンクタ,std::function}とか)を受け取って→それをメンバに覚えておき→処理を実行する必要が生じた時点で実行する,という話です. (言葉に詳しくないのですが,ひょっとしたら,DI(dependency injection)とか呼ぶのかもしれません.違うかもしれません.)
l-_-l

2019/01/31 01:41

なるほど、ありがとうございます!! 関数ポインタは盲点でした。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問