C++問題集_クラスの基本3
問題1
①Humanクラスを作成。
Humanクラスには、privateなメンバ変数として名前name、身長height、体重weightを登録できるものとする。
各メンバ変数name、height、weightはHumanクラスのメンバ関数にてアクセスするものとする。
②Humanクラスを継承したStudentクラスを作成。
Studentクラスには、privateなメンバ変数として生徒番号numを登録できるものとする。
メンバ変数numはStudentクラスのメンバ関数にてアクセスするものとする。
③main関数内にて、Studentクラスのオブジェクトtaroを生成する。(コンストラクタにて初期化する。詳細は以下)
オブジェクト生成時に、基本クラスのコンストラクタにて、名前「名無し」、身長0センチ、体重0キロを設定するとともに設定内容を出力するようにする。
同様にオブジェクト生成時に、派生クラスのコンストラクタにて、生徒番号9999番を設定するとともに設定内容を出力するようにする。
④Humanクラスのメンバ関数を用いて、名前「花子」、身長155センチ、体重57キロをそれぞれ設定・出力せよ。
⑤Studentクラスのメンバ関数を用いて、生徒番号123を設定し出力せよ。
【実行結果】
$ prac1 名前を初期化 : 名無し 身長を初期化 : 0 体重を初期化 : 0 番号を初期化 : 9999 名前を設定 : 花子 身長を設定 : 155 体重を設定 : 57 番号を設定 : 123 $
【答え】
#include <iostream> #include <string> using namespace std; class Human { private: string name; int height; int weight; public: Human(string n = "名無し", int h = 0, int w = 0); void set_name(string n); void set_height(int h); void set_weight(int w); }; //class Student : Human { // よくある間違い class Student : public Human { private: int num; public: Student(int no = 9999); void set_num(int no); }; Human::Human(string n, int h, int w){ name = n; height = h; weight = w; cout << "名前を初期化 : " << name << endl; cout << "身長を初期化 : " << height << endl; cout << "体重を初期化 : " << weight << endl; } void Human::set_name(string n){ name = n; cout << "名前を設定 : " << name << endl; } void Human::set_height(int h){ height = h; cout << "身長を設定 : " << height << endl; } void Human::set_weight(int w){ weight = w; cout << "体重を設定 : " << weight << endl; } Student::Student(int no){ num = no; cout << "番号を初期化 : " << num << "\n" << endl; } void Student::set_num(int no){ num = no; cout << "番号を設定 : " << num << "\n" << endl; } int main(){ Student hanako; hanako.set_name("花子"); hanako.set_height(155); hanako.set_weight(57); hanako.set_num(123); return 0; }
問題2
※問題1の発展
①Humanクラスを作成。
Humanクラスには、privateなメンバ変数として名前name、身長height、体重weightを登録できるものとする。
各メンバ変数name、height、weightはHumanクラスのメンバ関数にてアクセスするものとする。
②Humanクラスを継承したStudentクラスを作成。
Studentクラスには、privateなメンバ変数として生徒番号numを登録できるものとする。
メンバ変数numはStudentクラスのメンバ関数にてアクセスするものとする。
③main関数内にて、Studentクラスのオブジェクトhanakoを生成する。(コンストラクタにて初期化する。詳細は以下)
オブジェクト生成時に、引数に名前「由美」、身長159センチ、体重54キロ、生徒番号1111を渡して、基本クラスのコンストラクタにて、名前「名無し」、身長0センチ、体重0キロを設定するとともに設定内容を出力するようにする。また、派生クラスのコンストラクタにて、生徒番号9999番を設定するとともに設定内容を出力するようにする。
※ヒント : 本問題は設定を行わないため、問題1のようにset_name関数などのメンバ関数は不要。
【実行結果】
$ prac2 名前を初期化 : 由美 身長を初期化 : 159 体重を初期化 : 54 番号を初期化 : 1111 $
【実行結果】
#include <iostream> #include <string> using namespace std; class Human { private: string name; int height; int weight; public: Human(string n = "名無し", int h = 0, int w = 0); }; class Student : public Human { private: int num; public: Student(string n, int h, int w, int no = 9999); }; Human::Human(string n, int h, int w){ name = n; height = h; weight = w; cout << "名前を初期化 : " << name << endl; cout << "身長を初期化 : " << height << endl; cout << "体重を初期化 : " << weight << endl; } // Student::Student(string n, int h, int w, int no) : Human(string n, int h, int w) { よくある間違い Student::Student(string n, int h, int w, int no) : Human(n, h, w) { num = no; cout << "番号を初期化 : " << num << "\n" << endl; } int main(){ Student yumi("由美", 159, 54, 1111); return 0; }
問題3
※問題1の発展
①Humanクラスを作成。
Humanクラスには、メンバ変数として名前name、身長height、体重weightを登録できるものとする。
各メンバ変数name、height、weightはHumanクラスのメンバ関数にてアクセスするものとする。
②Humanクラスを継承したStudentクラスを作成。
Studentクラスには、privateなメンバ変数として生徒番号numを登録できるものとする。
メンバ変数numはStudentクラスのメンバ関数にてアクセスするものとする。
また、reset_all関数を用意する(引数で受け取った名前、身長、体重を、直接Humanクラスの各メンバ変数に設定できる関数)
③main関数内にて、Studentクラスのオブジェクトhanakoを生成する。(コンストラクタでの初期化は実装しない。)
④main関数にて、Humanクラスの関数にて名前「花子」、身長155センチ、体重57キロ、生徒番号123を各々設定・出力する。
⑤main関数にて、②のreset_all関数に、名前「由紀子」、身長162センチ、体重62キロ、生徒番号3333を渡して各メンバを再設定・出力する。
※ここで重要なのは「Humanクラスのset_name関数を使わない」こと!!
【実行結果】
$ prac3 名前を設定 : 花子 身長を設定 : 155 体重を設定 : 57 番号を設定 : 123 名前を再設定 : 由紀子 身長を再設定 : 162 体重を再設定 : 62 番号を再設定 : 3333 $
【実行結果】
#include <iostream> #include <string> using namespace std; class Human { protected: // 派生クラスから基本クラスのメンバに直接アクセスできるようになる string name; int height; int weight; public: void set_name(string n); void set_height(int h); void set_weight(int w); }; class Student : public Human { // publicな継承のため、外部(この場合main関数)から直接アクセス可能 private: int num; public: void set_num(int no); void reset_all(string n, int h, int w, int no); }; void Human::set_name(string n){ name = n; cout << "名前を設定 : " << name << endl; } void Human::set_height(int h){ height = h; cout << "身長を設定 : " << height << endl; } void Human::set_weight(int w){ weight = w; cout << "体重を設定 : " << weight << endl; } void Student::set_num(int no){ num = no; cout << "番号を設定 : " << num << "\n" << endl; } void Student::reset_all(string n, int h, int w, int no){ name = n; height = h; weight = w; num = no; cout << "名前を再設定 : " << name << endl; cout << "身長を再設定 : " << height << endl; cout << "体重を再設定 : " << weight << endl; cout << "番号を再設定 : " << num << "\n" << endl; } int main(){ Student hanako; hanako.set_name("花子"); hanako.set_height(155); hanako.set_weight(57); hanako.set_num(123); hanako.reset_all("由紀子", 162, 62, 3333); return 0; }
問題4
問題1にて、Studentクラスの継承の仕方がpublicではなく、privateだった場合どうなるか。
理由も添えて述べよ。
また、それらを説明するためのプログラムを作れ。
【答え】
main関数から直接基本クラスであるHumanクラスの関数(set_name関数、set_height関数、set_weight関数)を実行できなくなる。
継承が行われた場合、外部であるmain関数からset_name関数などのHumanクラス関数を実行する際、実質的にはStudentクラスに継承されたものを実行する。publicな継承が行われた場合はpublicなset_name関数などを実行する。
privateまたはprotectedな継承が行われた場合、外部であるmain関数からset_name関数などのHumanクラス関数を実行する際、実質的にはStudentクラスに継承されたものを実行しようとするが、継承された関数はprivateまたはprotectedな関数であるため、クラス外であるmain関数から直接実行はできない。
一方、派生クラスであるStudentクラスの関数(set_num)は実行できる。
【実行結果】
$ prac4 名前を初期化 : 名無し 身長を初期化 : 0 体重を初期化 : 0 番号を初期化 : 9999 番号を設定 : 123 $
【プログラム】
#include <iostream> #include <string> using namespace std; class Human { private: string name; int height; int weight; public: Human(string n = "名無し", int h = 0, int w = 0); void set_name(string n); void set_height(int h); void set_weight(int w); }; class Student : private Human { private: int num; public: Student(int no = 9999); void set_num(int no); }; Human::Human(string n, int h, int w){ name = n; height = h; weight = w; cout << "名前を初期化 : " << name << endl; cout << "身長を初期化 : " << height << endl; cout << "体重を初期化 : " << weight << endl; } void Human::set_name(string n){ name = n; cout << "名前を設定 : " << name << endl; } void Human::set_height(int h){ height = h; cout << "身長を設定 : " << height << endl; } void Human::set_weight(int w){ weight = w; cout << "体重を設定 : " << weight << endl; } Student::Student(int no){ num = no; cout << "番号を初期化 : " << num << "\n" << endl; } void Student::set_num(int no){ num = no; cout << "番号を設定 : " << num << "\n" << endl; } int main(){ Student hanako; // 直接アクセスできないため、下記コメントを外すとコンパイルエラーとなる // hanako.set_name("花子"); // hanako.set_height(155); // hanako.set_weight(57); hanako.set_num(123); return 0; }
問題5-1 仮想関数
①Humanクラスを作成。
Humanクラスには、「名前は名無しです」と出力するout_name関数を作成する。
②Humanクラスを継承したStudentクラスを作成。
Studentクラスには、「名前は太郎です」と出力するout_name関数を作成する。
③main関数内にて、Humanクラスのポインタp1、p2を生成する。
p1には、動的に作成したHumanクラスのオブジェクトのアドレスを格納する。(引数無しのオブジェクト)
p2には、動的に作成したStudentクラスのオブジェクトのアドレスを格納する。(引数無しのオブジェクト)
④p1、p2それぞれに対してout_name関数を実行し、下記「実行結果」のように出力させよ。
【実行結果】
$ prac5 名前は名無しです 名前は太郎です $
【答え】
#include <iostream> #include <string> using namespace std; class Human { private: // この行は無くても可 public: Human(); virtual void out_name(); // virtualをつけると仮想関数になる } class Student : public Human { private: // この行は無くても可 public: Student(); void out_name(); }; Human::Human(){ } void Human::out_name(){ cout << "名前は名無しです" << endl; } Student::Student(){ } void Student::out_name(){ cout << "名前は太郎です" << endl; } int main() { Human *p1, *p2; p1 = new Human(); p2 = new Student(); // 派生クラスは基底クラスのポインタに格納できる p1->out_name(); p2->out_name(); }
問題5−2 仮想関数(2)
下記の課題プログラムの下線部「___?___」に①〜④がそれぞれ一つだけ記述された場合の実行結果はどうなるか。
理由も添えて説明せよ。
①p1->out_name();
②p1->out_num();
③p2->out_num();
④p3->out_num();
⑤p1->out_height();
【課題プログラム】
#include <iostream> #include <string> using namespace std; class Human { public: Human(){} virtual void out_name(){ cout << "Humanクラス : 名前は名無しです" << endl; } void out_height(){ cout << "Humanクラス : 身長は0センチです" << endl; } }; class Student : public Human { public: Student(){} void out_name(){ cout << "Studentクラス : 名前は太郎です" << endl; } void out_num(){ cout << "Studentクラス : 番号は1234です" << endl; } void out_height(){ cout << "Humanクラス : 身長は170センチです" << endl; } }; int main(){ Human *p1, *p2; p1 = new Student(); p2 = new Human(); Student *p3; p3 = new Student(); [___?___] return 0; }
【答え(実行結果)】
①
$ prac5_4 Studentクラス : 名前は太郎です $
(基本クラスのポインタを使って派生クラスのメンバ関数を使用)
②
コンパイルエラー
(基本クラスのポインタp1が指す派生クラスのオブジェクトから、派生クラスで新規追加されたメンバ関数を使用することはできない)
③
コンパイルエラー
(当然のことだが、基本クラスのポインタp2が指す基本クラスのオブジェクトから、派生クラスで新規追加されたメンバ関数を使用することはできない)
④
$ prac5_4
Studentクラス : 番号は1234です
$
(②と異なり、派生クラスのポインタp3が派生クラスのオブジェクトを指しているので、派生クラスで新規追加されたメンバ関数を使用できる)
⑤
$ prac5_4
Humanクラス : 身長は0センチです
$
(①と異なり、基本クラスのポインタが指す派生クラスのオブジェクトからvirtualでない関数を実行すると基本クラスの関数が実行される)
問題6 純粋仮想関数
①純粋仮想関数outMes関数を持つHumanクラスを作成。
②Humanクラスをもとに、StudentクラスとTeacherクラスを作成し、各々outMes関数をオーバーライドして各々「Studentクラスです」「Teacherクラスです」と出力するように実装。
③StudentクラスとTeacherクラスのオブジェクトを作成し、各々outMes関数を実行し下記出力結果が得られるようにせよ。
【実行結果】
$ prac6 Studentクラスです Teacherクラスです $
【答え】
#include <iostream> using namespace std; class Human { public: virtual void outMes() = 0; }; class Student : public Human { public: void outMes() { cout << "Studentクラスです" << endl; } }; class Teacher : public Human { public: void outMes() { cout << "Teacherクラスです" << endl; } }; int main(){ Human *s, *t; s = new Student(); t = new Teacher(); s->outMes(); t->outMes(); return 0; }
問題7 多重継承
①クラスA、クラスB、クラスCを作成し、それぞれに独自の関数と、クラスA〜Cで同名の関数(処理は異なる)を実装する。
②クラスCはクラスAとBを継承したものとする。
③クラスCのオブジェクトを使ってすべての関数(3つのクラスの独自の関数と同名の関数、計6つの関数)を実行せよ。
<例>
【実行結果】
funcA funcB funcC sameFuncA sameFuncB sameFuncC
【答え】
#include <iostream> #include <string> using namespace std; class A { public: void funcA(){ cout << "funcA" << endl; } void sameFunc(){ cout << "sameFuncA" << endl; } }; class B { public: void funcB(){ cout << "funcB" << endl; } void sameFunc(){ cout << "sameFuncB" << endl; } }; class C : public A, public B { public: void funcC(){ cout << "funcC" << endl; } void sameFunc(){ cout << "sameFuncC" << endl; } }; int main(){ C c; c.funcA(); c.funcB(); c.funcC(); c.A::sameFunc(); c.B::sameFunc(); c.C::sameFunc(); return 0; }