16

私は Java から来て、現在 C++ を学んでいます。私は Stroustrup のプログラミングの原則と C++ の使用方法を使用しています。私は今ベクトルを扱っています。117 ページで、彼は、ベクトルの存在しない要素にアクセスすると実行時エラーが発生すると述べています (Java でも同じで、範囲外のインデックス)。私は MinGW コンパイラを使用しています。このコードをコンパイルして実行すると、次のようになります。

#include <iostream>
#include <cstdio>
#include <vector>

int main() 
{ 
    std::vector<int> v(6);
    v[8] = 10;
    std::cout << v[8];
    return 0;
}

出力 10 として表示されます。さらに興味深いのは、存在しないベクトル要素を変更しない場合 (実行時エラーまたは少なくともデフォルト値を期待して出力するだけです)、いくつかの大きな整数が出力されることです。それで... Stroustrupは間違っていますか、それともGCCにはC++をコンパイルする奇妙な方法がありますか?

4

4 に答える 4

20

本は少し曖昧です。実行時に現れる未定義の動作であるため、「実行時エラー」ではありません。これは、何でも起こり得ることを意味します。しかし、エラーはプログラムの実行ではなく、厳密にはあなたにあります。実際、未定義の動作を伴うプログラムの実行について話すことさえ不可能であり、賢明ではありません。

C++ には、Java とはまったく異なり、プログラミング エラーから保護するものは何もありません。


@sftrabbit が言うように、std::vectorには代替インターフェイス があり.at()、これは常に正しいプログラムを提供し (ただし、例外がスローされる場合があります)、その結果、推論できるプログラムになります。


これは C++ の重要な基本的側面であると私は信じているので、例を挙げて要点を繰り返しましょう。ユーザーから整数を読み取っているとします。

int read_int()
{
    std::cout << "Please enter a number: ";
    int n;
    return (std::cin >> n) ? n : 18;
}

ここで、次の 3 つのプログラムを考えてみましょう。

危険なもの:このプログラムの正しさは、ユーザーの入力に依存します! 必ずしも正しくないわけではありませんが、安全ではありません(私が壊れたと呼ぶところまで)。

int main()
{
    int n = read_int();
    int k = read_int();
    std::vector<int> v(n);
    return v[k];
}

無条件に正しい:ユーザーが何を入力しても、このプログラムがどのように動作するかはわかっています。

int main() try
{
    int n = read_int();
    int k = read_int();
    std::vector<int> v(n);
    return v.at(k);
}
catch (...)
{
    return 0;
}

正気のもの:上記のバージョン.at()は扱いにくいです。チェックしてフィードバックを提供することをお勧めします。動的チェックを実行するため、チェックされていないベクトル アクセスは実際には正常であることが保証されます。

int main()
{
    int n = read_int();

    if (n <= 0) { std::cout << "Bad container size!\n"; return 0; }

    int k = read_int();

    if (k < 0 || k >= n)  { std::cout << "Bad index!\n"; return 0; }

    std::vector<int> v(n);
    return v[k];
}

(ベクトルの構築が独自の例外をスローする可能性は無視しています。)

C++ の多くの操作は安全ではなく、条件付きでしか正しくないという教訓がありますが、プログラマーは事前に必要なチェックを行うことが期待されています。言語が代わりにやってくれるわけではないので、お金はかかりませんが、覚えておく必要があります。いずれにせよエラー状態を処理する必要があるため、ライブラリまたは言語レベルで高価で非特定の操作を強制するのではなく、チェックを統合するのに適した立場にあるプログラマーに責任が委ねられるという考えです。とにかく書く必要があるコードに。

皮肉なことを言いたければ、このアプローチを Python と対比させたいと思います。Python では、ユーザーが作成したエラー処理をまったく行わずに、信じられないほど短く正確なプログラムを作成できます。反対に、プログラマーが意図したものからわずかに逸脱するようなプログラムを使用しようとすると、特定されていない、読みにくい例外とスタック トレースが表示され、何を改善すべきかについてのガイダンスがほとんど得られません。エラー処理を書くことを強制されることはありません。多くの場合、エラー処理は書かれません。(C++ と Java を完全に対比することはできません。Java は一般的に安全ですが、短いJava プログラムはまだ見たことがありません。)</rantmode>

于 2012-12-23T23:51:53.810 に答える
6

C および C++ は常に境界チェックを行うわけではありません。実行時エラーを引き起こす可能性があります。そして、例えば 10000 程度の数字を使いすぎると、ほぼ確実に問題が発生します。

vector.at(10) を使用することもできますが、これは間違いなく例外になります。参照: http://www.cplusplus.com/reference/vector/vector/at/ との比較: http://www.cplusplus.com/reference/vector/vector/operator%5B%5D/

于 2012-12-23T23:53:21.157 に答える
4

私はあまり注意していないので、ベクトルの「operator[]」が「at()」のように境界をチェックすることを望みました。:-)

1 つの方法は、ベクトル クラスを継承し、operator[] をオーバーライドして at() を呼び出すことです。これにより、より読みやすい "[]" を使用でき、すべての "[]" を "at()" に置き換える必要がなくなります。継承されたベクトル (ex:safer_vector) を法線ベクトルとして定義することもできます。コードは次のようになります (C++11 では、Xcode 5 の llvm3.5)。

#include <vector>

using namespace std;

template <class _Tp, class _Allocator = allocator<_Tp> >
class safer_vector:public vector<_Tp, _Allocator>{
private:
    typedef __vector_base<_Tp, _Allocator>           __base;
public:
    typedef _Tp                                      value_type;
    typedef _Allocator                               allocator_type;
    typedef typename __base::reference               reference;
    typedef typename __base::const_reference         const_reference;
    typedef typename __base::size_type               size_type;
public:

    reference operator[](size_type __n){
        return this->at(__n);
    };

    safer_vector(_Tp val):vector<_Tp, _Allocator>(val){;};
    safer_vector(_Tp val, const_reference __x):vector<_Tp, _Allocator>(val,__x){;};
    safer_vector(initializer_list<value_type> __il):vector<_Tp, _Allocator>(__il){;}
    template <class _Iterator>
    safer_vector(_Iterator __first, _Iterator __last):vector<_Tp,_Allocator>(__first, __last){;};
    // If C++11 Constructor inheritence is supported
    // using vector<_Tp, _Allocator>::vector;
};
#define safer_vector vector
于 2013-11-29T14:35:31.383 に答える