「C++/C++特有の概念」の版間の差分

削除された内容 追加された内容
「CとC++の相違点」の節を追加
全体を修正
1 行
== オブジェクト指向とクラス ==
== Cの基礎知識 ==
C++ではオブジェクト指向をサポートするために'''クラス'''(class)が導入されています。
Cの基礎知識については、
オブジェクト指向は、
[[C言語/基礎知識]]を参照してください。
データと手続きを一体化して記述することで、
ただしprintf関数とscanf関数については、
プログラムの内容をわかりやすくするというプログラミングの手法です。
後述する[[#コンソール入出力]]で、
より改善された方法があるため、
学習する必要はあまりありません。
 
クラスはオブジェクト指向の中核となる概念で、
== CとC++の相違点 ==
様々な言語でクラスを定義することが出来ます。
#Cでは引数がない関数の関数原型の引数のリストにはvoidを指定する。一方、C++ではvoidの指定を省略できる。
C++以外ではJava, Python, Rubyなどがオブジェクト指向言語として有名です。
#Cでは関数原型の記述は推奨されるが任意である。一方、C++では必ず記述しなければならない。
#Cでは関数の戻り値の型がvoid以外の時でもreturn文で値を指定しなくともよい。一方、C++では必ず指定しなければならない。
#Cでは関数の戻り値の型を省略すると整数型が使われる。一方、C++では必ず型を指定しなければならない。
#Cではローカル変数の宣言はブロックの先頭で行う必要がある。一方、C++ではどこで行ってもよい。
#Cではbool型がない。一方、C++ではbool型がある。
 
C++のクラス定義は、
== オブジェクト指向 ==
他の言語と比べても非常に汎用的で出来ることが多く、
'''オブジェクト指向プログラミング'''(Object-Oriented Programming : OOP)は、
それだけにわかりにくい面もあります。
コンピュータプログラムの構造、構成法の一つで、
単純にオブジェクト指向に興味があるのなら、
関連するデータの集合体と、
PythonやRubyのオブジェクト指向の方が簡単かも知れないので、
それを操作する手続きを「オブジェクト」(object)と呼ばれるひとまとまりの単位として一体化し、
そちらも検討してみるとよいでしょう。
オブジェクトの組み合わせとしてプログラムを記述する手法である。
<ref>[http://e-words.jp/w/%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E6%8C%87%E5%90%91%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0.html オブジェクト指向プログラミングとは|OOP|Object Oriented Programming - 意味/定義 : IT用語辞典]</ref>
 
== オブジェクト指向とプログラムの大きさ ==
プログラミングでは、
どんどん複雑オブジェクト指向は特大きっていくプログラムに対応を作成するため場合よく用いられています。
逆に、それほど大きくないプログラムを書く時には、
新しい手法が次々と作られてきた。
オブジェクト指向はあまり役に立ちません。
具体的にどれくらいの大きさのプログラムが大きいと思うかは、
書き手の好みにもよります。
 
オープンソースからの具体例では、
最初は、フロントパネルのスイッチをオンオフすることによって、
webプラウザのMozilla Firefox、
ごく短いプログラムが作成された。
メールソフトのThunderbird、、
オフィスソフトのOpenOfficeは、
C++で書かれています。
一方、巨大プログラムと呼ばれてもよい大きさのプログラムでも、
Cで書かれているプログラムは存在します。
例えば、
OSのLinux、
ウィンドウシステムのX Window Systemは、
Cで書かれています。
(これらのソフトウェアにCが採用された理由には、
プロジェクト立ち上げ当時のC++の普及度や安定度、
あるいは対象となるソフトウェアの扱う領域や目的等の要因があると思われ、
今まさに同様のプロジェクトをはじめた場合にもCが選ばれるかは分かりません)
 
== オブジェクト指向とGtk ==
次に、アセンブリ言語によって、
オブジェクト指向が用いられる例として、
より長いプログラムが作成できるようになった。
GUIのウィジェットがあります。
ウィジェットは、
ウィンドウやボタンなど、
グラフィックを作成するために用いられるオブジェクトです。
これらは共通の操作として、最大化や最小化などを持っています。
これらの操作に対応する関数をウィジェットごとに作成するのは大変な作業なので、
多くの場合これらはオブジェクト自体に操作を与えるプログラム手法がとられます。
 
例えば、Gtkでは、
そして、1950年代、最初の高レベル言語(FORTRAN)が開発されたことによって、
Containerクラスとそれを継承したクラスに属するウィジェットには、
数千行にわたるプログラムが作成できるようになった。
<source lang="cpp">
しかし、何でも許される方式であったため、
gtk_container_add(GtkContainer, GtkWidget)
大きなプログラムを書こうとすると、
</source>
読みづらく保守しにくい「スパゲティコード」となってしまった。
という関数を用いてウィジェットを入れ込む作業が行われます。
このように、多くの似通ったデータを扱う際には、
オブジェクト指向プログラミングがよく用いられます。
ただし、Gtk+はCを用いてウィジェットを作成しているので、
ここでのオブジェクト指向は変則的です。
 
実際には、
1960年代には、'''構造化プログラミング言語'''(structured programming language)の登場で、
上の例ではオブジェクト指向でいう継承という考えが用いられています。
スパゲティコードを排除し、
継承とは、
五万行に及ぶプログラムを作成し、保守できるようになった。
似通った性質をもつクラスを複数作成する時に、
Cも広い意味では構造化プログラミング言語である。
それらの差分だけを新たに書き加え、
構造化プログラミング言語では、
共通の部分は既存のものを流用するという考え方です。
制御構造を使い、プログラムを構成要素に分割して記述する。
 
GtkWindowクラスは、
しかし、構造化プログラミング言語も、
GtkContainerクラスを継承しているので、
プログラムの長さが一定に達すると限界に達する。
GtkWindowクラスに対してもgtk_container_addを用いることが出来ます。
そこで、オブジェクト指向プログラミングでは、
これは、GtkWindowだけでなく、GtkContainerを継承した全てのクラスの特徴です。
前述したとおり、
プログラムを自己完結したオブジェクトとして記述することで、
複雑さを軽減し、
より巨大なプログラムを記述できるようにした。
 
実際に継承を用いる時には、
オブジェクト指向プログラミングには、
どの部分をどのクラスで導入するかが問題になりますが、
カプセル化、ポリモーフィズム、継承の3つの特色がある。
ある程度試行錯誤を繰り返すしかなさそうです。
これらについて説明していく。
 
C++でも、クラスの継承を用いることが出来ます。
=== カプセル化 ===
これについては後述します。
'''カプセル化'''(encapsulation)とは、
データとそれを操作する手続きを一体化して「オブジェクト」として定義し、
オブジェクト内の細かい仕様や構造を外部から隠蔽することである。
<ref>[http://e-words.jp/w/%E3%82%AB%E3%83%97%E3%82%BB%E3%83%AB%E5%8C%96.html カプセル化とは|encapsulation - 意味/定義 : IT用語辞典]</ref>
 
== クラスの基本 ==
手続きとデータを一体化し、
'''クラス'''(class)とは、データとその操作手順であるメソッドをまとめたオブジェクトの雛型を定義したものである。
手続きによってしかデータを操作できないようにすることで、
<ref>[http://e-words.jp/w/%E3%82%AF%E3%83%A9%E3%82%B9.html クラスとは|class - 意味/定義 : IT用語辞典]</ref>
ブラックボックスなオブジェクトを作成し、
独立性を高める。
 
=== クラスの宣言 ===
カプセル化では、
C++でクラスを宣言するには、classキーワードを用いて次のように記述する。。
データと手続きを、
<source lang="cpp">
'''非公開'''(private)と'''公開'''(public)に分けて、
class クラス名{
オブジェクトの外部からは、
//非公開関数と変数
非公開のものにはアクセスできず、
public:
公開のもののみにアクセスできるようにする。
//公開関数と変数
}オブジェクトリスト;
</source>
 
クラス名でクラスの名前を指定する。
カプセル化によって、保守性や開発効率を挙げることができ、
コードの再利用も簡単になる。
 
クラスは多くの点で構造体と似ている。
=== ポリモーフィズム ===
クラスはメンバ変数の他にメンバ関数を含めることができるが、
'''ポリモーフィズム'''(polymorphism、多相性、多態性、多様性)とは、
実は構造体もメンバ関数を含むことができる。
同名のメソッドや型などをオブジェクトの種類に応じて使い分けることができる性質のことである。
<ref>[http://wisdom.sakura.ne.jp/programming/cpp/cpp44.html 構造体と共用体]</ref>
<ref>[http://e-words.jp/w/%E3%83%9D%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%95%E3%82%A3%E3%82%BA%E3%83%A0.html ポリモーフィズムとは|polymorphism|ポリモルフィズム|多相性 - 意味/定義 : IT用語辞典]</ref>
 
class内の関数と変数のことをメンバ(member)と呼ぶ。
ポリモーフィズムでは、
メンバにはオブジェクトの外からアクセスできない非公開のものと、
同一の名前で複数の異なる動作を作成し使うことができる。
アクセスできる公開のものがある。
classのメンバはデフォルトで非公開である。
 
オブジェクトリストを指定すると、
例えば、Cで絶対値を求めるには、
その名前のオブジェクトを作成する。
引数のデータ型に合わせて、
複数のオブジェクトを作成する場合は、
abs(), labs(), fabs()の3つの関数を使い分ける必要がある。
「,(コンマ)で区切る。
一方、C++で絶対値を求めるには、
オブジェクトリストの指定は任意である。
引数がどのデータ型であっても、
abs()という1つの関数で求めることができる。
 
メンバ関数が大きい場合、
このように1つの関数名を複数の異なる目的で使うことを、
クラスの宣言の外にその定義を記述することもできる。
'''関数のオーバーロード'''(function overloading)という。
その場合、メンバ関数の定義は、
関数名の前に「クラス名::」を付ける。
「::(コロン2文字)」のことをスコープ解決演算子(scope resoluthion operator)と呼ぶ。
次のように記述する。
 
<source lang="cpp">
ポリモーフィズムの利点は、
戻り値の型 クラス名::関数名(仮引数の並び)
メソッド名を統一することで、
{
記述ミスを減らせるなどが挙げられる。
関数の本体
}
</source>
 
*例(メンバ関数の定義をクラスの宣言の中に書く)
=== 継承 ===
<source lang="cpp">
'''継承'''(inheritance)とは、
class aaa {
既に定義されているクラスをもとに、拡張や変更を加えた新しいクラスを定義することである。
int a;
<ref>[http://e-words.jp/w/%E7%B6%99%E6%89%BF.html 継承とは|inheritance|インヘリタンス - 意味/定義 : IT用語辞典]</ref>
public:
int getA() const {return a;}
int setA(int a){return this->a = a;}
};
</source>
 
*例(メンバ関数の定義をクラスの宣言の外に書く)
継承を使うことで、
<source lang="cpp">
あるものの性質を受け継ぎ独自の機能を追加することができる。
class aaa {
int a;
public:
int getA() const;
int setA(int a);
};
int aaa::getA() const
{
return a;
}
int aaa::setA(int a)
{
return this->a = a;
}
</source>
 
上の例では、
例えば、哺乳類を継承し人、犬、猫を新たに作るといった考え方ができる。
getA,setAというメンバ関数と、
aというintのデータを持ったクラスを定義しています。
classのメンバはデフォルトでです。
getAはaの中身を得るためのメソッドで、
setAはaを設定するためのメソッドです。
これらはaがprivateであることから必要になります。
setAでは、thisが用いられていますが、
これは与えられたクラスの性質を持つオブジェクトへのポインタを表しています。
ポインタなので、それが表すデータにアクセスする時には->を用います。
これは(*this).aと書いても同じ意味になります。
 
=== オブジェクトの作成 ===
継承では、
オブジェクトの作成は次のように記述する。
異なる部分だけを新たに作るだけでよく、
<source lang="cpp">
コードの再利用が可能になる。
クラス名 オブジェクト名;
</source>
 
クラス名でクラスの名前を指定する。
== コンソール入出力 ==
オブジェクト名で作成するオブジェクトの名前を指定する。
Cでコンソール入出力する場合、
複数のオブジェクトを作成する場合は、
printf関数やscanf関数を使った。
「,(コンマ)で区切る。
 
*例(ob1, ob2というaaaクラスのオブジェクトを作成する。)
C++では、
<source lang="cpp">
改善された新しい方法で
aaa ob1, ob2;
コンソール入出力を行うことができる。
</source>
入出力演算子である出力演算子<<と入力演算子>>を使う。
これらはビット単位のシフト演算子としても使われるが、
C++で新たな機能が追加された。
 
入出力演算子を使うには、
<iostream>ヘッダをインクルードする必要がある。
 
=== メンバへのアクセス ===
コンソールへの出力は次のように行う。
メンバにアクセスするには、ドット(.)演算子を用いて次のように記述する。
<source lang="cpp">
オブジェクト名.メンバ名
cout<<式;
</source>
<<演算子を複数使うことで、
複数の項目を出力することもできる。
 
*例(ob1のsetAメンバ関数を呼び出す。)
キーボードからの入力は次のように行う。
<source lang="cpp">
ob1.setA(1234);
cin>>変数;
</source>
scanf関数ように&が付いていないことに注意。
複数の項目を入力するには、
空白文字(スペース、タブ、改行)で区切る必要がある。
 
=== オブジェクトののコピー ===
入出力演算子は、
 
演算子オーバーロードの例でもある。
 
== クラスの詳細 ==
=== カプセル化 ===
カプセル化とは、オブジェクト指向のクラスにおいて、
オブジェクトが与える手法以外の手法で、
オブジェクトの持つデータに影響を与えてはいけないとする考え方です。
これはオブジェクト自身が自分のデータを扱う方法を全て提供すべきで、
それ以外の方法に頼ることはクラスの設計の問題であるとする考え方にもとづいています。
 
Javaなどの言語でも、
カプセル化がサポートされています。
Pythonなどの言語では、
カプセル化は(あまり)サポートされていません。
 
C++では、
public, private, protectedの3つのキーワードがアクセス制御として与えられています。
privateは、同じクラスに属するメソッドからしか値が呼び出すことができなくなります。
publicは、プログラムの他の部分からも呼び出せるようになります。
protectedは、クラスを継承した先から用いることが出来るようになります。
しかし、プログラムの他の部分から呼び出すことは出来ません。
 
<source lang="cpp">
//例 コンソーカプセ出力
#include <iostream>
using namespace std;
 
class aaa {
int a;
public:
int getA() const {return a;}
int setA(int a){return this->a = a;}
};
 
int main()
{
intaaa i=1234ob1;
//ob1.a=1234;//この行はコンパイルエラーになる。
cout<<"iの値は"<<i<<"です"<<endl;
ob1.setA(1234);
return 0;
cout<<"ob1のaの値は"<<ob1.getA()<<endl;
}
</source>
 
=== ポリモーフィズム ===
ポリモーフィズムは、
オブジェクト指向の考え方の一つで、
同一の名前で複数の異なる動作を作成し使う。
 
ポリモーフィズムは、
C++では関数のオーバーロードで実現される。
関数のオーバーロードとは、
複数の関数を同じ名前で作成し使うことである。
関数のオーバーロードを行うには、
単に同じ名前の関数を複数定義するだけである。
ただし、引数の型か個数が異ならなければ、
関数のオーバーロードはできない。
戻り値の型だけが異なっていてもできない。
 
<source lang="cpp">
//ポリモーフィズムの例
//例 コンソールから入力
#include <iostream>
using namespace std;
 
int add(int a, int b);
int add(int a, int b, int c);
 
int main()
{
cout<<"1+2は"<<add(1,2)<<endl;
int i;
cout<<"整数を入力してください。1+2+3は"<< add(1,2,3)<<endl;
}
cin>>i;
 
cout<<"入力された値は"<<i<<"です。"<<endl;
int add(int a, int b)
return 0;
{
return a+b;
}
 
int add(int a, int b, int c)
{
return a+b+c;
}
</source>
 
=== 継承 ===
 
=== コンストラクタ関数 ===
 
=== デストラクタ関数 ===
 
== クラス・構造体・共用体の比較 ==
 
== 脚注 ==