我来自Java并且正在学习C ++。我正在使用Stroustrup的Progamming原则和使用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 ++的奇怪方法呢?
答案 0 :(得分:13)
这本书有点模糊。它不是“运行时错误”,而是在运行时显示的未定义行为。这意味着任何事情都可能发生。但错误严格来说是你,而不是程序执行错误,事实上甚至谈论具有未定义行为的程序的执行也是不可能的,也是不明智的。
C ++中没有任何内容可以保护您免受编程错误的影响,与Java完全不同。
正如@sftrabbit所说,std::vector
有一个替代接口.at()
,它总是提供一个正确的程序(尽管它可能会抛出异常),因此可以推理出一个。
让我用一个例子重复这一点,因为我相信这是C ++的一个重要基本方面。假设我们正在读取用户的整数:
int read_int()
{
std::cout << "Please enter a number: ";
int n;
return (std::cin >> n) ? n : 18;
}
现在考虑以下三个程序:
危险的一个:此程序的正确性取决于用户输入!它不一定不正确,但它不安全(我称之为破坏)。
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对比,它允许你编写令人难以置信的短和正确的程序,而无需任何用户编写的错误处理一点都不另一方面,任何尝试使用这样一个程序只会偏离略微程序员所预期的程序会给你带来一个非特定的,难以阅读的异常和堆栈跟踪以及对什么的指导你应该做得更好。您不必强制编写任何错误处理,并且通常不会出现错误处理。 (我无法将C ++与Java进行对比,因为虽然Java通常是安全的,但我还没有看到短 Java程序。)&lt; / rantmode&gt;
答案 1 :(得分:5)
C和C ++并不总是进行边界检查。它可能会导致运行时错误。如果你的数量过多,比如10000左右,那几乎肯定会导致问题。
你也可以使用vector.at(10),它肯定会给你一个例外。 看到: http://www.cplusplus.com/reference/vector/vector/at/ 和....相比: http://www.cplusplus.com/reference/vector/vector/operator%5B%5D/
答案 2 :(得分:4)
我希望vector的“operator []”将边界检查为“at()”,因为我不是那么小心。 : - )
一种方法是继承vector类并覆盖operator []以调用(),这样一个人可以使用更可读的“[]”,而不需要将所有“[]”替换为“at()”。您还可以将继承的向量(例如: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
答案 3 :(得分:3)
这是@Evgeny Sergeev的一个有价值的评论,我提出答案:
对于GCC,您可以-D_GLIBCXX_DEBUG用安全实现替换标准容器。最近,这似乎也适用于std :: array。更多信息:gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode.html
我想补充一点,也可以捆绑个人&#34; safe&#34;使用gnu_debug :: namespace前缀而不是std ::。
的vector和其他实用程序类的版本换句话说,不要重新发明轮子,至少可以使用GCC进行阵列检查。