爱死Thinking in系列了,所以起了这个名字。本文的思想也部分来至于这套书,或参照对比,或深入挖掘,或补益拾慧,或有感而发,既包括Thinking in C++,甚至也包括Thinking in Java。
Thinking again in C++(二)自赋值是非公断
关键字:C++,自赋值,自复制,赋值,assign,assignment,复制,拷贝,copy
1.需要考虑的自赋值。当类包含指针或引用成员时应注意检查。
class String
{
private:
char * pc_Buffer;
public:
String & operator=(const String & strR);
String & operator+=(const String & strR);
//...
};
(1)类内部:对称赋值运算符、接受自身类型或自身基类类型参数的成员函数,有时候还要考虑+=系列运算符。
String & String::operator=(const String & strR)
{
if (this==&strR) //[1]
return *this;
delete [] pc_Buffer; //[2]
pc_Buffer=new char[strlen(strR.pc_Buffer)+1];//[3]
//...
}
[1]中的判断是必须的。如果this==&strR,[2]将本身删除,[3]就会使用“悬挂指针”。
下面operator+=()的实现隐藏着错误。
String & String::operator+=(const String & strR)
{
int iLengthNew=strlen(pc_Buffer)+strlen(strR.pc_Buffer);
char * pcBufferNew=new char[iLengthNew+1];
strcpy(pcBufferNew,pc_Buffer);
delete [] pc_Buffer; //[4]
strcat(pcBufferNew,strR.pc_Buffer); //[5]
pc_Buffer=pcBufferNew;
return *this;
}
如果this==&strR,[4]将本身删除,[5]就会使用“悬挂指针”。正确的做法不必使用判断语句,只需调换[4][5]两条语句的顺序。
(2)类外部(包括友元):接受多个同一类型参数或多个有继承关系的类型参数的函数。
class CDerive : public CBase{};
void f(CBase & b1,CBase & b2);
void g(CBase & b,CDerive & d);
CBase bSame;
CDerive dSame;
f(bSame,bSame); //[1]
f(dSame,dSame); //[2]
g(dSame,dSame); //[3]
[1][2][3]都出现了自赋值,所以f()、g()的设计中都要有所考虑。
2.不可能出现自赋值。
(1)拷贝构造器:因为正在构造的对象还未完全生成,而传递给构造器的实参对象是已构造完毕的对象,这两者绝不可能是同一对象。
(2)非对称赋值运算符:即使形参类型是自身的基类。若D是B的派生类,无论是否重载了对称赋值运算符,D类对象之间的赋值行为都不会调用D::operator=(const B & b)。
class CDerive : public CBase
{
public:
operator=(const CBase & b); //不用考虑this和b之间的自赋值
void f(const CBase & b); //需要考虑this和b之间的自赋值
};
CDerive dSame;
dSame=dSame; //[1]
dSame.f(dSame); //[2]
语句[1]中,编译器不会把dSame上溯造型为CBase,而是调用缺省或自定义的D::operator=(const D & d)。只有等式左边确为D,右边确为B,才调用D::operator=(const B & b),这时不可能出现自赋值。相反,语句[2]中,编译器会把dSame上溯造型为CBase,所以f()需要考虑自赋值。
3.不是自赋值的赋值。仅仅内容相同的赋值不是自赋值。
CTest a,b,same;
a=same;
b=same;
a=b; //[1]
[1]不是自赋值,不会出问题,不需要检查,而且内容相同无法直接用地址来检查。
4.不应该检查的自赋值。
strcpy(char * strDest,const char * strSrc);中,当strDest==strSrc时,是自赋值,但并不会出错。
发现自赋值直接返回,这种特定情况下,也许能提高函数效率10倍,但绝大多数没有出现自赋值时都多了一个条件判断,可能降低函数效率10%,最后综合计算加权平均效率可能还是降低了。这取决于自赋值出现的概率。
设不判断自赋值,函数执行时间为1;若检查自赋值,设出现自赋值的概率为x,直接返回函数执行时间为0.1,不出现自赋值,多了一个条件判断函数执行时间为1.1,那么如果要求加权平均效率不降低:
0.1x+1.1(1-x)<1
解之,得:x>0.1。也就是说自赋值出现的概率必须大于10%,这在实际代码中可能吗?
<!--内容结束//-->
分享到:
相关推荐
StyLua 使用构建的Lua 5.1,Lua 5.2和的公断代码格式化程序。 StyLua的灵感来自,它解析您的Lua代码库,并从头开始打印出来,以增强一致的代码样式。安装有多种安装StyLua的方法:随着Github发布预先构建的二进制...
1. **Tribunal**:这个词指的是一个进行裁决和公断的机构,可以是正式的法庭,如军事法庭,或是非正式的如舆论的公断。 2. **Court**:法院一词涵盖了多种类型的司法机构,包括民事法院(处理民事纠纷)、刑事法院...
第二部分"小包公断案"测试了学生对小数除法的理解。第1题指出,被除数缩小为原来的1/100,商也应缩小1/100,这是正确的。第2题说明小数点的移动是以除数的小数位数为准,也是正确的。第3题84÷0.01实际上相当于84...
4. **仲裁法的实施时间**:《中华人民共和国仲裁法》自1995年9月1日起施行,对国内及涉外仲裁进行了规范。 5. **仲裁的审理方式**:仲裁通常以不公开审理为主,公开审理为例外,以保护当事人的商业秘密和个人隐私。...
2. **小数除法的性质**:在第二部分“小包公断案”中,涉及了小数除法的一些关键性质。例如,第2题指出,计算小数除法时,小数点的移动应基于除数的小数位数,这是正确的。第1题提到如果被除数缩小为原来的1/10,...
仲裁、公断 * arise:v. 出现、起源于 等等。这些词汇涵盖了英语学习的多个方面,对高职高专英语学习者来说非常重要。 在英语学习中,掌握词汇是非常重要的。只有掌握了足够的词汇,才能更好地理解和使用英语。...
- **套汇/套价/公断交易 (Arbitrage)**:利用不同市场间的汇率差异获取利润。 7. **交易操作**: - **汇票交割/汇票议付 (Negotiation of Draft)**:银行或金融机构为汇票提供资金。 - **折扣交割/票据折扣 (To ...
首先,仲裁是一种争议解决机制,又称公断,是双方当事人在争议发生前或后通过协议,自愿选择中立的第三方进行裁判。仲裁法则是国家制定的法律规范,用来调整仲裁过程中涉及的所有法律关系。 仲裁法的适用范围有明确...
非强制性方法则是和平解决争端的手段,包括谈判、调查、调停、和解、公断、司法解决、区域机关或方法的利用,以及协商。《联合国宪章》特别强调,在维护世界和平与安全的大前提下,各国应优先考虑使用和平方法解决...
20. **争议解决方式**:法律程序解决争议包括公断、诉讼和仲裁。 21. **分包责任**:分包单位对其与总包单位签订的分包合同负责。 22. **指定分包商索赔**:指定分包商的违约或延误造成的索赔通常属于合同外索赔。...
- **解析**:大部分专家认为基因改良植物存在“长期但确实的风险”,而选项中“在还没有科学公断时,不应告诉大家某一技术是安全的”与大部分专家的态度明显不一致。 ### 7. 逻辑推理与理解 - **知识点**:逻辑推理...