complex.h |
---|
class Complex{ private: double Re, Im; public: Complex(void); Complex(const double &re); Complex(const double &re, const double &im); const double re(void) const; const double im(void) const; void print(void) const; }; const Complex operator+(const Complex &lhs, const Complex &rhs); const Complex operator-(const Complex &lhs, const Complex &rhs); const Complex operator*(const Complex &lhs, const Complex &rhs); const Complex operator/(const Complex &lhs, const Complex &rhs); |
complex.cxx |
---|
#include<iostream> #include<cstdlib> #include"complex.h" Complex::Complex(void){ } Complex::Complex(const double &re): Re(re), Im(0.0){ } Complex::Complex(const double &re, const double &im): Re(re), Im(im){ } void Complex::print(void) const{ std::cout << "(" << Re << ")+i(" << Im << ")" << std::endl; return; } const double Complex::re(void) const{ return Re; } const double Complex::im(void) const{ return Im; } const Complex operator+(const Complex &lhs, const Complex &rhs){ Complex result( lhs.re()+rhs.re(), lhs.im()+rhs.im() ); return result; } const Complex operator-(const Complex &lhs, const Complex &rhs){ return Complex ( lhs.re()-rhs.re(), lhs.im()-rhs.im() ); } const Complex operator*(const Complex &lhs, const Complex &rhs){ double re=lhs.re()*rhs.re()-lhs.im()*rhs.im(); double im=lhs.re()*rhs.im()+lhs.im()*rhs.re(); Complex result(re, im); return result; } const Complex operator/(const Complex &lhs, const Complex &rhs){ if(rhs.re()==0.0 && rhs.im()==0.0){ std::cout << "Division by 0" << std::endl; exit(1); } double re=(lhs.re()*rhs.re()+lhs.im()*rhs.im())/(rhs.re()*rhs.re()+rhs.im()*rhs.im()); double im=(lhs.im()*rhs.re()-lhs.re()*rhs.im())/(rhs.re()*rhs.re()+rhs.im()*rhs.im()); return Complex(re, im); } |
main_complex.cxx |
---|
#include<iostream> #include<cstdlib> #include"complex.h" int main(void){ Complex x(2.0, 3.1), y(-1.2, 3.4), z(5.6, 7.8), u(-9.1, 2.3), result; std::cout << "x="; x.print(); std::cout << "y="; y.print(); result=x+y; std::cout << "result=x+y="; result.print(); result=x-y; std::cout << "result=x-y="; result.print(); result=x*y; std::cout << "result=x*y="; result.print(); result=x/y; std::cout << "result=x/y="; result.print(); result=x+y*x/z-u; std::cout << "result=x+y*x/z-u="; result.print(); result=0.1-x*y+0.2; std::cout << "result=0.1-x*y+0.2="; result.print(); return 0; } |
fraction.h |
---|
class Fraction{ private: int Denominator, Numerator; public: Fraction(void); Fraction(const int &numerator); Fraction(const int &numerator, const int &denominator); const int denominator(void) const; const int numerator(void) const; void print(void) const; }; const Fraction operator+(const Fraction &lhs, const Fraction &rhs); const Fraction operator-(const Fraction &lhs, const Fraction &rhs); const Fraction operator*(const Fraction &lhs, const Fraction &rhs); const Fraction operator/(const Fraction &lhs, const Fraction &rhs); |
fraction.cxx |
---|
#include<iostream> #include"fraction.h" Fraction::Fraction(void){ } Fraction::Fraction(const int &numerator): Numerator(numerator), Denominator(1){ } Fraction::Fraction(const int &numerator, const int &denominator): Numerator(numerator), Denominator(denominator){ } const int Fraction::denominator(void) const{ return Denominator; } const int Fraction::numerator(void) const{ return Numerator; } void Fraction::print(void) const{ std::cout << "(" << Numerator << ")/(" << Denominator << ")"; return; } const Fraction operator+(const Fraction &lhs, const Fraction &rhs){ int denominator=lhs.denominator()*rhs.denominator(); int numerator=lhs.denominator()*rhs.numerator()+lhs.numerator()*rhs.denominator(); return Fraction(numerator, denominator); } const Fraction operator-(const Fraction &lhs, const Fraction &rhs){ Fraction result; int denominator=lhs.denominator()*rhs.denominator(); int numerator=rhs.denominator()*lhs.numerator()-rhs.numerator()*lhs.denominator(); return Fraction(numerator, denominator); } const Fraction operator*(const Fraction &lhs, const Fraction &rhs){ return Fraction( lhs.numerator()*rhs.numerator(), lhs.denominator()*rhs.denominator() ); } const Fraction operator/(const Fraction &lhs, const Fraction &rhs){ return Fraction( lhs.denominator()*rhs.numerator(), lhs.numerator()*rhs.denominator() ); } |
main_fraction.cxx |
---|
#include<iostream> #include"fraction.h" int main(void){ Fraction x(2, 3), y(-1, 3), z(3, 5), u(-2, 7), result; std::cout << "x="; x.print(); std::cout << std::endl; std::cout << "y="; y.print(); std::cout << std::endl; result=x+y; std::cout << "result=x+y="; result.print(); std::cout << std::endl; result=x-y; std::cout << "result=x-y="; result.print(); std::cout << std::endl; result=x*y; std::cout << "result=x*y="; result.print(); std::cout << std::endl; result=x/y; std::cout << "result=x/y="; result.print(); std::cout << std::endl; result=y*x/z+u; std::cout << "result=y*x/z+u="; result.print(); std::cout << std::endl; result=1+y*x-2; std::cout << "result=1+y*x-2="; result.print(); std::cout << std::endl; return 0; } |
●解答 1. privateで宣言することによって、そのデータを隠ぺいすることができる。 これは、クラスの使用者がデータメンバに不用意にアクセスさせないことでバグを減らすためである。 2. 浅いコピーとはデータメンバのポインタだけをコピーして、 そのポインタが指す内容をそのままにしておく方法で、 深いコピーとはポインタが指すメモリ領域のデータそのものをコピーする方法である。 よって、この2つのコピーの違いは、ポインタのみをコピーするかデータそのものをコピーするかである。 3. const参照引数を使用せず、ポインタ渡しをする場合、 関数の内部で自由にオブジェクトにアクセスすることができるので、読み取りのみの状態を 保証することができない。 const参照引数を用いると、関数の内部でオブジェクトのデータ変更をするようなアクセスをすると エラーになる。 よって、完全ではないが、読み取りのみの状態を保証することができる。 4. コンストラクタはクラスのオブジェクトを作成するときの初期化を行うための関数である。 クラス名と同じ名前で戻り値の型の指定のない関数がコンストラクタを見分ける特徴である。 5. メンバ関数はクラスのメンバ変数の値を操作する関数のことである。 それ以外のふつうに用いる関数のことを非メンバ関数と呼ぶ。 メンバ関数はクラス内に定義する。 非メンバ関数と同様に、メンバ関数も返却値型と仮引数並びとともに宣言する。 メンバ関数は非メンバ関数と違い、オブジェクトのポインタがthisとして渡される。 よってメンバ関数と非メンバ関数は関数呼び出し規則が異なっている。 |
======== [解答] 1. 属に言う、インターフェースの統一が講義内での大きな理由である。 変数を全てPublicで表現した場合、クラスを使用する際に複数の方法での代入法が必要となる。 広義でみての代入や計算をすると考えてプログラミングを構築する方が効率や見やすさの点で優位であり、そして〜.h文での重要性が上がり、3つの文章で表現することの意味が増すから。 2. 浅いコピーと深いコピーでは同じ変数を参照しコピーするにしてもその内容をどこまで含んでいるかが違う、これを浅い、深いで例えて表現している。 深いコピーについては定例的に.clone()で表され、配列代入などの形で、通常の浅いコピーにおける「参照情報のみのコピー」ではなく、参照型変数の完全なコピーを実現することができる。大まかな表現で浅いコピーはポインタのコピー、深いコピーはリファレンス先のコピーだと言える。 3. const参照引数を用いる利点として大きなものは、その引数を参照することによる効率の向上である。 前述のように浅いコピーが去れた場合、参照情報から再び渡された値をコピーする必要、手間があるが、const参照引数においてはそれが軽減される。 また引数容量が非常に大きいデータを持つクラスだった場合はコピーの手間がかかり全大敵に、実行速度は遅くなってしまう、この例についても解消法として参照引数が使用される。 4. クラスの定義の際に記述されている名前と同じ名前の関数であるという前提がコンストラクタにあるため、そこから区別する必要がある。 5. 講義資料内で紹介された(4.5)式(4.6)式についての違いを述べる。 メンバ関数、名前にクラス名をいれなくてもprint関数は他の関数とは区別される。 非メンバ関数は、print関数は他の関数とは手順などを含め区別させている。 また非メンバ関数上ではprint関数はpublicとして定義されている。 public上で定義されているので、cxx文の時などには必要最低限の情報でprint関数を引き出すことができる。 |
======== [問題1] データメンバをprivateに宣言する理由を述べてください。 [解答1] データメンバをprivateに宣言することによって、外部から読み書きなどができなくなるようになります つまりはこのデータメンバに対して「アクセス制限」することができます。 [問題2] 浅いコピーと深いコピーの違いを述べてください。 [解答2] ある2つの構造体(a,bとします)においてaのみに具体的な値があり、bはaの値を代入しているとします。このとき=を使って代入していると、a.element1の先頭アドレスがb.element1に代入されていることになり、たとえばb.element1の数値を変更すると同時にa.element1の値も変更されてしまします。また、flee関数でb.elementを解放すると、同時にa.element1のさしている領域すらも開放したことになってしまいます。このことが浅いコピーです これを防ぐために、malloc関数を用いて別の領域を用意し、そのなかに値を代入していくのが深いコピーで、浅いコピーでの問題点を解決しています。 [問題3] const参照引数の利点を述べてください。 [解答3] もしもクラスのメンバ関数の中であっても、const参照引数は、引数の中身を変更することができなくなるようにすることができます。 [問題4] 与えられたプログラムからコンストラクタが定義されている箇所を見つける指針を述べてください。 [解答4] クラス名と同じ名前を持っていて、戻り値の型のない関数が定義されていたとすると、それがコンストラクタになります。 クラスのオブジェクトを作成するときの初期化を行うための関数です。コンストラクタは必ずしも定義する必要はなく、その場合初期化は何ひとつ行われません。 [問題5] メンバ関数と非メンバ関数の違いを、その性質、宣言・定義のしかたの観点から述べてください。 [解答5] メンバ関数とは、それが属している構造体のデータメンバを操作するための関数です。ある構造体のオブジェクトに対して、 ドット演算子(.演算子)を使って呼び出します。読み込み専用のメンバ関数の場合、メンバ変数の値をあらかじめ変更できないように、 定数のメンバ関数として宣言しておくと安全です。 |
======== 解答 1.データメンバをprivateに宣言する理由を述べてください。 クラスを使用する際、一つの目的に対してやり方を統一のため。 privateメンバとして関数を宣言すれば、そのクラスのメンバ関数以外からアクセスできなくなる。 2.浅いコピーと深いコピーの違いを述べてください。 ↑深いコピー ・配下のデータを全てコピーする。 ・Aのインスタンスのみコピーして配下のデータは元のインスタンスを共有する。 ・Aのインスタンスだけコピーし、コピーされたものは配下を持たない。 ↓浅いコピー 3.const参照引数の利点を述べてください。 ポインタではなく参照を使う利点は、関数内部でのポインタに関するミスを防ぎ、可読性を上げること。 (*p++等はミスを招き可読性を下げる顕著な例) しかしそれはポインタを受け取った関数の中で、最初に受け取ったポインタの参照先を参照する変数を用意すれば良い。 4.与えられたプログラムからコンストラクタが定義されている箇所を見つける指針を述べてください。 ・コンストラクタは戻り値を持たない。 ・クラスをインスタンス化した時に自動的に呼び出されるため、クラスのメンバ変数を初期化するために使用される。 5.メンバ関数と非メンバ関数の違いを、その性質、宣言・定義のしかたの観点から述べてください。 メンバ関数は、名前にクラス名を入れなくてもほかの関数とは区別され、混同することがなく簡単に扱うことが出来る。 例:関数print_MyClassを次のようにメンバ関数printに変更 void print(void) const; の宣言をすれば メンバ関数内でarg.MemberをMemberのみで表すことが出来る。 非メンバ関数は同じ名前を持ち、引数の型または数の異なる普通の関数である。 なお、宣言のやり方はどちらも返却値型や仮引数等と一緒に宣言します。 例 ==== class MyClass{ public: double Member; /*非メンバ関数*/ void print(void) const; /*メンバ関数*/ }; ==== |
======== 1. データメンバをprivateに宣言する理由を述べてください。 仕様変更等するときに一括でまとめてあるので楽に変更できる。 2. 浅いコピーと深いコピーの違いを述べてください。 参照先のインスタンスの複製を作りセットする方法を「深いコピー」と言う。 参照値のみコピーする方法を「浅いコピー」と言う。 3. const参照引数の利点を述べてください。 constデータは競合が起こらないので、 無限に共有することができるためコピーの必要がない。 これによって、プログラムは正確かつ効率的になる。 4. 与えられたプログラムからコンストラクタが定義されている箇所を見つける指針を述べてください。 コンストラクタの戻り値はインスタンスへの参照であり、コンストラクタはインスタンス生成時以外に呼び出されることは無いため戻り値がない 5. メンバ関数と非メンバ関数の違いを、その性質、宣言・定義のしかたの観点から述べてください。 メンバ関数は仮想関数にすることができるが、非メンバ関数は仮想関数にすることが出来ない。 |
======== [解答] |
= 1. データメンバをprivateに宣言する理由を述べてください。 privateにすることで、同じクラスの中からしか変数にアクセスできないようにしている。 メンバ関数を用いて間接的に値を設定するようにしておくことで、値の変更できる方法を一通りに指定し、 セキュリティを確保することができる。 |
= 2. 浅いコピーと深いコピーの違いを述べてください。 以下のプログラムを浅いコピーの例とする。 vector.h~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include<stdio.h> struct Vector{ int Size; double* Element; }; struct Vector Vector(int size); void free_Vector(struct Vector *arg); void print_Vector(struct Vector *arg); vector.c~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include<stdio.h> #include<stdlib.h> #include"vector.h" struct Vector Vector(int size){ struct Vector result; result.Size=size; result.Element=malloc(size*sizeof(double)); if(result.Element == NULL){ printf("Vector:Out of Memory!\n"); exit(1); } else{ return result; } } void print_Vector(struct Vector *arg){ int i; printf("("); for(i=0;i<arg->Size;i++){ printf("%.3e, ", p->Element[i]); } printf(")"); return; } void free_Vector(struct Vector *arg){ free(arg->Element); return; } main.c~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include<stdio.h> #include"vector.h" int main(void){ int i; struct Vector x=Vector(5), y=Vector(5); for(i=0;i<x.Size;i++){ x.Element[i]=(double)(i+1); } y=x; print_Vector(&x); print_Vector(&y); free_Vector(&x); free_Vector(&y); return 0; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 浅いコピーでは、配列yの先頭アドレスを配列xに格納されているアドレスに書き換えただけで、 配列yの中身は書き換えられていない。これだと、free関数でyを開放したつもりがxを開放してしまって その後にxの中身を書き換えてしまうと意図しない領域に値が格納されてしまい危険である。 一方、深いコピーでは配列の中身まで書き換える。 |
= 3. const参照引数の利点を述べてください。 変数をコピーして用いると、大きな数のデータを使用するときに時間がかかってしまう。 ポインタを用いると時間はかからないが、複雑なプログラムになってしまいわかりにくい。 参照引数を用いることで、変数を用いたときのようなわかりやすいプログラムのまま、ポインタを用いたときのような 高速に動作するプログラムを組むことが出来る。 また、値を書き換えるつもりがない場合はconstをつけて定数として扱っている。 |
= 4. 与えられたプログラムからコンストラクタが定義されている箇所を見つける指針を述べてください。 コンストラクタは、 ・クラスと同じ名前 ・戻り値を持たない 箇所を探せばよい。 |
= 5. メンバ関数と非メンバ関数の違いを、その性質、宣言・定義のしかたの観点から述べてください。 メンバ関数の宣言はクラスの中で行われるが、非メンバ関数はグローバルで行われる。 メンバ関数の定義の中では、どのオブジェクトから呼ばれているかわからないのでオブジェクト名は不要。 メンバ関数の性質として、クラスの中に関数があるためprivateに設定されたメンバにアクセスすることができる。 |
= |
======== [解答2-1] 同じメンバ内で宣言されたメンバ関数からのみアクセスできるようにし、設定方法を一つに統一する。 ====================== [解答2-2] 浅いコピー: ============== #include<stdio.h> #include"vector.h" int main(void){ int i; struct Vector x=Vector(5), y=Vector(5); for(i=0;i<x.Size;i++){ x.Element[i]=(double)(i+1); } y=x; print_Vector(&x); print_Vector(&y); free_Vector(&x); free_Vector(&y); return 0; } ============= 上のプログラムでは、yにxを代入するためにy=x としている。x.Elementはある領域の先頭アドレスを指すので、y.Elementにはx.Elementと同じアドレスを格納することになる。つまり、配列をコピーしたのではなく、アドレスをコピーしただけの状態になっている このとき、y.Element[0]の値を変更すると、x.Element の値も変更されてしまう つまり、わざわざ2つの変数を宣言した意味がなくなってしまう。 また、yが不要になってyの領域を解放した場合、xの領域が解放されたことに等しいので、その後x.Elementに何らかの値を代入しようとしたとき、その領域は全く別の用途に使用されている可能性があり危険である 深いコピー: 浅いコピーとは違い、アドレスではなくy.Element[0]=x.Element[0]、y.Element[1]=x.Element[1] のように要素一つ一つをコピーする ===================== [解答2-3] constを付けると、その変数は定数で変化しないことを表す。もし書き換えようとしたり、別の値を代入しようとしたらコンパイルエラーが起きるようにしている。 ===================== [解答2-4] クラス名と同じ名前を持つ関数を探す ===================== [解答2-5] ・宣言 メンバ関数はクラスの宣言中で宣言 非メンバ関数はクラスの宣言の外で宣言 ・定義 メンバ関数は引数の設定が不要、データメンバ名だけで扱う 非メンバ関数は、引数の設定が必要 ・性質 メンバ関数は、同じメンバ中のprivateメンバにアクセスできる 非メンバ関数はprivateメンバにアクセスできない ===================== |
======== 1. privateを使う理由は、インターフェースを統一するためである。 privateで宣言されたデータはプログラムの他の部分からは直接呼び出すことはできない。よって、今まで値の代入の仕方がいくつかあったのが、メンバ関数を用いてprivateメンバにアクセスするという一つの方法に限られる。 インターフェースを統一することによって、プログラム作成者以外の人がプログラムを見る場合に混乱が少なく理解が用意になるという利点が考えられる。 2. 深いコピーとは、参照している値をコピーするもので、浅いコピーとは、ポインタを使って参照自体をコピーする事である。 深いコピーでは参照している値がコピー先にコピーされるのが、浅いコピーでは参照自体のコピーだけなのでPCへの負担が少ない。 また、深いコピーでは参照元の値が後から変わってしまってもコピー先の値は変わらないが、浅いコピーでは参照元の値が変わるたびにコピー先の値も変わる。 3. const参照は参照先の変数のデータを変化させない参照である. const参照をした場合,参照先のデータを操作するとコンパイル時にエラーが発生するので,プログラムのエラー検出が容易になる。 4. コンストラクタが定義されている箇所を見つけるには、オブジェクトを作る時に非メンバ関数を使っているか、メンバ関数内で作っているかを確認すればよい。 ヘッダファイルを見て、メンバ関数内でオブジェクトが作られている部分がコンストラクタが定義されている箇所である。 5、 メンバ関数と非メンバ関数の違いは以下である。 メンバ関数 性質:名前にクラス名を入れなくても他の関数とは区別される。例えばprint関数をメンバ関数で定義した場合,わざわざクラス名を入れなくても他の関数とは区別される. 宣言・定義のしかた:クラスの宣言の中で行う。名前にクラス名を入れなくても他の関数とは区別される。 非メンバ関数 性質:宣言時にクラス名を入れる必要がある. 宣言.定義のしかた:クラスの宣言の外で行う。 |
======== [解答2] 1.データメンバをprivateに宣言する理由を述べてください。 privateで宣言されたデータメンバは、ドット演算子を使ってアクセスすることが出来ない。 privateで宣言する理由は、クラスの外側から自由に変数の値を書き換えられない (又、読み取れない)ようにするためであり、C言語編のとき、グローバル変数はあまり 使わない方がいい理由とよく似ています。 2.浅いコピーと深いコピーの違いを述べてください。 浅いコピーとは、参照型変数を「=」(代入演算子)を使ってコピーを行う、つまり参照情報のみコピーすることをさします。 深いコピーとは、参照型変数の参照情報だけではなく、オブジェクト自身のコピーも行う完全なコピーをさします。 3.const参照引数の利点を述べてください。 const指定された変数には値を代入できなくなり、最初に初期値として与えた値のまま変更できなくなります。 最大要素数を表す時に、#defineを使うことがありますが、#define による定数定義と違い、const の場合は データ型がはっきりするため、優れているといえます。 4.与えられたプログラムからコンストラクタが定義されている箇所を見つける指針を述べてください。 コンストラクタは、基本的にはメンバ関数と変わりませんが二つの特徴を持ちます。 一つはクラスと同じ名前であり、クラスと同じメンバ関数はコンストラクタとなります。 もう一つは戻り値は無いため、コンストラクタには戻り値は指定しません。 したがって、クラスと同じ名前であり、戻り値が無いものがコンストラクタであるといえます。 5.メンバ関数と非メンバ関数の違いを、その性質、宣言・定義のしかたの観点から述べてください。 メンバ関数と非メンバ関数のもっとも大きな違いは、メンバ関数は仮想関数にすることができるが、 非メンバ関数は仮想関数にすることが出来ない。つまり、動的に結合する必要があれば、仮想関数でなければならず、 そしてそれは何らかのクラスのメンバ関数でなければならない。 |
======== [解答] 1. データメンバをprivateに宣言すると、メンバ変数はpublicに宣言されたメンバ関数以外からはアクセスできなくなります。これにより、データメンバが外部から書き換えられなくなるので、データメンバを保護することができます。このことをカプセル化と呼び、クラスの外部からアクセスできるメンバを限定し、クラスが管理するデータをしっかり保護します。つまり、クラスは「保護したいデータ」と「保護データを操作する処理の2つの組み合わせで成り立ちます。 2. 浅いコピーとは値をコピーする際に、その値をコピーするだけで変数のアドレスをコピーするわけではない。なので、コピー元の値が変わっても、コピーした方の値は変わらない。 深いコピーとは、コピーする値を格納している変数のアドレスをコピーすることを意味する。これにより、コピー元の値が変わると、アドレスでコピーしているので、コピーした方の値も変わる。 3. const参照引数を用いると、その変数の値を書きかえることができなくなります。これはクラスのメンバ関数であっても同じです。もし、変数にconst修飾子をつけていなかったら、関数により中身を書きかえられる可能性があります。なので、const参照引数を用いると中身が書き換えられず、もし、関数で書き換えようとするとコンパイルエラーで出ます。 4. コンストラクタの特徴として、publicに宣言する際に戻り値をもたない。つまり、コンストラクタが定義されている箇所を見つける際は、戻りをもっていない関数を探し出せば、そこがコンストラクタの定義をしている部分である。また、voidコンストラクタをpublicに宣言すると、関数の戻り値のところにvoidと記されており、voidは値を何も返さないという意味をもつので、戻りがない。戻り値がない関数の定義を見つけると、そこがコンストラクタの定義されている箇所である。 5. メンバ関数と非メンバ関数のヘッダフィルにおける宣言のしかたから違いを見つける。 非メンバ関数のヘッダファイル class Complex{ public: double Re, Im; }; const Complex make_Complex(const double &a, const double &b); const Complex add_Complex(const Complex &x, const Complex &y); void print_Complex(const Complex &x); メンバ関数のヘッダファイル class Complex{ public: double Re, Im; void print(void) const; }; const Complex make_Complex(const double &a, const double &b); const Complex add_Complex(const Complex &x, const Complex &y); ここでは、print関数に注目してみる。 メンバ関数ではpublicにprint関数を定義してしている。宣言している場所が違うことが分かる。また、メンバ関数では戻り値を取っていない。これも、非メンバ関数とは違う。 つぎに定義のしかたから違いを見つける。 非メンバ関数の定義のしかた void print_Complex(const Complex &x){ メンバ関数の定義のしかた void Complex::print(void) const{ これより、関数が戻り値をとってるかとってないかの違いが分かる。メンバ関数では、「::」を「のメンバ関数」つまり、クラスComplexのメンバ関数と読むという意味である。 また、main関数内において、print関数を呼び出す方法も異なる。 非メンバ関数 print_Complex(z); メンバ関数 z.print(); これより、メンバ関数ではオブジェクトを用いてメンバ関数を呼んでいる。 |
======== [解答] ======データメンバをprivateに宣言する理由===== 同じクラス内からしかアクセスできないようにし、設定方法を統一する(インターフェースの統一) ==================================== =====浅いコピーと深いコピーの違い===== 浅いコピーは、1つの変数の先頭アドレスをもう1つの変数にコピーすることである。 コピーしているものが先頭のアドレスなので、どちらか片方の変数の値を変更すると、もう片方も自動的に変更されてしまう。 深いコピーは1つの変数の値をもう1つの変数にコピーすることである。 この2つの変数はそれぞれ別のアドレス指しているため、どちらか片方の値を変えても、もう片方が変わってしまうことはない。 ============================== =====const参照引数の利点===== constを変数の先頭につけることにより、変数が定数であるということを表している。 したがって、constをつけた変数に値の代入、変更はできなくなる。 ========================= =====与えられたプログラムからコンストラクタが定義されている箇所を見つける指針===== クラス名と同じ名前で宣言されている関数を探す。 戻り値がないことを確認する。 ============================================================== =====メンバ関数と非メンバ関数の違い===== メンバ関数は、クラス宣言中にメンバの宣言を行い、引数の設定が不要である。 また、同メンバ中のprivateメンバにアクセスすることができる。 非メンバ関数は、クラスの宣言外でメンバの宣言を行い、引数の設定が必要となる。 また、同メンバ中のprivateメンバにアクセスすることができなくなる。 ================================ |
======== [解答] 以下にそれぞれの解答を示す。 1.データメンバをprivateに宣言することで、無駄な領域を使用しないことによるメモリの節約や、データメンバの書き換えの必要がない、または、書き換えがされるとプログラムやその結果に問題が起こるという問題の解消のため。 2.浅いコピーとは、データメンバのポインタだけをコピーして、そのポインタがさす内容をそのままにしておくことで、深いコピーとは、オブジェクトのメンバデータをメンバごとにコピーすることである。 3.不要な領域確保を行わないので、メモリの節約ができる利点がある。 4.コンストラクタは、クラス名と同じ名前で戻り値の型の指定のない関数なので、これが定義されている箇所を探せばよい。 5.メンバ関数は、クラスの中で宣言されてこのメンバ関数について定義が必要である。非メンバ関数とは、クラスとは関係なくstdio.hなどをincludeすることで使用でき、宣言や定義の必要もない。printf関数などがある。 |
======== [問題1] データメンバをprivateに宣言する理由を述べてください。 [解答] privateメンバをクラス外から直接は利用できない。よってデータを安全に処理することができる。 [問題2] 浅いコピーと深いコピーの違いを述べてください。 [解答] 参照型変数のコピーにおいて、参照情報だけがコピーされることを浅いコピーという。 一方浅いコピーに対して、参照型変数の完全なクローンを作ることを深いコピーと呼ぶ。 [問題3] const参照引数の利点を述べてください。 [解答] const参照引数は、その引数の値が関数の中で変更されないことを表明するのに役立つ。 関数の動作としてはconstがなくても同じであるが、関数を作成した人とそれを利用する人が別々の場合に、有益なメッセージとなる。 [問題4] 与えられたプログラムからコンストラクタが定義されている箇所を見つける指針を述べてください。 [解答] コンストラクタは戻り値を持たない。 コンストラクタの名前はクラス名と同じ名前である。 [問題5] メンバ関数と非メンバ関数の違いを、その性質、宣言・定義のしかたの観点から述べてください。 [解答] メンバ関数は非メンバ関数とは異なり、オブジェクトへのポインタがthisとして渡される。 すなわち、普通の関数とはコーリング・コンベンションが異なるわけである。 宣言をヘッダファイルに記述し、関数の実体はソースファイルに記述することになる。 |
======== [解答] 1.クラスを使用するに当たって、一つの目的のために複数の方法が用意されていることが望ましくないから。 2.浅いコピーとは、領域を新しく確保せずに単純な代入を行うこと 深いコピーとは領域を確保し指し示す先のデータをコピーする。 3.参照引数は実際にはポインタであるため、大きな構造体やクラスを引数に渡すときにも効率のよい方法ですが、const をつけない参照渡しであれば、関数により中身を書き換えられる可能性があることになります。参照渡しに const 修飾子をつけることにより、引数の中身を書き換えないことを宣言することができます。 4.コンストラクタの名前はクラス名と同じである。 そこで定義されている箇所を見付けるにはクラス名::クラス名()という部分を探せばよい。 5. |
======== [解答] 1.データメンバをprivateに設定すると、メンバは自分自身で直接確保したプライベートな構造体を持つ。 2.浅いコピーは新たな複合オブジェクトを作成し、その後、可能な限り元のオブジェクト中に見つかったオブジェクトに対する 参照 を挿入する。 一方、深いコピーは新たな複合オブジェクトを作成し、その後、元のオブジェクト中に見つかったオブジェクトのコピーを挿入する。 3.オブジェクトのコピーをとることなく、オブジェクトの実体を関数に渡せる。 また、カプセル化をより強力にできる。 どのようにプログラムされたかを知らなくとも、オブジ ェクトやコード群と遣り取りができるコードのことをカプセル化されたコードという。 4.コンストラクタとは特殊なメンバ関数のことである。new 演算子によってクラスのインスタンスが生成された時、自動的に呼び出される関数である。 コンストラクタはインスタンス生成時に自動的に呼び出されるので、コンストラクタを定義すればフィールドの初期化を忘れてしまうようなミスを確実に回避することができるようになる。 コンストラクタを定義するには、戻り値型を持たないメンバ関数を定義する。 コンストラクタに戻り値がないのは、コンストラクタの戻り値は常にコンストラクタが定義されているクラスのオブジェクトだからである。 5.メンバ関数は仮想関数にすることができる。しかし、非メンバ関数は仮想関数にすることが出来ない。また、もっとも左にある引数について型変換ができるのは非メンバ関数に限られる。 |
======== [解答2] 1.クラスの外側から自由に変数の値を書き換えられない(又、読み取れない)ようにするため。 2.浅いコピーとは、参照型変数の値ではなく、参照情報自体をコピーするものであり、深いコピーとは、参照型変数の参照情報だけではなく、オブジェクト自身のコピーも行う完全なコピーをさす。 3.constデータはコピーの必要がない。 競合が起こらないので、 (たとえばポインタや参照を通して) 無限に共有することができる。 これによって、プログラムは正確かつ効率的になる。 もっとも明白な利点は、 記号定数(manifest定数)や文字列に名前をつけられることである。 また、const引数は、関数がその引数を通して参照できるデータを決して書き換えないことを保証し、 モジュール性を向上する。 4.クラス名と同じ名前を持ち、オブジェクトの初期化を行っているので、比較的早い段階でコードが書かれているという観点より、コンストラクタが定義されている個所を見つける。 5.メンバ関数の宣言はクラス内で行われ、クラス内で完結する。非メンバ関数は、main関数より前で宣言、定義を行うか 宣言のみ行い定義はその後に記述するという方法がとられることが多い。 メンバ関数は主にデータメンバを扱うために利用され、非メンバ関数は引数を処理するために関数が定義されることが多い。 |
======== [解答] 1, データメンバをprivateにすることによって隠蔽を行うことが出来る。 具体的には、これ以降に書かれたデータメンバは非公開となり、クラスのメンバ関数からのみデータメンバ を使用することが出来る。このようなことを行う理由としてはクラスの外からは直接データメンバを扱えなくすること によってデータメンバの構造を外からは意識させなくする。必ずメンバ関数を通してデータメンバを扱わせることによって 配列の範囲外へのアクセスなどの事項が起きにくくする効果がある 2, 浅いコピーとは、参照型変数の値ではなく、参照情報自体をコピーするものであり、深いコピーとは、 参照型変数の参照情報だけではなく、オブジェクト自身のコピーも行う完全なコピーをさす。 3, キーワード const は、C++ の変数(オブジェクト)が定数であることを示すものである。 用途としては、オブジェクトの内容を変更できないというアクセス制御を行うなどのに用いる。 4, ソースコード上でクラスと同じ名前を持つこと。 オブジェクトの初期化を行っているので、比較的早い段階でコンストラクタの定義が行われることなどから コンストラクタの定義が行われている個所を見つけることが出来る 5, メンバ関数の宣言はクラス内で行われる。非メンバ関数は、main関数より前で宣言、定義を行うか 宣言のみ行い定義はその後に記述するという方法がとられる。 メンバ関数は主にデータメンバを扱うために利用され、非メンバ関数は引数を処理するために関数が定義されることが多い |
======== (問題2) (1) # データメンバをprivateに宣言する理由を述べてください。 (解答) private以降の文で宣言されたデータまたは関数は、このクラス以外のプログラムの他の部分からは直接呼び出すことはできない。プライベートメンバ(非公開メンバ)には、プログラムの他の部分からは直接アクセスできず、パブリックメンバ(公開メンバ)にはアクセスできる。例えば、メンバの値になんらかの条件があるとき、パブリックのように公開していると、その条件を保証するための手段を考えなくてはいけなくなり、それはクラスの側では不可能で、クラスを使う側で何らかの防御措置を講じなければならない。つまり、メンバをプライベート(非公開)にするということは、メンバへのアクセスを制限し、より安全なプログラミングを可能にする手段である。 (2) 浅いコピーと深いコピーの違いを述べてください。 (解答) また、深いコピーでは参照元の値が後から変わってしまってもコピー先の値は変わらないが、浅いコピーでは参照元の値が変わるたびにコピー先の値も変わる。 (3) const参照引数の利点を述べてください (解答) const参照には参照先の変数のデータを変化させないことができる。つまり引数の型の前にconstを付けるだけで、参照先のデータを操作しようとするとコンパイルエラーが発生するようになる。 (4) 与えられたプログラムからコンストラクタが定義されている箇所を見つける指針を述べてください。 (解答) ヘッダファイルのメンバ関数内でオブジェクトが作られている部分がコンストラクタが定義されている箇所である。 (5) メンバ関数と非メンバ関数の違いを、その性質、宣言・定義のしかたの観点から述べてください。 (解答) メンバ関数は名前にクラス名を入れなくれも他の関数と区別され、クラスの宣言の中で宣言、定義をする。 非メンバ関数は宣言する時にクラス名を入れる必要があり、クラスの宣言の外で宣言、定義を行う。 |
======== [解答] 1.クラスを使用する際、視覚的分かりにくさの残る x.Re=2.3のような書き方を避けたい。そのためprivateメンバとしてメンバを宣言し、publicメンバとしてprivateメンバをアクセス可能にした。こうすることで、メンバ関数以外からはprivateメンバにアクセスすることができなくなった。 2.参照型変数のコピーにおいて、参照情報だけがコピーされることを浅いコピーという。浅いコピーに対して、参照型変数の完全なクローンを作ることを深いコピーと呼ぶ。 3.const参照引数を使うことで、その参照引数を書き換えられないようにすることができる。もし値が変えられていた場合にはコンパイラがエラーを出してくれる。 4.。コンストラクタはクラス名と同じ名前で戻り値の型の指定のない関数が定義されているので、publicメンバの中でそれが宣言されているかを見ればよい。 5.非メンバ関数からは、privateメンバにアクセスすることができないが、定義すればメンバ関数からはprivateメンバにアクセスすることができる。また非メンバ関数の定義はクラスの外で行われ、メンバ関数の定義はクラスの中で行われる。 |
======== [解答] 1.クラスの外からのアクセスを制限することでデータのおかしな書き換えを防ぐため。 2.浅いコピーとはコピー元と同じデータをコピー先に複写するもの。深いコピーとはコピー元と同じデータを参照し、コピー先を変更するとコピー元も同じ変更が及ぶもの。 3.const参照の利点とは、期待しない値の変更を防ぐことができる。 4.クラスと同じ名称の関数、または返却値のない関数を探す。 5.メンバ関数はクラスの内部で宣言・定義し、クラスのprivateなメンバにもアクセスすることができる。非メンバ関数はクラスの外側で宣言・定義され、クラス内部のprivateなメンバにはアクセスすることができない。 |
======== [解答] 1 privateを使う理由は、privateで宣言されたメンバ以降は、他のプログラムから呼び出すことはできない。つまりprivateメンバの場合外部からのメンバへのアクセスを制限することでパブリックメンバと分けて考える事ができ混乱を減らし安全なプログラミングを作成することができる。 2 コピーするときに気をつけなければならないことで、深いコピー(deep copy)と浅いコピー(shallow copy)問題がある。深いコピーとは、「参照している値をコピーする」もので、浅いコピーとは、「参照している値ではなく、参照自体をコピーする」もの区別することができる。参照自体をコピーするということは参照元の値が変わったときにはコピー先の値も変わってしまうことを意味して、深いコピーの場合このような事は起こらず参照先の値が変わっても元の値を保持します。 3 const参照は参照先の変数のデータを変化させないようにすることができる。 const参照をした場合,参照先のデータを操作するとコンパイル時にエラーが発生するので,プログラムのエラーが容易にわかるようになる。 4 コンストラクタが定義されている箇所を見つけるには、オブジェクトを作る時に非メンバ関数を使っているか、メンバ関数内で作っているかを確認すればよい。 ヘッダファイルを見て、メンバ関数内でオブジェクトが作られている部分がコンストラクタが定義されている箇所である。 5 メンバ関数の性質は名前にクラス名を入れなくても他の関数とは区別される関数である。宣言・定義は、クラスの宣言の中で行う。 |
======== [解答] 1.クラスの外からのアクセスを制限することでデータのおかしな書き換えを防ぐため。 2.浅いコピーとはコピー元と同じデータをコピー先に複写するもの。深いコピーとはコピー元と同じデータを参照し、コピー先を変更するとコピー元も同じ変更が及ぶもの。 3.const参照の利点とは、期待しない値の変更を防ぐことができる。 4.クラスと同じ名称の関数、または返却値のない関数を探す。 5.メンバ関数はクラスの内部で宣言・定義し、クラスのprivateなメンバにもアクセスすることができる。非メンバ関数はクラスの外側で宣言・定義され、クラス内部のprivateなメンバにはアクセスすることができない。 |
======== [解答] 2-1 privateというクラス宣言部は以降の文で宣言されたデータまたは関数は、このクラス以外のプログラムの他の部分からは直接呼び出すことはできない。したがって、例えばmain関数で次のような構文でこのクラスのオブジェクトの高さを設定することはできません。また、同じ宣言部で、publicという 以降の文で宣言されたデータまたは関数は、プログラムのどの部分からでも呼び出すことができるものがある。このように、知られたくない情報を隠すという意味や、複数人数でプログラムの開発を行っている場合にPublicで公開されているメソッドを簡単に変更したりプライベートに書き換えたりすることを避けるために使用される。 2-2 フィールドが参照型変数の場合、参照値のみコピーする方法を「浅いコピー」と言う。対して、参照先のクラスの実体(インスタンス)の複製を作りセットする方法を「深いコピー」と言う。浅いコピーの場合、全てがコピーされるわけではないため、誤ってコピー元のデータを修正してしまう可能性がある。ただし、深いコピーはインスタンスの複製を作るため処理に時間が掛かるというデメリットがある。浅いコピーと深い コピーの違いが関係するのは、複合オブジェクトのみである。 2-3 constデータはコピーの必要がない。 競合が起こらないので、ポインタや参照を通して無限に共有することができます。 これによって、プログラムは正確かつ効率的になる。また、 記号定数や文字列に名前をつけることができる。関数がその引数を通して参照できるデータを決して書き換えないことを保証し、 独立性が増す。 2-4 コンストラクタには戻り値は無いため、コンストラクタには戻り値は指定しない。そのため戻り値が指定されていない箇所を探す。 2-5 メンバ関数、非メンバ関数、friend関数 メンバ関数と非メンバ関数のもっとも大きな違いは、メンバ関数は仮想関数にすることができるが、非メンバ関数は仮想関数にすることが出来ない。つまり、動的に結合する必要があれば、仮想関数でなければならず、そしてそれは何らかのクラスのメンバ関数でなければならない。 |
======== 「解答」 (1) 指定したメンバ関数以外からアクセスできないようにするため。 つまるところ読み出し書き込みを禁止するため。 (2) 浅いコピー:コピー対象の中にあったオブジェクトに対する参照を挿入。 深いコピー:コピー対象の中にあったオブジェクトそのもののコピーを挿入。 (3) Const参照引数の利点 データが書き変わらない、変わらない。 文字列に名前を付けることができる。 などなど。 (4) コンストラクタが定義されている部分を見つける指針 関数定義部で::となっているところをみつけてみる。 コンストラクタの関数名はクラス名と同じなので、::を挟んで同じ名前が続いている部分がコンストラクタを定義している部分である。 (5) メンバ関数では非メンバ関数と違い、関数の中で定義された構造体を普通の関数のように扱うことができる。 |
======== [解答2] 1. 同じメンバ内で宣言されたメンバ関数からのみアクセスできるようにし、設定方法を一つに統一する。 2. 浅いコピー: yにxを代入するためにy=xとしている。x.Elementはある領域の先頭アドレスを指すので、y.Elementにはx.Elementと同じアドレスを格納することになる。つまり、配列をコピーしたのではなく、アドレスをコピーしただけの状態になっている このとき、y.Element[0]の値を変更すると、x.Elementの値も変更されてしまう。 つまり、わざわざ2つの変数を宣言した意味がなくなってしまう。 また、yが不要になってyの領域を解放した場合、xの領域が解放されたことに等しいので、その後x.Elementに何らかの値を代入しようとしたとき、その領域は全く別の用途に使用されている可能性があり危険である 深いコピー: 浅いコピーとは違い、アドレスではなくy.Element[0]=x.Element[0]、y.Element[1]=x.Element[1] のように要素一つ一つをコピーする 3. constを付けると、その変数は定数で変化しないことを表す。もし書き換えようとしたり、別の値を代入しようとしたらコンパイルエラーが起きるようにしている。 4. クラス名と同じ名前を持つ関数を探す 5. ・宣言 メンバ関数はクラスの宣言中で宣言 非メンバ関数はクラスの宣言の外で宣言 ・定義 メンバ関数は引数の設定が不要、データメンバ名だけで扱う 非メンバ関数は、引数の設定が必要 ・性質 メンバ関数は、同じメンバ中のprivateメンバにアクセスできる 非メンバ関数はprivateメンバにアクセスできない |