インストールはC言語のページを参照して欲しい
C :引数を取らない場合、voidを明示しなければならない C++:引数を取らない場合、何も記載しないでも良い(voidを明示しても良い)
C :戻り値がある場合、値を返さなくてもコンパイル可能 C++:戻り値がある場合、必ず何らかの値を返さないとコンパイルエラー
C :関数などのブロックの先頭で宣言しなければならない(注) C++:任意の位置で宣言可能 (注)C99規格対応により、任意位置での宣言がサポートされた
C++:for文の初期化部分で宣言可能、かつ、スコープはループ内(注) (注)VC2003の場合、"言語拡張"オプションで変更可能、かつ、デフォルトでは、スコープはループ内に収まらないので注意。 (注2)VC6の場合、スコープはループ内に収まらず、かつオプションで変更も不可能である。
(例)
for ( int i = 0 ; i < 5 ; i++ ){
printf("%d", i);
}
i++; // ここではiは見えない。エラー。(注に記した場合を除く)
// による1行コメントが追加(注) (注)C99規格対応により、Cにも // がサポートされた
新しい型として、真偽値を表す bool が追加されました。
代入できる値は true または false のいずれかです。true は真を、falseは偽を表します。
bool b1 = true;
bool b2 = false;
if(b1){ ... } // 実行される
if(!b2){ ... } // 偽の否定は真なのでこれも実行される
if(b2){ ... } // 実行されない
dynamic_cast、static_cast、reinterpret_cast、const_cast が追加
参照型や、引数の参照渡しが追加(詳しくは後述)
C++では、struct, enum, classのキーワードは省略できることがある。
enum _sex{male, female, unknown};
struct human{
int age;
char* name;
_sex sex; // Cなら enum _sex sex; と書かねばならないが、enum を省略できる
};
int main(){
human Taro; // Cなら struct human Taro; と書かねばならないが、struct を省略できる
return 0;
}
まずは簡単なプログラムの説明をします。 以下のコードは画面に文字の表示をするプログラムでしょう。
#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;
参照とは、エイリアスとも呼び、「変数につけられた別名」です。
int a = 5; int& b = a; // bにaを代入する感じで~
int a = 5; int& b = a; b = 3;とすると、aの値も3に変わります。( b = 3; の代わりに a = 3; と書いたとの同じ結果を生じる。)
#include <iostream>
void inc(int& x){
x++;
}
int main(){
int a = 5;
inc(a);
std::cout << a << std::endl;
return 0;
}
このコードはどんな結果を生じるでしょうか。C++にはインライン関数というものがあります。
簡単に言うと、「(コンパイラが)関数のコードを、その関数を呼び出した場所にそのまま貼り付ける」といった感じです。(通常の関数呼び出しは別の場所にかかれたコードへジャンプする)
~定義方法~
インライン関数を定義するには、関数定義の頭に inline キーワードを付けます。
inline void inc(int* x){ // 定義の頭に inline を指定
(*x)++;
}
int main(){
int a = 0;
inc(&a);
return 0;
}
これをコンパイルすると、次のコードをコンパイルしたのと(ほぼ)同じアセンブリコードが生成されます。
int main(){
int a = 0;
int* x = &a; // この部分が inc(&a) に相当
(*x)++; // この部分が inc(&a) に相当
return 0;
}
ちょうど inc の中身が inc を呼び出した位置に展開された形になり、これを「インライン展開」と呼びます。
~インライン関数のメリット~
~インライン関数のデメリット~
~インライン関数を使う上での注意~
C++はCの(ほぼ)上位互換であり、同じコンパイラでコンパイルできます。
しかし、C++からCの関数を呼び出すには注意が必要です。
例えば、 A.c というソースコードと、A.c にある関数が宣言された A.h というヘッダがあったとして、次のような B.cpp を作ったとします。
#include <iostream>
#include "A.h"
int main(){
std::cout << a("foo_bar"); // A.hで char* a(char* s); と宣言されているとする
return 1;
}
この状態で A.c と B.cpp をプロジェクトに追加して、VisualC++6.0でビルドすると、
B.obj : error LNK2001: 外部シンボル ""char * __cdecl a(char *)" (?a@@YAPADPAD@Z)" は未解決です
と表示され、ビルドできません。
では、これを解決する方法を説明します。
以上をふまえて、修正された A.h は次のようになります。
#ifdef __cplusplus
extern "C" {
#endif
char* a(char* s); // 元々あった宣言文たち
......
#ifdef __cplusplus
}
#endif
これでめでたくビルドに成功します。
なんでこんなことが必要なのか知りたい人のために解説です。
まず、C、C++のコードはコンパイラによってアセンブリ言語に変換されます。
その際、各関数にあたる部分のコードのはじめには、ラベルという目印が付きます。
(例えば、たいていのCコンパイラなら a という関数のはじめには _a というラベルがつき、a を呼び出す時には call _a などという命令を呼びます。)
このラベルのつけ方が、CとC++で異なるために、extern "C" を使う必要が発生します。
C++では、同じ名前の関数でもオーバーロードが可能なため、 _a という簡単なラベルではなく、関数名、引数の型 の情報を持ったラベル名が使われます。
つまり、 B.cpp の a の呼び出し部分は
call _acharp
のようになるわけです。(これは例で、実際のラベル名は符号化されてぱっと見ても引数の型がわかるようにはなっていませんが。)
しかし A.c はCモードでコンパイルされているので、関数aのラベルは _a となっており、 _acharp というラベルは見つからずエラーとなります。
では extern "C" はどのような意味があるかというと、「この関数はCのラベル名で探してね」
とコンパイラに指定する働きがあります。
このおかげで B.cpp の a の呼び出し部分が call _acharp ではなく
call _a
となり、めでたく関数aを見つけることができて呼び出せる、というわけです。
注)以上を考えると、 extern "C" を使わなくても A.c をC++モードでコンパイルすればいいということに気づきます。実際、拡張子を .cpp に変えるかコンパイルオプションでC++としてコンパイルすれば、 A.h を修正しなくてもすみます。しかしあらかじめコンパイル済みのライブラリなどを使う(または配布する)ときにはそうはいかないので、extern "C" を使います。
例えばこんなクラスを定義(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;
}
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を加えても良い)で ググってくれたまえ!