指出下面C++代码(with STL)的错误,测测你的水平(选自GotW)

陈硕 2001-10-30 12:48:46
加精
// Example 2: Some fun with vectors
// Difficulty: 4 / 10

vector<int> v;

v.reserve( 2 );
assert( v.capacity() == 2 );
v[0] = 1;
v[1] = 2;
for( vector<int>::iterator i = v.begin();
i < v.end(); i++ )
{
cout << *i << endl;
}

cout << v[0];
v.reserve( 100 );
assert( v.capacity() == 100 );
cout << v[0];

v[2] = 3;
v[3] = 4;
// ...
v[99] = 100;
for( vector<int>::iterator i = v.begin();
i < v.end(); i++ )
{
cout << *i << endl;
}

如果你能指出全部错误,你的C++水平一定很高喔。
...全文
195 11 打赏 收藏 举报
写回复
11 条回复
切换为时间正序
当前发帖距今超过3年,不再开放新的回复
发表回复
SognoMe 2001-12-11
我在msdn中只找到对reverse_iterator的!=重载,却没有iterator的,这是为什么?
template<class BidIt, class T, class Ref, class Ptr, class Dist>
bool operator!=(
const reverse_bidirectional_iterator<BidIt, T, Ref,
Ptr, Dist>& lhs,
const reverse_bidirectional_iterator<BidIt, T, Ref,
Ptr, Dist>& rhs);
template<class RanIt, class T, class Ref, class Ptr, class Dist>
bool operator!=(
const reverse_iterator<BidIt, T, Ref, Ptr, Dist>& lhs,
const reverse_iterator<BidIt, T, Ref, Ptr, Dist>& rhs);
template<class T, class Dist>
bool operator!=(
const istream_iterator<T, Dist>& lhs,
const istream_iterator<T, Dist>& rhs);
template<class E, class T>
bool operator!=(
const istreambuf_iterator<E, T>& lhs,
const istreambuf_iterator<E, T>& rhs);
The template operator returns !(lhs == rhs).


  • 打赏
  • 举报
回复
cber 2001-10-30
呵呵,我看来是没有好好注意了,只是凭直觉乱说了一通,看来有必要回头再去好好看看gotw原文(其实我对于gotw看的并不是很多,最近大部分时间用在XP上面了)
反正有一点可以保证的就是,我绝对不会写出这样的代码来的:)
  • 打赏
  • 举报
回复
陈硕 2001-10-30
刚发完上一贴才发现myan已经回复了,看来我的帖子是多余了。
  • 打赏
  • 举报
回复
陈硕 2001-10-30
cber:很遗憾地告诉你,你的回答只对了一半:((,看来老虎也有打盹的时候啊。

下面是“GotW #074”的主要内容,我知道你一定更喜欢看原文,但我贴此文是为了照顾一些刚学C++的朋友。

GotW #074主要内容:

// Example 1: [] vs. at()
//
void f( vector<int>& v )
{
v[0]; // A
v.at(0); // B
}
在这个例子中,如果v不是空的,则A句和B句无分别。若v是空的,那么B句会throw一个std::out_of_range的exception;C++ Standard从效率和兼容‘内建型别’上考虑,并不要求A句throw出exception。即vector<T>::at()有下标越界检查(Bounds checking),而vector<T>::operator []()一般(注意是一般,C++ Standard并没有说operator[]一定不能做下标越界检查)没有做下标越界检查。如果向operator[]传入非法的下标,其行为“不可预测”

// Example 2: Some fun with vectors

vector<int> v;

v.reserve( 2 );
assert( v.capacity() == 2 );
1. 如cber所说,v.reserve(2)表示v的内部缓冲区至少能容纳2个元素,而不一定刚好容纳2个元素(可能更多)。vector的大小是按指数增长的,即cber所说“选择2的n次方来分配空间”,这句话应该改为assert( v.capacity() >= 2 );

2. 其实这个assert( v.capacity() >= 2 );也是多余的,因为C++ Standard保证执行v.reserve(n);后, 一定有v.capacity() >= n;即在vector<T>::reserve()中已经做了类似的assert,你又何必自己再来做重复的工作呢?除非你对你的C++ Standard Libaray的健壮性不放心:)。


v[0] = 1;
v[1] = 2;
这两条语句有明显的错误:v.reserve()只是增加了vector的存储空间,并没有增加其中元素的个数。这时v仍然是空的(v.size() == 0,v.capacity() >= 2),所以这里下标越界了。可以考虑改为v.push_back(1);v.push_back(2);


for( vector<int>::iterator i = v.begin(); i < v.end(); i++ )
{
cout << *i << endl;
}
1. 这个循环不会打印任何东西,因为此时v是空的。
2. 尽量使用const,这里的iterator并没有改动v的元素,所以应该用const_iterator
3. 循环的结束条件处,比较两个iterators应该用 != 而非 < ,只有random-access iterator才能用 “<”来比较,而作为循环的结束条件,更加普遍的做法是用 “!=”来比较iter与v.end()。
4. 用++i取代i++,两者效率不同。
5. 避免不必要的重复计算,因为v.end()的值在循环中没有变,所以在循环之前对v.end()求一次值就可以了。
6. 用'\n'替代endl,因为使用endl会强制flush(冲洗) ostream的内部输出缓冲区,如果你不是每打印一个*i就需要flush一次缓冲区的话,用'\n'。
7. 使用标准的演算法,可以避免以上所有问题:
copy( v.begin(), v.end(), ostream_iterator<int>(cout, "\n") );
另外,似乎没有copy( v.begin(), v.end(), ostream_iterator<int>(cout, endl) );这种写法,所以当你确实需要每打印一行就flush一次缓冲区时,自己写一个copy()吧。


cout << v[0];
一般情况下,输出1,其实v[0]是越界了。


v.reserve( 100 );
assert( v.capacity() == 100 );
同上,这里的assert是不正确且多余的。
这里v重新分配(realloc)了缓冲区,但它认为自己只有0个元素,故原来的v[0] == 1并没有复制到新的缓冲区,所以新的v[0] == 0;所以下一句
cout << v[0];
会打印0,这一定会气倒一片人的。


v[2] = 3;
v[3] = 4;
// ...
v[99] = 100;
首先下标越界,其次按cber说的可以用for_each()


for( vector<int>::iterator i = v.begin();
i < v.end(); i++ )
{
cout << *i << endl;
}
改为:
copy( v.begin(), v.end(), ostream_iterator<int>(cout, "\n") );
关于用STL演算法取代自己编写循环的进一步讨论,请参看CUJ 2001十月,Scott Meyers写的《STL Algorithms vs. Hand-Written Loops》一文

好多的陷阱啊,一不小心就掉进去了,非得好好看看Effective STL / Exceptional C++不可。
我晕了,你晕了没有?

  • 打赏
  • 举报
回复
myan 2001-10-30
简直错的一塌糊涂,不忍卒读。
我时间很紧张,随便说一说:

vector<int> v;

v.reserve( 2 );
assert( v.capacity() == 2 ); // cber说得对
v[0] = 1; // 这里就有问题。reserve改变的是capacity, size没变化,
v[1] = 2; // 这时候就往容器里赋值,后患无穷,下面的循环根本不执行,
因为v.end()并没有向后移动
for( vector<int>::iterator i = v.begin();
i < v.end(); // i != v.end() 标准可没有保证vector一定是动态数组实现的
i++ )
{
cout << *i << endl;
}

cout << v[0];
v.reserve( 100 ); // 这个操作可能使指向v的所有指针、引用和迭代器失效
assert( v.capacity() == 100 );
cout << v[0]; // 所以这里可能在引用一个错误的元素

后面的就不说了。

v[2] = 3;
v[3] = 4;
// ...
v[99] = 100;
for( vector<int>::iterator i = v.begin();
i < v.end(); i++ )
{
cout << *i << endl;
}

  • 打赏
  • 举报
回复
FMD 2001-10-30
好东东
where to find XC++??
  • 打赏
  • 举报
回复
vcmfc 2001-10-30
#74 我昨天刚当看完.
  • 打赏
  • 举报
回复
cber 2001-10-30
这个条款已经收录在XC++中了,不过我现在书不在手边,还是试一试吧(主要是看看自己还记得多少):
vector<int> v;

v.reserve( 2 );
assert( v.capacity() == 2 );
//reserve(n)只是说容器的容量至少(不是一定)要能容纳n个元素
//实际上,为了分配方便,一般的编译器都是选择2的n次方来分配空间的

v[0] = 1;
v[1] = 2;

for( vector<int>::iterator i = v.begin();//为了避免不够聪明的编译器重复计算
i < v.end(); i++ ) //v.end()最好提取到循环外面,因为只是输出*i,
{ //最好选择使用const_iterator,另外,i++会导致效率低下,建议用++i
cout << *i << endl;
}
//实际上上述代码可以用一条语句搞定,如下:
//copy(v.begin(), v.end(), back_inserter(ostream_iterator<int>(cout, endl)));
//(很久没有写这样的代码,又懒得查书和测试,希望上面的语句没有大问题)

cout << v[0];
v.reserve( 100 );
assert( v.capacity() == 100 );//和前面的描述一样
cout << v[0];

v[2] = 3; //从这开始到v[99]=100的代码太冗余了,可以用一个循环语句或for_each搞定
v[3] = 4;
// ...
v[99] = 100;
for( vector<int>::iterator i = v.begin();
i < v.end(); i++ )//和上面的循环一样
{
cout << *i << endl;
}

//最后,该程序把整个std都暴露出来,这很不好,建议用using std::vector;
//using std::cout;/using std::endl等全称,这不容易导致name pollution
  • 打赏
  • 举报
回复
tinytot 2001-10-30
这个也要等!
  • 打赏
  • 举报
回复
陈硕 2001-10-30
11月1日公布答案。
  • 打赏
  • 举报
回复
tinytot 2001-10-30
愿闻其祥!
  • 打赏
  • 举报
回复
发帖
C语言
加入

6.6w+

社区成员

C语言相关问题讨论
社区管理员
  • C语言
  • 花神庙码农
  • 架构师李肯
申请成为版主
帖子事件
创建了帖子
2001-10-30 12:48
社区公告
暂无公告