グリとブランのC++講座 ~c++98(stl)と、ほんのちょっとのc++11~

  • 7,327 views
Uploaded on

C++入門者向けに、STLのコンテナを中心に解説してあります。

C++入門者向けに、STLのコンテナを中心に解説してあります。

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
7,327
On Slideshare
0
From Embeds
0
Number of Embeds
5

Actions

Shares
Downloads
22
Comments
0
Likes
12

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. グリとブランの C++ 講座株式会社ヘキサドライブ原 龍 (ドラゴン)~ C++98 (STL) と、ほんのちょっとの C++11 ~
  • 2. 最近はUnity等を使っての開発が浸透してきて、C++ を書く機会が少なくなってる気がするニョ。名前:グリ 名前:ブラン
  • 3. 確かにそうだけど、実行速度を求められるゲームプログラムはまだまだ C++ で書くことも多いニャ。
  • 4. 時代がソーシャル要素を盛り込んだネットワークゲームに移行しても、クライアントサイドはもちろんのこと、サーバーサイドも C/C++ が最も使われてるんだニャ。
  • 5. へー、それはなんでかニョ?
  • 6. もちろん C++ じゃなくて GC が使える Java やC#、動的言語の Ruby や Python で書いたほうが開発効率は格段に上がるニャ。
  • 7. でも C++ と Java/C# だったらスループット性能で10倍(※) 、C++ と Ruby/Python だったら1000倍も違うと言われているんだニャ。※JITコンパイラを備えるJavaVMでは、Cよりも速くなることがあるようです。ただし、ネットワークに対する入出力が多いゲームでは、システムコールを呼び出す前後でのバッファオーバーフローのチェック等が毎回生じるため、Cとの速度差が生まれるとのこと。
  • 8. おー、結構違うんだニョ。
  • 9. それだけ違ったら、用意するサーバーの台数も変わってくるニャ。サーバーの運用コストって高いから…。
  • 10. 確かに、できるだけお金はかけたくないニョ。
  • 11. そうだニャ。だから C++ について見てみるニャ。
  • 12. • 簡単に C++ の歴史と機能紹介• Standard Template Library (STL)• 【C++】サンプル【書いてみた】
  • 13. それじゃあ、さくっと C++ の歴史と機能を紹介するニャ。
  • 14. 簡単に C++ の歴史と機能紹介As for C++C++は、汎用プログラミング言語の一つである。C言語の拡張として開発された。拡張はクラスの追加に始まり、仮想関数、多重定義、多重継承、テンプレート、例外処理といった機能が続いていった。静的な型システムを持ち、手続き型プログラミング・データ抽象・オブジェクト指向プログラミング・ジェネリックプログラミングといった複数のプログラミングパラダイムをサポートするマルチパラダイムプログラミング言語である。詳しくは wiki を見てね!http://ja.wikipedia.org/wiki/C%2B%2B
  • 15. 簡単に C++ の歴史と機能紹介C++98• 初めての標準化• STL に代表される標準ライブラリの追加C++03• C++ 98での不具合の修正等C++11• C++98以来初の大きな改訂• マルチスレッドやジェネリックプログラミングを強化詳しくは wiki を見てね!http://ja.wikipedia.org/wiki/C%2B%2B
  • 16. 簡単に C++ の歴史と機能紹介クラス - Classclass Hoge {public: // どこからでもアクセス可能Hoge(void) : _member(0) {} // コンストラクタvirtual ~Hoge(void) {} // デストラクタprotected: // 継承した子クラスからのみアクセス可能void Function(void);private: //クラス内からのみアクセス可能int _member;};オブジェクトの設計図。クラスから生成したオブジェクトをインスタンスという。クラスにはインスタンス生成時に呼ばれるコンストラクタと、インスタンスが破棄される時に呼ばれるデストラクタを定義できる。また、クラス内の関数(メンバ関数)や変数(メンバ変数) のアクセスレベルも指定できる。Hoge* hoge = new Hoge();
  • 17. 簡単に C++ の歴史と機能紹介クラス - Classclass Parent {public:Parent(void) : _member(0) {}virtual ~Parent(void) {}protected:int _member;};class Child: public Parent {public:Child(void) {}virtual ~Child(void) {}void OutputMember(void) { std::cout << _member << std::endl; }};クラスを継承することで、親クラスの機能を持ち合わせたまま、子クラスで拡張することもできる。
  • 18. 簡単に C++ の歴史と機能紹介オーバーライド - Overrideclass Parent {public:void Function(void) { std::cout << "parent" << std::endl; }};class Child : public Parent {public:void Function(void) { std::cout << "child" << std::endl; }};子クラスが親クラスのメンバ関数を上書きできる。Parent* parent = new Child();Child* child = new Child();parent->Function(); // parentchild->Function(); // child
  • 19. 簡単に C++ の歴史と機能紹介仮想関数 - Virtual Functionclass Parent {public:virtual void Function(void) { std::cout << "parent" << std::endl; }};class Child : public Parent {public:void Function(void) { std::cout << "child" << std::endl; }};メンバ関数をオーバーライドした子クラスを、親クラスの型へアップキャストしながら格納した時に、メンバ関数の動作を上書きすることができる。Parent* parent = new Child();Child* child = new Child();parent->Function(); // childchild->Function(); // child
  • 20. 簡単に C++ の歴史と機能紹介Tips: デストラクタの落とし穴class Parent {public:Parent() { std::cout << "Parent Constructor" << std::endl; }~Parent() { std::cout << "Parent Destructor" << std::endl; }};class Child : public Parent {public:Child() { std::cout << "Child Constructor" << std::endl; }~Child() { std::cout << "Child Destructor" << std::endl; }};継承される可能性のあるクラスのデストラクタは仮想関数にすること。仮想関数にしないと、ポリモーフィズムを活用した際に想定外の挙動となることがある。以下サンプル。
  • 21. 簡単に C++ の歴史と機能紹介Tips: デストラクタの落とし穴Child child;/*▼出力(特に問題なし)Parent ConstructorChild ConstructorChild DestructorParent Destructor*/Parent* pParent = new Child(); // 親の型にアップキャストdelete(pParent);/*▼出力(宣言された型が優先されるため、子のデストラクタが呼ばれない!)Parent ConstructorChild ConstructorParent Destructor*/
  • 22. 簡単に C++ の歴史と機能紹介純粋仮想関数 - Pure Virtual Functionclass Parent {public:virtual void Function(void) = 0;};class Child : public Parent {public:void Function(void) { std::cout << "child" << std::endl; }};実装が無い、宣言のみの仮想関数。継承して実装をしなければインスタンス化できない。//Parent* pParent= new Parent(); コンパイルエラー!Parent* pParent= new Child();pParent->Function(); // child
  • 23. 簡単に C++ の歴史と機能紹介多重定義 - Overloadclass Hoge {public:void Function(int n) { std::cout << n << std::endl; }void Function(float f) { std::cout << f << std::endl; }};引数を変えた、同名の関数や演算子を複数定義できる。Hoge hoge;hoge.Function(1);hoge.Function(5.8f);
  • 24. 簡単に C++ の歴史と機能紹介テンプレート - Templatetemplate <typename T>class Hoge {public:void Function(T value) {std::cout << value << std::endl;}};コンパイル時にコードを生成できる。ジェネリックプログラミング(データ形式に依存しないプログラミング形式) に用いられる。コンパイル時に生成されるため、使用を誤るとコード量自体は多くないのに生成された実行ファイルのサイズが肥大化してしまうという危険性もあるため注意が必要。Hoge<int> hoge1;hoge1.Function(1);Hoge<float> hoge2;hoge2.Function(0.3f);
  • 25. 簡単に C++ の歴史と機能紹介テンプレートメタプログラミング- Template Metaprogrammingtemplate <int N>struct Factorial{enum { value = N * Factorial<N - 1>:: value};};template <>struct Factorial<0> {enum { value = 1 };};テンプレートを応用して再帰的にコードを生成することにより、動的な計算量を削減することができる。// これらは実行時に計算することがないため、処理コストが少ない。int x = Factorial<4>::value; // 24int y = Factorial<0>::value; // 1
  • 26. 代表的なものはこれくらいかニャ。
  • 27. あとは多重継承もあるけど、使ったら往々にしてコードが複雑化してしまうから、割愛するニャ。できるだけ単一継承と包含で対応するニャ。※多重継承を使うことでコードが複雑化するというのは、あくまでも私見です。使い所を誤らなければ問題ありません。
  • 28. C++ 自体が機能的に多いという事はなくて、これらの機能を応用することで様々な案件に対応する言語なんだニャ。テンプレートとかは奥が深いニャ。
  • 29. 聞いてるのかニャ?聞いてますニョ~
  • 30. じゃあ次は C++98 で標準化された、Standard Template Library (STL)について解説するニャ。
  • 31. Standard Template Library (STL)As for STLテンプレートを最大限に生かす構成を取っており、コンテナ・イテレータ(反復子)・アルゴリズム・関数オブジェクトと呼ばれる各要素から成っている。C++におけるジェネリックプログラミングのはしりとなった。オブジェクトを格納するコンテナとそれを操作するアルゴリズム、その接点としてイテレータが存在し、コンテナとアルゴリズムが互いに完全に独立しているのも特徴の一つである。詳しくは wiki を見てね!http://ja.wikipedia.org/wiki/Standard_Template_Library
  • 32. Standard Template Library (STL)コンテナ:vector連続要素を保持する動的配列。STLの中では一番シンプルで有用な場面も多い。リファレンスは http://www.cppll.jp/cppreference/cppvector.html を参照。特徴的なのは、格納された要素がメモリ空間において隣接しているということ。つまり、 &v[n] == &v[0] + n ということになる。そのため、末尾への要素追加における計算量は定数時間 O(1) となる。ちなみに中間への要素追加における計算量は線形時間 O(n) である。vector はメモリの再割り当てを、それが必要になったタイミングで行う。(末尾への要素追加時に領域が確保できていなかったら、一定の領域を確保するなど)再割り当てを行う場合は一定のコストがかかるが、reserve を使用することにより、再割り当ての機会を減らすことも可能。API 呼び出しによるオーバーヘッドはあるが、malloc や new[] よりもメモリリークの回避や配列アクセスにイテレータを使用できたりと利点が多いため、vector は多くの場面で活用できる。
  • 33. Standard Template Library (STL)コンテナ:deque (Double Ended Queue)双方向キュー。簡単に言うと、vector を末尾への追加だけではなく、双方向に追加できるようにしたもの。リファレンスは http://www.cppll.jp/cppreference/cppdeque.html を参照。vector と同じく、先頭や末尾への要素追加は O(1) 、中間への要素追加は O(n)。内部的にはリングバッファというデータ構造になっており、確保した領域の先頭アドレスに要素がある状態で push_front した場合は、確保した領域の末尾に追加されるというイメージ。先頭要素の位置は保持しているため、push_front したからといって全ての要素がメモリ空間上で再配置されることはない。先頭への要素追加/削除が行える点で vector よりも使い勝手が良いが 、reserve ができない、要素がメモリ空間上で連続しているという保証はないという欠点もあるため、どちらを採用するかは処理によって検討する必要がある。
  • 34. Standard Template Library (STL)コンテナ:list双方向連結リスト。リファレンスは http://www.cppll.jp/cppreference/cpplist.html を参照。vector や deque と違い、任意の位置への要素追加にかかる計算量が定数時間 O(1) となる。しかし、連結リストでデータを保持するため、ランダムアクセスは遅い。(先頭、末尾からの探索となる)また、必要な分しかメモリを確保しないという特徴もある。メモリキャッシュに乗らないので、deque を使ったほうがパフォーマンス的に上がる事が多いかもしれない。
  • 35. Standard Template Library (STL)コンテナ:set要素の重複を許さない集合。リファレンスは http://www.cppll.jp/cppreference/cppset.html を参照。要素の追加、検索、削除の計算量は対数時間 O(log n) となる。vector と比べて重複を許さないというアルゴリズム的な差異があって使い勝手が良いが、一般的に Red-Black ツリー(http://ja.wikipedia.org/wiki/%E8%B5%A4%E9%BB%92%E6%9C%A8)によって実装されており、それによりツリーを構築するために相当なオーバーヘッドがかかってしまう。そのため、メモリキャッシュに乗ることも念頭に入れて、vector で重複チェックを行った上で要素を追加する方がパフォーマンス的には優れているとも言える。
  • 36. Standard Template Library (STL)コンテナ:map連想コンテナ。キーと値を関連付けて管理する。リファレンスは http://www.cppll.jp/cppreference/cppmap.html を参照。set と同じで Red-Black ツリーなので、要素の追加、検索、削除の計算量は対数時間 O(log n) となる。使用する際のデメリットも set と同様。しかし、キーを指定の型に設定できることはメリットとして大きいため、使用したい場面も多い。
  • 37. Standard Template Library (STL)コンテナアダプタ:stack末尾からの挿入と末尾からの取り出しをサポート。Last In First Out (LIFO) とも呼ばれる。リファレンスは http://www.cppll.jp/cppreference/cppstack.html を参照。コンテナアダプタ:queue末尾からの挿入と先頭からの取り出しをサポート。First In First Out (FIFO) とも呼ばれる。リファレンスは http://www.cppll.jp/cppreference/cppqueue.html を参照。※コンテナアダプタとはコンテナを内部に保持して、限定的に機能を公開しているもの。単体では使いにくいが、protected 継承することにより、内部のコンテナにアクセスできる。それにより独自の拡張が可能となっている。
  • 38. Standard Template Library (STL)イテレータ - Iteratorイテレータはポインタと同じような使い方でコンテナの各要素にアクセスするために用意される。イテレータの中にもいくつか種類があるが、代表的なものは以下のものである。▼前方イテレータ - forward_iterator要素の読み書きが行える。ただし前方への移動しかできない。▼双方向イテレータ - bidirectional_iterator双方向へ移動しながら要素の読み書きが行える。list や set、map はこれ。▼ランダムアクセスイテレータ - random_iteratorランダムアクセスが可能な双方向イテレータ。vector や deque はこれ。
  • 39. Standard Template Library (STL)アルゴリズム - Algorithmコンテナに対する汎用的な処理を集めたもの。非常に多くの関数が用意されているため、STLを使用する際の恩恵として大きい。カテゴリとして、四種類に分類される。・検索・操作 (http://www.cppll.jp/cppreference/cpp_algo_find.html)・配列操作 (http://www.cppll.jp/cppreference/cpp_algo_manip.html)・ソート関連 (http://www.cppll.jp/cppreference/cpp_algo_sort.html)・その他 (http://www.cppll.jp/cppreference/cpp_algo_other.html)
  • 40. Standard Template Library (STL)関数オブジェクト - Function Object関数呼び出し演算子をオーバーロードすることにより、オブジェクトでありながら関数のように呼び出せる。関数ポインタやポリモーフィズムの代わりになり得る。アルゴリズムの中には関数オブジェクトを引数で与えるものもあり、それによって更にアルゴリズムの汎用性を向上させている。また、C++11 から標準化されたラムダ式を使うことで、より簡潔に書ける。
  • 41. STL はとても便利だけど、使い所を誤ると実行速度の低下やリソースの無駄遣いに繋がるから気を付けるニャ。
  • 42. また、コンテナに関する箇所は私見で書いているところもあるから、色々な人の意見を参考にしたり、自分でも考えて使うようにしてほしいニャ。
  • 43. 便利なものこそ、よく考えて使えってことだニョ。
  • 44. その通りだニャ。最後にいくつかサンプルを載せるニャ。
  • 45. 【C++】サンプル【書いてみた】公開するインターフェースクラスと実装部分を切り離す// インターフェースクラス。公開する。class IModel {public:IModel(void) {}virtual ~IModel(void) {}virtual void SetPosition(const Vector3& position) = 0;virtual const Vector3& GetPosition(void) const= 0;};
  • 46. 【C++】サンプル【書いてみた】公開するインターフェースクラスと実装部分を切り離す// 実装部分。公開しない。class Model : public IModel {public:Model(void) : _position() {}virtual ~Model(void) {}void Update();void Draw();void SetPosition(const Vector3& position) { this->_position = position; }const Vector3& GetPosition(void) const { return this->_position; }private:Vector3 _position ;};
  • 47. 【C++】サンプル【書いてみた】公開するインターフェースクラスと実装部分を切り離す// モデル作成クラスclass ModelCreator {public:static IModel* Create(void); // new Model();};// ポリモーフィズムIModel * pModel = ModelCreator::Create();
  • 48. 【C++】サンプル【書いてみた】typedef std::vector<int> IntList;IntList list;list.push_back(1000);for (int i = 0; i < 10 ; ++i) {list.push_back(std::rand());}std::sort(list.begin(), list.end());IntList::iterator it = std::find(list.begin(), list.end(), 1000);list.erase(it, list.end());リストにランダムな整数値を入力し、閾値を基準に削除する
  • 49. 【C++】サンプル【書いてみた】ポリシークラス - Policy Classstruct NoChecking {template <typename T>static bool CheckPointer(T*) { return true; }};struct NullChecking {template <typename T>static bool CheckPointer(T* p) { return (p != nullptr); }};struct BadPointerDoNothing {template <typename T>static T* HandleBadPointer(T* p) { std::cout << "pointer is moldy" << std::endl; returnp; }};構文的に同じインタフェースの下で、異なる処理を提供する。ポリシーを使うクラスは、それが使うそれぞれのポリシーに対してひとつのテンプレート引数を持って、テンプレート化されている。 このため必要なポリシーを選択することが出来る。
  • 50. 【C++】サンプル【書いてみた】ポリシークラス - Policy Classtemplate <typename T,typename CheckingPolicy = NoChecking,typename BadPointerPolicy = BadPointerDoNothing>class PointerWrapper {public:PointerWrapper() : _value(nullptr) {}explicit PointerWrapper(T* p) : _value(p) {} // 暗黙的呼び出しを禁止operator T*() {if (!CheckingPolicy::CheckPointer(_value)) {return BadPointerPolicy::HandleBadPointer(_value);} else {return _value;}}private:T* _value ;};
  • 51. 【C++】サンプル【書いてみた】ポリシークラス - Policy ClassPointerWrapper<int> num1(new int);*num1 = 42;std::cout << "num1:" << *num1 << std::endl;// これは実行時にNULLアクセスによるエラーになる。//PointerWrapper<int> num2;//*num2 = 42;//std::cout << "num2:" << *num2 << std::endl;// これもエラーになるが、NULLアクセス前に pointer is moldy と出力する。//PointerWrapper<int, NullChecking> num3;//*num3 = 42;//std::cout << "num3:" << *num3 << std::endl;
  • 52. おまけとして、機能紹介って意味でC++11 のサンプルコードを載せるニャ。詳細な解説は、また別の機会にするニャ。
  • 53. 【C++】サンプル【書いてみた】型推論// イテレータの型は複雑std::vector<int>::iterator it = vector.begin();↓// このような形で型判別をコンパイラ任せることで、可読性を上げ、冗長性を省くauto it = vector.begin();// また、decltypeもひとつの選択肢として用意されている。int foo;decltype(foo) bar = 1; // fooと同じ型でコンパイル時に決定される
  • 54. 【C++】サンプル【書いてみた】NULLポインタvoid foo(char *);void foo(int);foo(NULL); // foo(int) が呼ばれる!定数 0 には整数定数と NULL ポインタという二つの側面がある。これまで NULL はプリプロセッサマクロで 0 となるように定義されてきた。しかし 0 はあらゆるポインタ型への変換が許されているため、オーバーロードの使い方によっては意図した動作をしないことがある。そのため、新たな予約語として nullptr が用意された。nullptr は整数型との比較や代入は禁止されているが、NULL と同様にあらゆるポインタ型への変換が許されている。今後、NULL が非推奨となる可能性もあるので、nullptr を使うように心掛けたい。
  • 55. 【C++】サンプル【書いてみた】ラムダ式// 関数オブジェクトと同じように使えるvoid* ptr = NULL;auto isNullptr = [](void* ptr) { return ptr == nullptr; };if (isNullptr(ptr)) {std::cout << "ptr is null." << std::endl;}// その場で呼び出すこともできるint square2 = [](int x) { return x * x; } (2);std::cout << square2 << std::endl; // 4// Hello,Worldをこんな書き方することも可能。特にこうする意味は無いけど。[]() { std::cout << "Hello,World" << std::endl; } ();
  • 56. 【C++】サンプル【書いてみた】ラムダ式を使ってクロージャint sum = 0;std::vector<int> list;list.push_back(1);list.push_back(2);list.push_back(3);std::for_each(list.begin(), list.end(), [&sum](int x) {sum += x; // このラムダ式が宣言されているスコープの変数にアクセス可能});std::cout << sum<< std::endl; // 6
  • 57. お疲れ様でした。それではまた次の機会に。