5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

STL:vectorの要素アクセス方法 at と operator[]

Last updated at Posted at 2020-08-30

STL のvectorを使っていて頭に入れておくべきことがあったので、ここにメモしておきます。
std::vectorの n 番目の要素の参照を返す(要素へアクセスする)方法は大きく2つあります。
さあ、あなたはどっち?
iteratorを使ったアクセスについてはまた改めて!

#vector::at
境界チェックをする(vectorの管理範囲外にアクセスしていないか)
  範囲外アクセスをした時、std::out_of_rangeの例外を送出する。
  範囲外アクセスチェックのため、若干の OH(オーバーヘッド)が生じる。

例.
// vectorにある要素の値を変数に代入したり
variable = vector.at(index);

// ある値をvectorにある要素に代入したり
vector.at(index) = value;

#vector::operator[]
境界チェックをしない
  範囲外アクセスをした時、 未定義動作を行う。
  範囲外アクセスチェックをしないため、OH が生じない。

例.
// vectorにある要素の値を変数に代入したり
variable = vector[index];

// ある値をvectorにある要素に代入したり
vector[index] = value;

#サンプルコード
「え?どゆこと?」
そう思ったあなた、是非ご自身の環境で以下のコードを試してみて下さい。
あ、for 文のindexに注意して下さいね。

sample.cpp
#include <iostream>
#include <vector>
#include <stdexcept>

int main()
{
    std::vector<int> sampleVector;

    // sampleVectorのindex0~9に要素を詰める. 
    for (int i = 0; i < 10; i++)
    {
        sampleVector.push_back(i);
    }

    std::cout << "###std::vector::at" << std::endl;

    // at
    try
    {
        // sampleVectorの管理範囲外であるindex10の要素にアクセスする. 
        for (int i = 1; i <= 10; i++)
        {
            std::cout << sampleVector.at(i) << std::endl;
        }

        // atの境界チェックで例外を投げるため、この処理は実行されない. 
        std::cout << "Complete..." << std::endl;
    }
    catch (std::out_of_range& sampleException)
    {
        std::cout << "out of range!" << std::endl;
        std::cout << "Error : " << sampleException.what() << std::endl;
    }

    std::cout << std::endl << "###std::vector::operator[]" << std::endl;

    // operator[]
    try
    {
        // sampleVectorの管理範囲外であるindex10の要素にアクセスする. 
        for (int i = 1; i <= 10; i++)
        {
            std::cout << sampleVector[i] << std::endl;
        }

        // 境界チェック無しのため、そのまま不定値が出力され、実行完了となる. 
        std::cout << "Complete..." << std::endl;
    }
    // 境界チェック無しのため、例外は投げない. 
    catch (std::out_of_range& sampleException)
    {
        std::cout << "out of range!" << std::endl;
        std::cout << "Error : " << sampleException.what() << std::endl;
    }

    std::cout << "End..." << std::endl;

    return 0;
}
出力.
###std::vector::at
1
2
3
4
5
6
7
8
9
out of range!
Error : invalid vector subscript

###std::vector::operator[]
1
2
3
4
5
6
7
8
9
2011707104
Complete...
End...

おやおや、全然違う結果になっていますねぇ
へっへっへ・・・
std::vector::operator[]の方は、なんだかとんでもない値を出力していますねぇ
へっへっへ・・・

#まとめ
上記2つの違いは「範囲外アクセスをした時に、未定義動作を行うか例外を投げるか」です。
防御的プログラミングということであれば、未定義動作を行わないことが保証されている方をできるだけ使うべきだと思います。

atは範囲外アクセスに対してstd::out_of_range例外を投げるため、適切に処理されなかった場合、std::terminateが呼ばれてプログラムが異常終了します。
例外処理を用意していなかったとしても、プログラムが終了するだけです。
デバッグも比較的容易でしょう。

未定義動作を原因とするバグは、実際に未定義動作を行った箇所と異なる箇所で致命的なエラーを発生させることも多くあります。
エラー発生箇所と原因箇所が異なる場合、デバッグは困難になるでしょう。

個人的にはまずatを使うことをオススメしたいかなぁ。

at派 vs operator[]派の議論を見たいので、よかったらコメント下さい!

5
1
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?