Y's note

Web技術・プロダクトマネジメント・そして経営について

本ブログの更新を停止しており、今後は下記Noteに記載していきます。
https://note.com/yutakikuchi/

C++最速マスター その4

C++プログラミング入門

C++プログラミング入門

Index

  • inline
  • iterator
  • pair
  • cast
    • static_cast
    • dynamic_cast
    • const_cast
    • reinterpret_cast
  • Class
    • Inner Class
    • Local Class
    • Annonymous Class
  • etc
    • size_t

inline

inlineは関数に付ける修飾子です。inline void echoError() {}のように設定すると関数をインライン展開します。インライン展開は呼び出しもとにコードそのものを埋め込んでしまうようなイメージです。これにより関数呼び出し処理の時間削減を行います。inlineはサイズの大きい関数またアドレス取得するような関数には適用ができません。

#include <iostream>
using namespace std;

// inline関数
inline int max( int x, int y ) { 
    return ( x > y ) ? x : y;  
}

int main() {
    int a = 10; 
    int b = 11; 
    cout << "-- inline func ---" << endl;
    // inline関数呼び出し
    cout << max( a, b ) << endl;
    return 0;
}

iterator

basic_iterator

iteratorはpointerと同じような構造であり、vectorやmapなどのコンテナの中身を反復参照する時に使います。pointerと互換性を持っているので、pointerをiteratorとして使う事ができます。iteratorには幾つか種類があるので以下に簡単に整理します。またvectorとしてのiterator利用についてサンプルコードを載せます。

iterator 説明
InputIterator 読み込み専用アクセス。前方へのアクセス(++演算子はあるが、--演算子は無い)。istreamで利用
OutputIterator 書き込み専用アクセス。前方へのアクセス(++演算子はあるが、--演算子は無い)。ostreamで利用
ForwardIterator 読み書き可能アクセス。Input,OuputIteratorを組み合わせたもの
BidirectionalIterator 読み書き可能アクセス。前方アクセスだけでなく後方も可能。(++、--演算子ともにある) 。list/set/mapで利用
RandomAccessIterator 読み書き可能、ランダムアクセス可能。++、--演算だけでなくランダムで指定もできる。array/vector/dequeで利用
#include<iostream>
#include<vector>
using namespace std;

int main() {
    
    // int型のvectorを定義
    vector<int> vect;
    
    //末尾に要素追加
    vect.push_back( 1 );
    vect.push_back( 100 );
    vect.push_back( 2 );

    // iterator
    for( vector<int>::iterator itr = vect.begin(); itr != vect.end(); itr ++ ) {
        cout << *itr << endl;
    }
    return 0;
}

実行結果

1
100
2
100
reverse_iterator

reverse_iteartor、rbegin()、rend()を使う事によって双方向iteratorの動作を逆転させることが可能です。通常のiteratorは最初のノードから参照するのが基本ですが、reverse_iteratorを使って反転させます。

#include<iostream>
#include<vector>
using namespace std;

int main() {
    
    // int型のvectorを定義
    vector<int> vect;
    
    //末尾に要素追加
    vect.push_back( 1 );
    vect.push_back( 100 );
    vect.push_back( 2 ); 

    // reverse iterator
    for( vector<int>::reverse_iterator itr = vect.rbegin(); itr != vect.rend(); ++itr ) { 
        cout << *itr << endl;
    }   

    // randomaccess
    cout << vect[1] << endl;
    return 0;
}

実行結果

2
100
1
100
const_iterator

通常のiteratorconstで宣言をしたiteratorconst_iteratorを使ったそれぞれの場合で可能な操作が異なります。以下の表に用途をまとめます。const_iteratorを使うと値の書き換えが不可能で、++演算による参照は可能なiteratorになります。読み込み専用だけであればconst_iteartorを使うと良いと思います。constで宣言した場合はポインタを使った値書き換えが可能ですが、前方/後方参照などが出来なくなります。const_iteartorを使う場合はその逆となり、値の書き換えが出来ず前方/後方参照ができるようになります。

type 値書き込み 前方/後方参照(++、--演算)
iterator
const ×
const_iteartor ×
#include<iostream>
#include<vector>
using namespace std;

int main() {
    
    // int型のvectorを定義
    vector<int> v;
    v.push_back( 1 );
    v.push_back( 100 );
    v.push_back( 2 );

    // 通常のiterator
    vector<int>::iterator itr0 = v.begin();
    *itr0 = 0;
    cout << "basic iterator" << endl;
    cout << *itr0 << endl;
    itr0++;
    cout << *itr0 << endl;

    // constで宣言したiterator
    const vector<int>::iterator itr1 = v.begin();
    *itr1 = 1;
    // 以下はエラーとなる
    //itr1++;
    cout << "const" << endl;
    cout << *itr1 << endl;

    // const_iteratorを使った場合
    vector<int>::const_iterator itr2 = v.begin();
    // 以下はエラーとなる
    //*itr2 = 2;
    itr2++;
    cout << "const_iterator" << endl;
    cout << *itr2 << endl;

    return 0;
}

実行結果

basic iterator
0
100
const
1
const_iterator
100

pair

pairは2つの値の組み合わせで管理します。vectorとpairを組み合わせて使うとmapのような使い方ができます。第一の値を参照にするにはfirst、第二の値はsecondを指定します。

make_pair

通常pairを利用する時にはpairのようにテンプレートの型を指定する必要がありますが、make_pairを使うとmake_pair( 'a', 'b' )のように型の記述を省略して直接値を入れることが出来ます。以下にサンプルコードと実行結果を載せます。

#include <iostream>
#include <sstream>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;

string itos( int &i ) {
    ostringstream s;
    s << i;
    return s.str();
}

int main() {
    typedef pair<int,int> int_pair;
    typedef pair<string,string> string_pair;
    vector<int_pair> v;
    vector<string_pair> sv;
    
    cout << "int vector" << endl;

    v.push_back( pair<int,int>(4,1) );
    v.push_back( make_pair( 3, 1 ) );
    v.push_back( make_pair( 2, 1 ) );
    v.push_back( make_pair( 1, 2 ) );


    // iteratorで抽出
    for( vector<int_pair>::iterator itr = v.begin(); itr!= v.end(); itr++ ) {
        cout << "Key:" + itos( (*itr).first )  + " Value:" + itos( (*itr).second ) << endl;
    }

    // sort
    sort( v.begin(), v.end() );
    cout << "after sort" << endl;
    for( vector<int_pair>::iterator itr2 = v.begin(); itr2!= v.end(); itr2++ ) {
        cout << "Key:" + itos( (*itr2).first )  + " Value:" + itos( (*itr2).second ) << endl;
    }

    cout << "string vector" << endl;

    sv.push_back( pair<string,string>( "d", "a" ) );
    sv.push_back( make_pair( "c", "d" ) );
    sv.push_back( make_pair( "b", "a" ) );
    sv.push_back( make_pair( "a", "b" ) );

    // iteratorで抽出
    for( vector<string_pair>::iterator sitr = sv.begin(); sitr!= sv.end(); sitr++ ) {
        cout << "Key:" + (*sitr).first + " Value:" + (*sitr).second << endl;
    }

    // sort
    sort( sv.begin(), sv.end() );
    cout << "after sort" << endl;
    for( vector<string_pair>::iterator sitr2 = sv.begin(); sitr2!= sv.end(); sitr2++ ) {
        cout << "Key:" + (*sitr2).first + " Value:" + (*sitr2).second << endl;
    }
    return 0;
}

実行結果

int vector
Key:4 Value:1
Key:3 Value:1
Key:2 Value:1
Key:1 Value:2
after sort
Key:1 Value:2
Key:2 Value:1
Key:3 Value:1
Key:4 Value:1
string vector
Key:d Value:a
Key:c Value:d
Key:b Value:a
Key:a Value:b
after sort
Key:a Value:b
Key:b Value:a
Key:c Value:d
Key:d Value:a

cast

型変換 - Wikipedia はてなブックマーク - 型変換 - Wikipedia
C++には独特なcastが定義されています。intからfloatへの変換やpinterを使ったものまで様々です。C++ではcastは使うべきでは無いと言われているようですが、一応紹介だけはします。

cast 説明
static_cast ビット変換キャスト、アップキャスト、型のテストをしないダウンキャスト
dynamic_cast 安全なダウンキャスト
const_cast constやvolatileを解除。mutable キーワードが使えない場合に使用
reinterpret_cast 全く型が異なる変換を行うキャスト
static_cast

以下はintからdoubleに変換、constを外すためのcastです。

#include <iostream>
#include <string>

using namespace std;
int main() {
    int i = 1;
    double d;
    char str;

    d = static_cast<double>( i );
    cout << d << endl;

    // 以下は変換できない
    // str = static_cast<char>( i );
    // cout << str << endl;
    return 0;
}
#include <iostream>
#include <string>

using namespace std;
int main() {
    const int i = 1;
    int d;
    // error
    //i = 3;
    // constを解除するcast
    d = static_cast<int>( i );
    d = 3;
    cout << d << endl;
    return 0;
}
dynamic_cast

継承している子クラスへのcast。

#include <iostream>
#include <string>
using namespace std;

class Parent {
    public :
        virtual string echoString() {}
};

class Child : public Parent {
    public :
       string echoString() {
            cout << "echo String" << endl;
       }
};

int main() {
    Child child;
    Parent *parent = &child;
    Child *c = dynamic_cast<Child*>( parent );
    if( c == NULL ) {
        cout << "failed down cast" << endl;
        return 1;
    }
    return 0;
}
const_cast

pointer関連のconstを解除します。

#include <iostream>
#include <string>

using namespace std;
int main() {
    int i = 2;
    const int *p = &i;
    int *cp = const_cast<int*>( p );
   
    cout << cp << endl;
    cout << *cp << endl;

    return 0;
}
reinterpret_cast

異なる型のPointer変換などに利用します。

#include <iostream>
#include <string>
using namespace std;

int main() {
    
    int i = 10;
    int *ip = &i;
    double *dp;

    dp = reinterpret_cast<double*>( ip );
    cout << dp << endl;

    return 0;
}

Class

Javaを最速でマスターするための予備知識7項目 - Yuta.Kikuchiの日記 はてなブックマーク - Javaを最速でマスターするための予備知識7項目 - Yuta.Kikuchiの日記
JavaにはNestedClass、InnerClass、AnnonymouseClass、LocalClassなどの種類が存在しましたがC++でも同じようにいくつか種類が用意されています。

InnerClass

Classの内部に更にClassを定義する入れ子階層を実現できます。

#include <iostream>
#include <string>
using namespace std;

// OuterClass
class OuterClass {
    public : 
        // InnerClass
        class InnerClass {
            public :
                string echoInnerMethod() {
                    cout << "echo Inner Method" << endl;    
                }
                InnerClass() {
                    cout << "constructor InnerClass" << endl;
                }

                ~InnerClass() {
                    cout << "destructor InnerClass" << endl;
                }
        };
    
        OuterClass() {
            cout << "constructor OuterClass" << endl;
        }

        ~OuterClass() {
            cout << "destructor OuterClass" << endl;
        }
};

int main() {
    OuterClass::InnerClass ic;
    ic.echoInnerMethod();
}

実行結果

constructor InnerClass
echo Inner Method
destructor InnerClass
Local Class

関数の中に定義するClassです。他の関数からは呼び出しができません。呼び出そうとするとコンパイルエラーになります。error: ‘LocalClass’ was not declared in this scope

#include<iostream>
#include<string>

using namespace std;

void localfunc() {
    class LocalClass {
        public :
            void echoString() {
                cout << "echo String" << endl;
            }
    };
    LocalClass lc;
    lc.echoString();
}

int main() {
    localfunc();
    //error
    //LocalClass lc;
    //lc.echoString();
    return 0;
}

実行結果

echo String
Annonymous Class

AnnonymousClassにはメンバ変数は定義可能ですが、メンバ関数は定義できません。定義を行うとコンパイルerror: an anonymous union cannot have function membersというエラーが出ます。 Annonymous Classのメンバ変数を呼び出す場合はOuterClassを経由します。

#include <iostream>
#include <string>
using namespace std;

// OuterClass
class OuterClass {
    public : 
        // AnnonymouseClass
        class {
            public :
                int num;
                /* 関数は定義できない
                string echoInnerMethod() {
                    cout << "echo Inner Method" << endl;    
                }
                */
        };

        void setNumber( int n ) {
            num = n;
        }

        int getNumber() {
            return num;
        }

        OuterClass() {
            cout << "constructor OuterClass" << endl;
        }

        ~OuterClass() {
            cout << "destructor OuterClass" << endl;
        }
};

int main() {
    OuterClass oc;
    oc.setNumber( 10 );
    cout << oc.getNumber() << endl;
}

実行結果

constructor OuterClass
10
destructor OuterClass

etc

size_t

sizeof演算子で得られるデータ型をsize_tで表現します。実際にはunsigned longと同じようなデータ型のようです。

#include <iostream>
using namespace std;

int main() {
    int i = 10;
    size_t s = sizeof( i );
    cout << s << endl;
    return 0;
}