C++のstd::vectorについて

std::vectorについて勉強しなおした.

STLコンテナの一種.実行時に動的にサイズを変更できる.vectorを使用するためには, #include <vector> を記述する必要がある.

vectorの生成

vectorには複数のコンストラクタが存在する.

const int data[] = {1, 2, 3, 4, 5};

std::vector<int> empty_vector;                  // 空のvector
std::vector<int> int_vector(10);                // 要素数10のvector
std::vector<double> double_vector(10, 3.2);     // 要素数10,各要素は3.2で初期化されたvector
std::vector<double> copy_vector(double_vector); // double_vectorのコピー
std::vector<int> iter_vector(data, data + 5);   // dataからdata+5の要素をもつvector

vectorのサイズ・容量

vectorでは,実際に確保されている動的配列の要素数と,実際に使用されている動的配列の要素数は異なる.これらの値を調べたい時には,それぞれ capacity() 関数, size() 関数を使用する.これらの関数の戻り値は, std::vector::size_type 型である.

vectorで使用できるサイズの最大値は max_size() 関数で得ることができる.

std::vector<int> v;

std::cout << v.capacity()   // 確保されている要素数
<< v.size()       // 使用されている要素数
<< v.max_size()   // std::vector<int>で使用できる最大要素数
<< std::endl;

vectorの容量を拡張したい時には, reserve() 関数を使用する.

std::vector<int> v;

v.reserve(100);

vectorの操作

要素の追加・代入

vectorの末尾に値を追加するには, push_back() 関数を使用する.要素追加時には,容量の拡張は自動的に行われる.
また,任意の位置に値を挿入するには, insert() 関数を使用する.

std::vector<int> v(5, 1);

v.push_back(3);     // vの末尾に3を追加
v.(v.begin(), 10);  // vの先頭に10を挿入

vectorへの値の代入には, = 演算子が使用できる.さらに, assign() 関数を使用することもできる.

std::vector<int> v(10);

v.assign(10, 1);    // 10個の1を代入
v[5] = 0;           // v[5]に0を代入

const int a[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
v.assign(a, a + 10);    // aからa+10の要素を代入

要素の取得

要素にアクセスするには,通常の配列のように [] を使用することができる.添字アクセスの際には容量の拡張は行われないため,サイズ以上の値を指定した場合は範囲外アクセスになる.

範囲外アクセスに対応したい場合には, at() 関数を使用する.この関数を使用している際に範囲外アクセスが起こると, std::out_of_range 例外が送出される.

std::vector<int> v(10, 3);
try {
cout << v[3] << '\n';   // 3が表示される
v.at(15) = 13;          // std::out_of_range例外の送出
} catch (const std::out_of_range& e) {
std::cerr << e.what() << std::endl;
}

また,先頭の要素は front() 関数,末尾の要素は back() 関数で取得できる.

std::vector<int> v(10);
std::cout << v.front() << '\n';
std::cout << v.back()  << '\n';

要素の削除

要素の削除には, pop_back() 関数,もしくは erase() 関数を使用する.なお,要素を削除しても,その領域は残ったままになる.そのため, new で確保した領域を削除した場合でも,自動的に delete されない.

std::vector<int> v(10, 1);

v.pop_back();                       // 末尾の要素を削除
v.erase(v.begin());                 // 先頭要素を削除
v.erase(v.begin() + 2, v.end());    // 3番めの要素から最後まで削除

要素をすべて削除したい場合は, clear() 関数を使用する.

std::vector<int> v(5, 5);

v.clear();

PythonからMySQLを使う

DjangoでデータベースにMySQLを使用するときはmysqlclientを使用することが推奨されている.Djangoが勝手にデータベースに接続などの処理をしてくれるのでモジュールの使い方は知らなくても使うことは可能だが,せっかくなら使い方もわかるほうが良いので調べてみた.

基本的に,Python標準ライブラリのsqlite3と使い方は同じ.
まずコネクションオブジェクトを作成し,そこからカーソルオブジェクトを作る.できたカーソルオブジェクトを使って様々なクエリを実行する.

インストール

$ pip install mysqlclient

MySQLに接続する

conn = MySQLdb.connect(
    user='username',
    passwd='password',
    host='host',
    db='dbname'
)

返り値はコネクションオブジェクト.userpasswd は名前の通り.MySQLに登録されているユーザー情報を記述する. host はデータベースの置いてある場所を指定する.ローカルのMySQLに接続する場合は localhost を指定する. db には使用するデータベース名を指定する.

カーソルオブジェクトの作成

c = conn.cursor()

MySQLdb.connect で作成したオブジェクトを使ってカーソルオブジェクトを作成する.

クエリの実行

c.execute(query)

query に指定したクエリを実行する.

プレースホルダ

クエリ中に %s を記述すると,プレースホルダとして扱える.ここに値を埋め込む場合は,与えたい値を execute() の第2引数にタプルで渡す.

c.execute('select * from test where id = %s', (2,))

レコードの取得

execute() でselect文を実行した後,レコードを得るためには以下のいずれかを使用する.

  • fetchone() : レコードを1件取得
  • fetchmany(n) : レコードをn件取得
  • fetchall() : レコードをすべて取得

データベースへの変更を保存

conn.commit()

このメソッドを呼び出すことで,変更を保存できる. これを呼び出し忘れると,追加・削除などの変更が破棄される ので注意.

このメソッドはカーソルオブジェクトではなく,コネクションオブジェクトが持っていることにも注意.

サンプルコード

# coding: utf-8

import MySQLdb


def main():
    conn = MySQLdb.connect(
        user='testuser',
        passwd='testuser',
        host='192.168.33.3',
        db='testdb'
    )
    c = conn.cursor()

    # テーブルの作成
    sql = 'create table test (id int, content varchar(32))'
    c.execute(sql)
    print('* testテーブルを作成\n')

    # テーブル一覧の取得
    sql = 'show tables'
    c.execute(sql)
    print('===== テーブル一覧 =====')
    print(c.fetchone())

    # レコードの登録
    sql = 'insert into test values (%s, %s)'
    c.execute(sql, (1, 'hoge'))  # 1件のみ
    datas = [
        (2, 'foo'),
        (3, 'bar')
    ]
    c.executemany(sql, datas)    # 複数件
    print('\n* レコードを3件登録\n')

    # レコードの取得
    sql = 'select * from test'
    c.execute(sql)
    print('===== レコード =====')
    for row in c.fetchall():
        print('Id:', row[0], 'Content:', row[1])
    
    # レコードの削除
    sql = 'delete from test where id=%s'
    c.execute(sql, (2,))
    print('\n* idが2のレコードを削除\n')

    # レコードの取得
    sql = 'select * from test'
    c.execute(sql)
    print('===== レコード =====')
    for row in c.fetchall():
        print('Id:', row[0], 'Content:', row[1])

    # データベースへの変更を保存
    conn.commit()
    
    c.close()
    conn.close()


if __name__ == '__main__':
    main()

実行結果

* testテーブルを作成

===== テーブル一覧 =====
('test',)

* レコードを3件登録

===== レコード =====
Id: 1 Content: hoge
Id: 2 Content: foo
Id: 3 Content: bar

* idが2のレコードを削除

===== レコード =====
Id: 1 Content: hoge
Id: 3 Content: bar