C++とは

  • C++はC言語にクラス・テンプレート・例外処理などを加えたものです。
  • そういうわけで、Cをある程度理解してから勉強してからでないと、なんのこったかワカランと思うのであります。
  • Cは理解したぜ! って人でも、「自分の書いたプログラムのバグが取れねー」とかいっているのなら! もしそーなのなら! C++の勉強する前に、コーヒーブレイクとして、お勧めするサイトがあるのだ。「パソコン初心者の館」の『Cプログラミング診断室』である。大笑いしながら見るのも一興、青ざめながら見るのもまた一興。

インストール

インストールはC言語のページを参照して欲しい

"クラス・テンプレート・例外処理"以外の違い

関数のプロトタイプ

C :引数を取らない場合、voidを明示しなければならない
C++:引数を取らない場合、何も記載しないでも良い(voidを明示しても良い)

戻り値

C :戻り値がある場合、値を返さなくてもコンパイル可能
C++:戻り値がある場合、必ず何らかの値を返さないとコンパイルエラー

変数の宣言位置

C :関数などのブロックの先頭で宣言しなければならない(注)
C++:任意の位置で宣言可能
(注)C99規格対応により、任意位置での宣言がサポートされた

for文

C++:for文の初期化部分で宣言可能、かつ、スコープはループ内(注)
(注)VC2003の場合、"言語拡張"オプションで変更可能、かつ、デフォルトでは、スコープはループ内に収まらないので注意。

(例)

for ( int i = 0 ; i > 5 ; i++ )

コメント

// による1行コメントが追加(注)
(注)C99規格対応により、Cにも // がサポートされた

キャスト

dynamic_cast、static_cast、reinterpret_cast、const_cast が追加

参照

参照型や、引数の参照渡しが追加

簡単なプログラム

まずは簡単なプログラムの説明をします。 以下のコードは画面に文字の表示をするプログラムでしょう。

#include<iostream>

using namespace std;//stdという名前空間を使用する

int main()//メイン関数。すべての始まり
{
	cout << "Hello,Work" <<endl;//Hello,Workと画面に表示する
	return 0;//システムに0を返す。
}

説明はコメントに書いてあるとおりです Hello,Workと書いてあるところを他の文字列にして試してみてください。 他の文字列を表示することができます。

オブジェクト指向プログラミング

C++はクラス単位でプログラミングを行う言語です。

そもそもクラスって何?という方に簡単な説明をすると クラスは変数や関数を一まとめにしたものです。

何のためにクラスを使うかの?というのを説明すると プログラムの変更をしたいときその変更を最小限に抑えるためです。 クラスにまとめることによりプログラムそのものを書き換えることなくクラスを書き換えるだけでプログラムを変更できます では上の文字表示プログラムをクラスを使って書いて見ましょう。 (*注意:実際に文字を表示するだけの小さなものをクラスにまとめるのは馬鹿のすることです。)

#include<iostream>

using namespace std;

class PrintCharactor {//PrintCharactorというクラスを宣言
public:
	void PutChar(char *);//PrintCharactorというクラスにPutCharという関数がありますよということを示す
};

void PrintCharactor::PutChar(char *str) {
	cout << str <<endl;//strを表示
}

int main() {
	PrintCharactor a;//aという名前でクラスを宣言して使えるようにする
	a.PutChar("Hello,Work");//PutChar関数を使って画面に表示
	return 0;
}

これで画面に表示できます。 かなり意味不明ですね。 次にこれの説明をします。

プログラムの詳細

先ほどの上のプログラムをコンパイルするとHello,Workが表示されると思います。 ではそのプログラムの詳細を示して行きたいと思います。

#include<iostream>

この部分はiostreamというファイルを取り込みます。こうすることでいろいろな関数を使えるようになります。

class PrintCharactor {//PrintCharactorというクラスを宣言
public:
	void PutChar(char *);//PrintCharactorというクラスにPutCharという関数がありますよということを示す
};

この部分はPrintCharactorというクラスを宣言します。 public:というのはこれをつけることで外からもアクセスできますということを示します。 これがないと外からアクセスできません。

int main() {
	PrintCharactor a;//aという名前でクラスを宣言して使えるようにする
	a.PutChar("Hello,Work");//PutChar関数を使って画面に表示
	return 0;
}

この部分はメイン関数です。 PrintCharactor a;はPrintCharactorクラスをaという名前で作ります。 a.PutChar("Hello,Work");この部分はaという名前で宣言されたクラスのPutcharという関数を呼びます。

これによって文字が表示できます。

関数オーバーロード

同じ名前で中身の違う関数を複数定義できます。
関数をオーバーロードするには、関数の呼び出し時に関数が区別できないと、つまり関数の引数の型が違わないとだめです。

//int 11 を返す関数 f()
int f(){
    return 11;
}

//a+b を返す関数 f(int,int)
int f(int a,int b){
    return a+b;
}

//こういうことも可能です
int f(int a,int b,int c){
    return f()+a+b+c;
}

ただし、無闇に多くの関数をオーバーロードすると乱雑になりやすいので注意しましょう。

演算子のオーバーロード

演算子の機能を、関数としてプログラマが定義できます。
これは、自前で定義したクラスの演算を分かりやすくしたい時などに便利です。 演算子のオーバーロードをするには、キーワードoperatorを使います。

//メートル
class Metre{
public:
    float v;
    Metre(float n){ v = n; }
};

//キロメートル
class KMetre{
public:
    float v;
    KMetre(float n){ v = n; }
};

//メートルとキロメートルの加算
Metre operator + (const Metre &m,const KMetre &km){
    return Metre( m.v + (km.v/1000.0f) );
}

KMetre operator + (const KMetre &km,const Metre &m){
    return KMetre( (m.v*1000.0f) + km.v );
}

//..........

Metre  X(100);
KMetre Y(4);

//クラスは違うが、演算は可能
Metre  Z=X+Y;
KMetre W=Y+X;

ほんのり多態性の紹介

例えばこんなクラスを定義(Character.h)したとして

class Character
{
public:
//	virtual void Say() = 0;		// 一身上の都合により純粋仮想関数にはしない
	virtual void Say() {	std::cout << "セリフが無いんじゃよ" << std::endl;};
};
class Hero : public Character
{
public:
	void	Say()	{	std::cout << "悪が栄えた試しなし!" << std::endl;		}
};
class Zako : public Character
{
public:
	void	Say()	{	std::cout << "イー!" << std::endl;	}
};
class Boss : public Character
{
public:
	void	Say()	{	std::cout << "やっておしまい!" << std::endl;	}
};

こんな感じだね。よく初心者サイトに載ってる例は。 でも、これだと、どんなメリットがあるのか良くわかんないんだよね。

#include<iostream>
#include "Character.h"
using namespace std;
int main()
{
	Hero	a;
	Zako	b;
	Boss	c;
	a.Say();
	b.Say();
	c.Say();
	return 0;
}

実行例

c:\>Sample
悪が栄えた試しなし!
イー!
やっておしまい!

ちなみに、こんなことはしちゃだめね。

#include<iostream>
#include<list>
#include "Character.h"
using namespace std;
int main()
{
	Hero	a;
	Zako	b;
	Boss	c;
	list<Character>	aryCharacter;
	aryCharacter.push_back(a);
	aryCharacter.push_back(b);
	aryCharacter.push_back(c);
	for(list<Character>::iterator iter = aryCharacter.begin(); iter != aryCharacter.end(); ++iter){
		iter->Say();
	}
	return 0;
}
  • listはSTLに含まれるコンテナで、すさまじく簡単に言ってしまえば動的な配列の一つ。{{br}}
  • iteratorはコンテナにアクセスするためのもので、ポインタみたいに使える。 実行例
    c:\>Sample
    セリフが無いんじゃよ
    セリフが無いんじゃよ
    セリフが無いんじゃよ

動的生成すると、ちゃんと動作するんだけど、メモリリーク対策が面倒くさい。 この例ではたった3行だけどね。

#include<iostream>
#include<list>
#include "Character.h"
using namespace std;
int main()
{
	Hero	a;
	Zako	b;
	Boss	c;
	list<Character*>	aryCharacter;
	aryCharacter.push_back(new Hero);
	aryCharacter.push_back(new Zako);
	aryCharacter.push_back(new Boss);
	for(list<Character*>::iterator iter = aryCharacter.begin(); iter != aryCharacter.end(); ++iter){
		(*iter)->Say();
	}
	for(list<Character*>::iterator jter = aryCharacter.begin(); iter != aryCharacter.end(); ++iter){
		delete *jter;
	}
	return 0;
}

実行例

c:\>Sample
悪が栄えた試しなし!
イー!
やっておしまい!

そしてメモリリーク対策としてスマートポインタ(boostライブラリのshared_ptr)を導入してみる。 (STLにはスマートポインタとしてauto_ptrがあるけど、コンテナに入れることは出来ない)
できたー! って思ってテストしたらなんか動作がおかしい…もしかしてメモリリーク? って思ったのでは、せっかくの多態性の恩恵も台無しだから、慣れない内・使えない事情が無いならば、使っとけ!

#include<iostream>
#include<list>
#include<boost/shared_ptr.hpp>
#include "Character.h"
using namespace std;
int main()
{
	typedef boost::shared_ptr<Character>	pChar;
	list<pChar>	aryCharacter;
	aryCharacter.push_back(pChar(new Hero));
	aryCharacter.push_back(pChar(new Zako));
	aryCharacter.push_back(pChar(new Boss));
	for(list<pChar>::iterator iter = aryCharacter.begin(); iter != aryCharacter.end(); ++iter){
		iter->get()->Say();
	}
	return 0;
}

実行例

c:\>Sample
悪が栄えた試しなし!
イー!
やっておしまい!

で、こーなってくると、動的生成部分がもーちょっとなんとかならんかなーて思うんだよね。 戻り値がCharacter*な関数があれば、いろいろ便利になる。 しかし面倒くさくなったので、興味がある人は"factory デザインパターン"(abstractを加えても良い)で ググってくれたまえ!