一件小事
事情是这么开始的,首先某个业务模块结果出现错误,与原结果对比不一致。检查了一下,是PCLint整改过程中,某人修改了下列代码(大概类似,便于理解,稍微改造一下,代码写的如何不做评论),原有代码:
std::list intList;
…;
std::list::iterator curItr = intList.begin();
std::list::iterator preCurItr = curItr++;
for (; curItr != intList.end(); preCurItr = (curItr++)) {
…;
}
PCLint整改后代码(仅for循环部分):
for (; curItr != intList.end(); curItr++) {
preCurItr = curItr++;
…;
}
这代码明显逻辑错误,于是,我便修正为以下代码(PCLint问题可能会重新存在,先不予考虑):
for (; curItr != intList.end(); preCurItr = curItr++) {
…;
}
有问题吗?
先不看后文,自己思考一下,修改后的代码有问题吗?
对比原有代码和我修改后的代码,可以非常明显看出for循环中缺少一个括号,于是我便认为:括号的优先级比较高,会先进行括号内部的运算,然后在进行赋值运算,那岂不是两个迭代器指向同一个元素了吗?
一个字:试!
由于原代码作者已经离职,原代码又没有任何问题,我便怀疑是算法本来就是这样?仔细思考了一下,又根据上下文代码推断了一下,确定了算法肯定不会这么执行的。于是乎,只能尝试一下这个到底有没有问题了,尝试的结果就是写下以下测试代码:
std::vector intVec = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::vector::iterator curItr = intVec.begin();
std::vector::iterator preCurItr = curItr++;
for (; curItr != intVec.end(); preCurItr = curItr++) {
std::cout << “curItr: “ << *curItr << “ preCurItr: “ << *preCurItr << std::endl;
}
curItr = intVec.begin();
preCurItr = curItr++;
for (; curItr != intVec.end(); preCurItr = (curItr++)) {
std::cout << “curItr: “ << *curItr << “ preCurItr: “ << *preCurItr << std::endl;
}
结果果真是完全一样的,以我的个性,肯定要打破铁锅问到底呐。
操作符 - 函数
身边的宁哥突然来了一句,++操作符就是这么实现的,跟括号有什么关系。真是“众里寻他千百度,蓦然回首,那人却在灯火阑珊处”啊。
是啊,仔细想想,就是一个操作符而已,后置++操作符的实现大概过程如下:
int tmp = *this;
++*this;
return tmp;
无论加不加括号,return的值永远都是tmp,实际上跟括号没有半毛钱关系啊。实际上操作符的实现不也就是一个函数吗?换个角度考虑一下不就豁然开朗了吗?
进一步思考
学而不思则罔,思而不学则殆。我总是犯一些很二的问题,所以要记得时时刻刻进行总结,否则几年之后还在纠结这些东西,就真是贻笑于大方之家了。
顺着刚才的劲头,于是乎写下了以下代码:
class A
{
public:
A()
{
std::cout << “Constructor” << std::endl;
}
A(const A &)
{
std::cout << "Copy Constructor" << std::endl;
}
A &operator=(const A &)
{
std::cout << "Copy" << std::endl;
return *this;
}
A &operator++()
{
std::cout << "Pre Plus" << std::endl;
return *this;
}
A operator++(int)
{
std::cout << "Post Plus" << std::endl;
A tmp = *this;
++*this;
return tmp;
}
};
int main()
{
A a;
A b = a++;
A c = (a++);
return 0;
}
深入理解一下,有这么几个关键点:
1. ++操作符优先级大于赋值操作符=,即使不考虑后置++的返回值,a++与(a++)也是完全一致的,当然此处跟操作符优先级没有关系,只是稍微思考一下。
2. C++编译器识别函数是否同名,主要使用(函数名_参数类型)方式,所以后置++多一个int型参数实际上只是为了区别用,没有任何实际意义,另外,int后不要定义参数名称,否则编译器会报warning:此参数没有被使用。
3. 前置++和后置++的返回值类型要注意区别,另外后置++内部实际上是调用前置++,这个有效率消耗,尤其是自定义类型不会被编译器优化时(这个在Debug模式下体现很明显,尤其是迭代器)。
4. 在同一条语句里面不能修改同一个变量一次以上,否则会造成未定义错误。例如:
int a = 0;
a = a++;
这样写有些编译器会在编译期间报出未定义的warning,有些编译器会在程序执行期间产生错误值,需要注意。
5.摒除一切罪恶的根源就是:不要写出让人难以理解的代码。当然在一些效率问题上可能会有例外,不过那也是一种美,叫做短码之美。但是大部分代码还做不到这种“美”,那就写“可维护的代码”吧!