变量名字的意义
一 问题
先让我们看一段简单c++代码来引出本篇文章探讨的话题,
#include <iostream>
#include <vector>
using namespace std;
class TestClass
{
public:
void addData(int data) { vData.push_back(data); }
vector<int> getVector() { return vData; }
private:
vector<int> vData;
};
int main()
{
TestClass tc;
tc.addData(1);
tc.addData(2);
tc.addData(3);
tc.addData(4);
vector<int>::const_iterator it = (tc.getVector()).begin();
for (; it != (tc.getVector()).end(); ++it) {
cout << *it << endl;
}
return 0;
}
对象tc添加了4个int值存入到内部的vData里,然后获取vData的迭代器并使用该迭代器去打印,输出如下,
并没有输出我们想要的结果,我们期望输出为1,2,3,4。
但是代码看上去没问题啊,而这样的输出显示是迭代器失效了。这到底是为啥呢?
二 调试
那就让我们看看迭代器指向的地址上的值到底是什么。
这里使用codeblocks进行调试,如果是初次调试,那么首先要去设置调试器,也就是debugger,这个可以去网上搜下设置方法。
设置好调试器后,我们打一个断点,如下,
然后按F8启动调试,程序运行到断点处,然后打开Watches和Memory dump窗口(Watches用来查看变量的值,Memory dump用来查看内存地址上的值),
如下,
从Watches中可以看到迭代器it上存放的地址值是0x7b1630,vData的起始地址是0x7b1670。然后我们在Memory dumo界面里输入这个地址,并把打印的bytes数目改为128,然后点击go,显示如下,
可以看到从地址0x781630开始存放的值并不是我们期望的1,2,3,4(因为是整形,所以每个值要占4个字节),而vData的地址是0x7b1670,可以看出在这个位置上存放的值是正是存入的1,2,3,4。由此可证明迭代器it得到的地址失效了。
vector<int>::const_iterator it = (tc.getVector()).begin();
问题肯定是出在上面这行代码,让我们理一下这行代码的逻辑:tc.getVector()返回vData的一份拷贝,然后通过这份拷贝获取迭代器的起始值赋给it。
看着没毛病,但是我们忽略了一个问题:我们没有对tc.getVector()返回的vector进行命名!!!
我们把代码修改如下,声明一个叫tmp的vector去接受vData的拷贝,
vector<int> tmp = tc.getVector();
vector<int>::const_iterator it = tmp.begin();
for (; it != tmp.end(); ++it) {
cout << *it << endl;
}
再编译运行,就得到正确的输出了,如下,
我们再次使用断点来调试看看,
按F8启动调试,运行到断点处,此时打开Watches和Memory dump窗口,如下,
此时迭代器it上存放的地址值是0x341630,vData的地址是0x341670,在Memory dump窗口中可以看到it指向的地址上的值和vData地址上的值是一样的了,这样就表示运行ok了。
三 总结
首先,引入《C++ primer 5th》上对变量的定义,
变量提供一个具名、可供程序操作的存储空间。
可以看出变量的名字的真正意义是可以让程序进行操作指定的一段内存空间,反观之前的错误写法,
vector<int>::const_iterator it = (tc.getVector()).begin();
我们没有对tc.getVector()返回的vector进行命名,这样这条语句执行完后,我们就无法操作这个返回的vector对应的内存空间,因为这段内存空间已经不再起作用(可能被回收了),继而导致迭代器it的失效。
所以,当我们贪图简洁而使用这种链式写法时,必须非常小心,以免掉入坑中。
如果有写的不对的地方,希望能留言指正,谢谢阅读。