Aritalab:Lecture/Programming/Cpp
m (→コンパイルの仕方) |
m |
||
| Line 1: | Line 1: | ||
{{Lecture/Header}} | {{Lecture/Header}} | ||
==C++プログラミング== | ==C++プログラミング== | ||
| − | C++ | + | C++はC言語の完全な上位互換ですが、オブジェクト指向型という点でC言語とは全く違うプログラミング思想に基づいています。簡単に言うと、JavaはC++の利点を活かすように開発された言語です。 |
| − | + | ||
===マクロ, inlineの利用=== | ===マクロ, inlineの利用=== | ||
| Line 59: | Line 58: | ||
<pre> | <pre> | ||
#include <iostream> | #include <iostream> | ||
| + | #include <fstream> | ||
#include <string> | #include <string> | ||
#include <vector> | #include <vector> | ||
| − | |||
using namespace std; | using namespace std; | ||
int main(int argc, char *argv[]) | int main(int argc, char *argv[]) | ||
{ | { | ||
| + | vector<string> lines; | ||
| + | string line; | ||
for(int i=1; i < argc; i++) { | for(int i=1; i < argc; i++) { | ||
//argv[0]には実行ファイル名が入っている | //argv[0]には実行ファイル名が入っている | ||
| − | + | ifstream fin; | |
| − | + | fin.open(argv[i], ios::in); | |
| − | + | if(!fin ){// 必ずエラーチェックを入れる | |
| − | + | cout << "Error: cannot open file(" << argv[i] << ")" << endl; | |
| − | + | exit(1); | |
| − | + | } | |
| − | + | while(getline(inf, line)) | |
| − | + | lines.push_back(line); | |
| − | + | ||
} | } | ||
| + | for(vector<string>::iterator itr=lines.begin(); | ||
| + | itr != lines.end(); ++itr) | ||
| + | cout << itr->c_str() << endl; | ||
} | } | ||
</pre> | </pre> | ||
| Line 140: | Line 143: | ||
delete[] cArray; | delete[] cArray; | ||
| − | char** cArray2 = (char**) malloc(sizeof(char)*10); | + | char** cArray2 = (char**) malloc(sizeof(char*)*10); |
free(cArray2); | free(cArray2); | ||
</pre> | </pre> | ||
===Garbage Collection=== | ===Garbage Collection=== | ||
| − | Javaとの最大の違いは、メモリ管理を自分で行う点です。これは単に「遅れている」という訳ではなく、実時間プログラミングのようなGarbage collector(GC) | + | Javaとの最大の違いは、メモリ管理を自分で行う点です。これは単に「遅れている」という訳ではなく、実時間プログラミングのようなGarbage collector(GC)が致命的となる場合でも用いられる「汎用性」と捉えましょう。C++の人が GC を使う際には boost::shared_ptr 等を使うのが普通だそうです。 |
Revision as of 17:04, 7 October 2010
| Wiki Top | Up one level | レポートの書き方 | Arita Laboratory |
|
C++プログラミング
C++はC言語の完全な上位互換ですが、オブジェクト指向型という点でC言語とは全く違うプログラミング思想に基づいています。簡単に言うと、JavaはC++の利点を活かすように開発された言語です。
マクロ, inlineの利用
C言語でマクロが果たしていた役割を、C++ではinline関数を用いて実現できます。 関数定義を
inline int cmp(const char& x, const char& y)
{ if (x < y) return -1; else if (x > y) return 1; else return 0; }
と書いておくと、cmp(x,y)という呼び出しはコンパイル時に全てソース中に展開されます。 実行ファイルが大きくなるのは欠点ですが、処理速度を犠牲にせずに可読性が上がります。
ヘッダーファイル
利用する関数の型情報は.hや.hhという拡張子を持つヘッダーファイルに記述し、それをプログラムの先頭で読み込みます。一番有名なヘッダーは<stdio.h>と<iostream>でしょう。
#include <stdio.h>
iostreamも以前は拡張子(.h)を付けてインクルードしていたのですが、標準化の際に付けないことが決まりました。なので以下のように書いてください。
#include <iostream> using namespace std;
二行目のusing以下は、iostreamで定義される標準入出力 std::cin, std::cout を、std::というネームスペース名無しで利用するために記述します。
ヘッダーファイルはクラスを定義するソースプログラム毎に用意します。ディレクトリ内に.cや.cpp等のファイルと混在してくると面倒です。例えばinclというディレクトリを作り、その中にヘッダーを集めておくと便利でしょう。(コンパイル方法は後述。)
各ヘッダーファイルは、プログラム中で複数回読み込むとエラーになります。ヘッダーファイルの先頭に
#ifndef LIST_HH #define LIST_HH #include "iterator.hh" : #endif
のように、ifndefのおまじないを入れておきましょう。
コンパイルの仕方
統合環境を使わない場合は、Makefileを作りましょう。Makeではタブが意味を持つので注意します。下の例では、ヘッダーをinclディレクトリに記述する場合です。(タブが重要なので、下の内容を単にcopy&pasteしても動きません。)
- 簡単な Make ファイル
CXX = g++ -pg -ansi -g -Wall -Iincl
DIRS = src
SRCS = $(wildcard *.cc)
OBJS = $(wildcard *.o)
clean:
rm -f main
main: $(SRCS) $(OBJS)
$(CXX) S(OBJS) -pg -o main
これを用意しておくと、make main や make cleanと打つだけでコンパイルや片づけができます。
- 与えられたファイルを全て結合して表示する cat.cpp
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
using namespace std;
int main(int argc, char *argv[])
{
vector<string> lines;
string line;
for(int i=1; i < argc; i++) {
//argv[0]には実行ファイル名が入っている
ifstream fin;
fin.open(argv[i], ios::in);
if(!fin ){// 必ずエラーチェックを入れる
cout << "Error: cannot open file(" << argv[i] << ")" << endl;
exit(1);
}
while(getline(inf, line))
lines.push_back(line);
}
for(vector<string>::iterator itr=lines.begin();
itr != lines.end(); ++itr)
cout << itr->c_str() << endl;
}
クラス定義
class class名 {
private:
// 外からアクセスできない変数やメンバー関数
public:
// 外からアクセス可能な変数やメンバー関数
};
と定義します。C言語における構造体との主な違いはアクセス制限です。 クラス定義の中では、自分自身をthisポインタで参照することができます。
メモリ管理
コンストラクタとデストラクタ
C++のクラス定義では、例えば以下のような記述をします。
class list_node;
typedef list_node* lnode;
class list_node {
private:
list_node(const list_node&);
list_node& operator=(const list_node&);
public:
void* key;
lnode list_pred;
lnode list_succ;
list_node(GenPtr x=0x0) : key(x), list_pred(0), list_succ(0) {}
~list_node() {}
};
この中でlist_node(GenPtr x=0x0)とあるのがコンストラクタ、~list_node()がデストラクタで、それぞれクラスのインスタンス生成、消去時に呼び出されます。コンストラクタ内ではクラス変数の初期化、デストラクタ内では必要なくなったポインタのnull化などをしておくと良いでしょう。(後者は無駄に思えるかもしれませんが、バグをなくすのに役立ちます。)
コピーコンストラクタと演算子のオーバーロード
上の例でprivate指定でアクセス制限されているのが、コピーコンストラクタlist_node(const list_node&)と演算子=の定義list_node& operator=(const list_node&)になります。 コピーコンストラクタは、
list_node x = 既に定義されているlist_nodeクラス;
と=をつけて初期化する場合と、関数にクラスを(参照渡しではなく)値渡しするときに実行されます。 また、演算子=のほうは、
list_node x,y; x = y = 既に定義されているlist_nodeクラス;
と書かれたときに実行されます。上の例では、list_nodeクラスがユーザに無闇にコピーされることを防ぐためにprivate指定にしています。
new と delete
Cにおけるmalloc/freeと、new/deleteは同じではありません。 クラスのnew/deleteでは、コンストラクタとデストラクタが呼ばれることに注意しましょう。 基本データ型のnew/deleteは、基本的にmalloc/freeと同じですが、void*しか返さないmallocに比較して型キャストの必要がありません。
ですから、C++らしいプログラムを心がけるには、常にnew/deleteを使うと良いでしょう。
- 働きの全く同じ二つの例
char** cArray = new char*[10]; delete[] cArray; char** cArray2 = (char**) malloc(sizeof(char*)*10); free(cArray2);
Garbage Collection
Javaとの最大の違いは、メモリ管理を自分で行う点です。これは単に「遅れている」という訳ではなく、実時間プログラミングのようなGarbage collector(GC)が致命的となる場合でも用いられる「汎用性」と捉えましょう。C++の人が GC を使う際には boost::shared_ptr 等を使うのが普通だそうです。