C++最速マスター その2
- 作者: グレゴリーサティア,ダウグブラウン,Gregory Satir,Doug Brown,望月康司,谷口功
- 出版社/メーカー: オライリー・ジャパン
- 発売日: 2001/11
- メディア: 単行本
- 購入: 9人 クリック: 147回
- この商品を含むブログ (29件) を見る
index
最速C++マスター その2です。今回内容としてまとめることは以下の通りです。
Const
Constは変数を後から書き換えができないようにする制約修飾子です。Const変数に対して書き換えを行うプログラミングを書くとコンパイル時にread-onlyエラーがでます。Constを付けると誤ったプログラミングを防ぐだけでなく、コンパイラが最適化しやすくなるので出来る限りConstを付けても良い箇所は付けるようにすると良いと思います。Constは通常の変数だけでなく、ポインタ変数、関数の引数、メンバ関数にも適用可能です。
#include <string> #include <iostream> class Student { private : std::string name; int number; public : void setName( const std::string& name ); void setNumber( const int& number ); std::string getName() const; int getNumber() const; }; //Constructor Student::Student() { } //Destructor Student::~Student() { delete this; } // メンバ関数 引数をconstで定義 void Student::setName( const std::string& name ) { this->name = name; } // メンバ関数 引数をconstで定義 void Student::setNumber( const int& number ) { this->number = number; } // メンバ関数 メンバ関数もconstを指定できる std::string Student::getName() const { return this->name; } // メンバ関数 メンバ関数もconstを指定できる int Student::getNumber() const { return this->number; } int main(){ const int a = 1; // errorになる // a = 2; Student *student = new Student(); student->setName( "John" ); student->setNumber( 2 ); std::cout << student->getName() << std::endl; std::cout << student->getNumber() << std::endl; delete student; student = NULL; return 0; }
Static
Staticを付けた変数は静的変数と呼ばれ初期化が一度しか行われない、アドレスが固定な特殊変数になります。staticを付けた変数は関数を抜けても参照が可能です。Static修飾子をClassのメンバ変数や関数に付与するとオブジェクトに属さないものとして定義できます。
#include <string> #include <iostream> class Student { private : static std::string name; static int number; public : // staticメンバ関数として定義 static void setName( const std::string& name ) { Student::name = name; } static void setNumber( const int& number ) { Student::number = number; } static std::string getName() { return Student::name; } static int getNumber() { return Student::number; } }; std::string Student::name; int Student::number; int main(){ Student::setName( "John" ); Student::setNumber( 2 ); std::cout << Student::getName() << std::endl; std::cout << Student::getNumber() << std::endl; return 0; }
Template
Template Function
C言語の関数は一般的には厳密な型定義を必要とします。型定義が厳密である故に例えば同じような処理の関数なんだけど引数の型が決まってしまうと、それぞれの型にあった関数を作らなければならないという問題が発生します。これはTemplateを使って引数の汎用化を行う事で解消できます。関数定義の前にtemplate
関数名()という記述をすることで型の固定定義を回避できます。これにより利用する側で様々な形式の引数を設定できます。 #include <string> #include <iostream> template <typename X> X sum( X var_x, X var_y, X var_z ) { return var_x + var_y + var_z; } template <typename X, typename Y, typename Z> X max( Y var_y, X var_z ) { if( var_y > var_z ) { return var_y; } return var_z; } int main(){ // sumの型定義は一つ。同じ型であれば適用可能 int i_sum = sum( 1,2,3 ); std::cout << i_sum << std::endl; // 上と同じく全てfloat型。引数の汎用化に成功 float f_sum = sum( 1.0,2.0,3.0 ); std::cout << f_sum << std::endl; // 下は型定義のエラーになる。引数の方に intとdoubleが混在 //double dd_sum = sum( 1, 2, 3.0 ); // 引数の型指定を呼び出し側で行えるようにする double d_max = max<double, double, int>( 2.1, 3 ); std::cout << d_max << std::endl; return 0; }Template Class
TemplateClassもTemplateFunctionと同様で、Classとして扱うデータの型定義を汎用化します。以下では汎用的なStackクラスを定義し様々なデータ型を保持可能としています。
#include <string> #include <iostream> template <typename TYPE> class Stack { private: TYPE buffer[100]; int index; public: Stack(); bool push( TYPE var ); TYPE pop(); }; template <typename TYPE> Stack <TYPE> ::Stack() { index = 0; } template <typename TYPE> Stack <TYPE> ::~Stack() { } template <typename TYPE> bool Stack <TYPE> ::push( TYPE var ) { buffer[index] = var; index++; return true; } template <typename TYPE> TYPE Stack <TYPE> ::pop() { --index; return buffer[index]; } // main int main(){ Stack <int> int_stack; int_stack.push( 1 ); int_stack.push( 2 ); std::cout << int_stack.pop() << std::endl; std::cout << int_stack.pop() << std::endl; Stack <char *> str_stack; str_stack.push( "test" ); str_stack.push( "stack" ); std::cout << str_stack.pop() << std::endl; std::cout << str_stack.pop() << std::endl; return 0; }上の例をポインタを使って書き換えてみます。classの定義は同じなのでint mainの中だけ書き換えます。
// main int main(){ Stack <int> *int_stack = new Stack <int>; int_stack->push( 1 ); int_stack->push( 2 ); std::cout << int_stack->pop() << std::endl; std::cout << int_stack->pop() << std::endl; delete int_stack; int_stack = NULL; Stack <char *> *str_stack = new Stack <char *>; str_stack->push( "test" ); str_stack->push( "stack" ); std::cout << str_stack->pop() << std::endl; std::cout << str_stack->pop() << std::endl; delete str_stack; str_stack = NULL; return 0; }
SmartPointer
stdに備わっているauto_ptr/shared_ptr/unique_ptrはSmartPointerと呼ばれるpointerで、newにより動的確保したオブジェクトポインタを自動的にdeleteしてくれる便利な機能です。auto_ptr
変数( new Class名前() );とクラスの動的呼び出しを行い、使わなくなった時点で自動的にdelete処理が働きClassのDestructorが呼び出されます。auto_ptrは所有権限の移行が=演算子で行われ、基は破壊されることになります。このような挙動問題がありauto_ptrは非推奨となっています。C++0xでは代わりに=演算子で所有権限が移行できないunique_ptrの利用が勧められています。 #include <iostream> #include <memory> using namespace std; class Student { private : std::string name; int number; public : Student(); ~Student(); void setName( const std::string& name ); void setNumber( const int& number ); std::string getName() const; int getNumber() const; }; //Constructor Student::Student() { cout << " ---constructor--- " << endl; } //Destructor Student::~Student() { cout << " ---destructor--- " << endl; } // メンバ関数 引数をconstで定義 void Student::setName( const std::string& name ) { this->name = name; } // メンバ関数 引数をconstで定義 void Student::setNumber( const int& number ) { this->number = number; } // メンバ関数 メンバ関数もconstを指定できる std::string Student::getName() const { return this->name; } // メンバ関数 メンバ関数もconstを指定できる int Student::getNumber() const { return this->number; } int main(){ //auto_ptrは非推奨となっているので利用には注意してください。 auto_ptr<Student> student( new Student() ); student->setName( "John" ); student->setNumber( 2 ); std::cout << student->getName() << std::endl; std::cout << student->getNumber() << std::endl; }
STL
STLはStandard Template Libraryの略で古くからあるC++のラリブラリです。Standard Template Library - Wikipedia 上で紹介したTemplateを有効に利用し、ライブラリ利用者はデータ型に捕われない使い方が可能です。以下では代表的なContainerの内容を中心に書きます。
Container List
Containerはデータを格納するものです。Cでは標準で備わっていなかった連想配列が使えたりします。それぞれのContainerに特徴があり使い方に注意しないと効率が悪いデータの持ち方をする可能性があります。以下簡単ですが表にまとめます。
Container Feature include header vector 動的配列/ 配列にランダムアクセス可能 / 末尾への挿入削除は高速 / 要素の数の変化が多い場合は注意 vector queue キュー / FIFO / 要素の挿入と取り出しのみが可能 queue deque 両端キュー/ランダムアクセス可能 / 先頭、末尾のデータ追加、取得に適している。 deque list 双方向リスト / シーケンシャルアクセス可能(ランダムアクセスには不向き) / 任意の位置に挿入、削除が高速 list stack スタック / FILO / 最後の要素追加と取得に適している stack priority_queue 優先順位付きqueue / 優先度の高いものから取り出す queue set セット / ソートをして要素を追加 / 値の重複が禁止 set multiset マルチセット / データの重複が可能なset set map 辞書 / 連想配列を作るのに適している。key value形式 / keyの重複は禁止 map multimap マルチ辞書 / 連想配列 / keyの重複が可能 map Vector
Vectorは動的な配列です。C++では配列表現はvectorを使う事で可能ですし、C++では配列を使うのはできるだけ避けるべきと言われています。以下にサンプルコードを記載します。push_back,back,pop_backなどの関数を覚えて基本的な使い方に慣れると良いと思います。
#include<iostream> #include<vector> #include <algorithm> using namespace std; template<typename T> void print_vector( const vector<T> & vect ) { for( int j=0; j<vect.size(); j++ ) { cout << vect[j] << endl; } } int main() { cout << " --- int vector --- " << endl; // int型のvectorを定義 vector<int> vect; //末尾に要素追加 vect.push_back( 1 ); vect.push_back( 100 ); vect.push_back( 2); // ソート cout << " --- sort --- " << endl; sort( vect.begin(), vect.end() ); print_vector( vect ); // 降順でソート cout << " --- sort desc --- " << endl; sort( vect.rbegin(), vect.rend() ); print_vector( vect ); //要素を末尾から取り出す cout << " --- pop --- " << endl; while( !vect.empty() ) { cout << vect.back() << endl; vect.pop_back(); } cout << " \n\n "; cout << " --- string vector --- " << endl; // string型のvectorを定義 vector<string> s_vect; //末尾に要素追加 s_vect.push_back( "vector" ); s_vect.push_back( "map" ); s_vect.push_back( "stack" ); // ソート cout << " --- sort --- " << endl; sort( s_vect.begin(), s_vect.end() ); print_vector( s_vect ); // 降順でソート cout << " --- sort desc --- " << endl; sort( s_vect.rbegin(), s_vect.rend() ); print_vector( s_vect ); //要素を末尾から取り出す cout << " --- pop --- " << endl; while( !s_vect.empty() ) { cout << s_vect.back() << endl; s_vect.pop_back(); } return 0; }--- int vector --- --- sort --- 1 2 100 --- sort desc --- 100 2 1 --- pop --- 1 2 100 --- string vector --- --- sort --- map stack vector --- sort desc --- vector stack map --- pop --- map stack vectormap
mapはkeyとvalueを組み合わせた連想配列です。データはkeyでsortされて保持されます。insert,find,erase関数を覚えておくと便利に使えます。
#include <iostream> #include <string> #include <map> #include <algorithm> using namespace std; template<typename X,typename Y> void print_map( map<X,Y>& value ) { typename map<X,Y>::iterator it = value.begin(); while( it != value.end() ) { cout << it->first + ":" + it->second << endl; ++it; } } int main() { cout << " --- key int map --- " << endl; //mapを定義 keyがint valueがstring map<int,string> int_values; //要素追加 int_values.insert( map<int,string>::value_type( 5, "c++" ) ); int_values.insert( map<int,string>::value_type( 10, "python" ) ); int_values.insert( map<int,string>::value_type( 2, "php" ) ); int_values.insert( map<int,string>::value_type( 1, "java" ) ); //defaultはkeyでsort print_map( int_values ); cout << " --- key string map --- " << endl; //mapを定義 keyもvalueもstring map<string,string> string_values; //要素追加 string_values.insert( map<string,string>::value_type( "love", "c++" ) ); string_values.insert( map<string,string>::value_type( "dislike", "python" ) ); string_values.insert( map<string,string>::value_type( "hate", "php" ) ); string_values.insert( map<string,string>::value_type( "like", "java" ) ); //defaultはkeyでsort print_map( string_values ); cout << " --- find map --- " << endl; //検索 map<string,string>::iterator it = string_values.find( "like" ); if( it != string_values.end() ) { cout << it->first + ":" + it->second << endl; } return 0; }--- key int map --- 1:java 2:php 5:c++ 10:python --- key string map --- dislike:python hate:php like:java love:c++ --- find map --- like:java
curl
C++でcurlにてhttp通信でデータを取得する方法について記述します。centosの環境ではcurl-develを必要とするのでyum install curl-develを実行して入れておきます。libcurlがcurl_ease_init,curl_ease_setopt,curl_easy_perfome,curl_easy_cleanupという関数を用意しているのでそれを組み合わせて使います。
#include <iostream> #include <string> #include <curl/curl.h> using namespace std; size_t writeFunction( char *pointer, const size_t size, const size_t nmemb, string* userp ) { int realsize = size * nmemb; userp->append(pointer, realsize); return realsize; } int main() { CURL *ch; CURLcode res; string write_data; ch = curl_easy_init(); curl_easy_setopt( ch, CURLOPT_URL, "http://d.hatena.ne.jp/yutakikuchi" ); curl_easy_setopt( ch, CURLOPT_WRITEFUNCTION, writeFunction ); curl_easy_setopt( ch, CURLOPT_WRITEDATA, (string*)&write_data ); res = curl_easy_perform( ch ); curl_easy_cleanup(ch); if( res != 0 ) { return 1; } printf("%s", write_data.c_str() ); return 0; }