###《More Effective C++》- 操作符
More Effective C++
#@author: gr
#@date: 2015-05-21
#@email: forgerui@gmail.com
五、对定制的“类型转换函数”保持警觉
5.1. C++中存在的转型
- C++语言默认提供的隐式转型,包括将
int转换为short,将double转换为char。 - 实现自己类型可能存在的转型:单自变量constructors和隐式类型转换操作符。
单自变量constructors:
指能够以单一自变量成功调用的constructors。可能声明单一参数;也可能声明多个参数,但除第一个参数外都有默认值。
class Name{
public:
Name(const string& s); //可以把string转换为Name
//....
};
class Rational{
public:
Rational(int numerator = 0, int denominator = 1); //可以把int转换为Rational
};
隐式类型转换操作符:
指拥有奇怪名称的member function:关键词operator之后加上一个类型名称。不能指定返回值,因为返回值类型基本上已表现于函数名称上。
class Rational{
public:
operator double() const;
};
//在下面情况下自动调用
Rational r(1,2);
double d = 0.5 * r; //将r转换为double
5.2. 隐式转换存在的问题
隐式类型转换操作符
Rational r(1, 2);
cout << r; //实际上想调用<<,但忘定义<<,将会调用double进行转换,与期望不符
如上面的这种情况,当类型不符时,会自动转换,不会报错提醒程序员。
解决方法:不要使用类型转换操作符,另取它名,需要时,手动调用成员函数实现转换。class Rational{
public:
double asDouble() const;
}; Rational r(1, 2);
cout << r.asDouble() << endl;
C++标准委员会在实现string时,也没有提供默认类型转换,提供了一个
c_str()函数转换成const char *。单变量的constructors
在C++中提供了
explicit关键字,避免隐式调用构造函数转换,必须显示调用。
如果编译器不能使用explicit,那么必须手动设计,以避免隐式转换。方法是增加一个Proxy做中间层。//template <class T>
class Array{
public:
class ArraySize{ //增加新类ArraySize做Proxy
public:
ArraySize(int numElements) : theSize(numElements){}
int size() const {return theSize;}
private:
int theSize;
}; Array(int lowBound, int highBound);
Array(ArraySize size); //这个声明使用ArraySize类型,而不是int类型
}; Array<int> a(10); //Array<int>的构造函数需要ArraySize,编译器会先把10转换为ArraySize类型
if (a == b[i]){} //这里会出现问题,a是Array<int>,b 是int类型,没有直接的类型比较,因此会报错
六、区别increment / decrement操作符的前置和后置形式
6.1. 前置和后置函数的声明
前置和后置函数都没有参数,那么如何判断是前置式还是后置式呢?方法是将后置式加一个int参数,用以区分。
class UPInt{
public:
UPInt& operator++();
const UPInt operator++(int);
UPint& operator+=(int);
};
UPInt i;
++i; //前置式,调用i.operator++()
i++; //后置式,调用i.operator++(0)
6.2. 后置式返回const对象
UPInt i;
i++++; //如果后置式不是返回const,就可以连续调用
调用i++++的结果,并不像预期一样可以累加两次,所以要限制这种行为。方法就是使返回的对象为const,这样第二次调用++时编译器会检查不允许通过。
6.3. 后置式应以前置式为基础
UPInt& UPInt::operator++()
{
*this++;
return *this;
}
const UPInt UPInt::operator(int) //省略参数名,防止编译器报警告
{逗号表达式,又称为“顺序求值运算符”。逗号表达式的一般形式为
表达式1,表达式2,表达式3……表达式n
求解过程是:先求解表达式1,再求解表达式2,...。整个逗号表达式的值是最后一个表达式n的值。
例如这里的“i++,p++”,先求i++的值,然后求p++的值,整个表达式的值是p++的运算结果
另外、逗号运算符是所有运算符中级别最低的
UPInt temp = *this;
++*this; //调用前置式
return temp;
}
后置式实现应以前置式为基础,这样只要维护前置式版本,后置式版本会自动调整为一致行为。
七、千万不要重载&&,||和,操作符
7.1. 真值表达式的骤死式
含义:一旦真假值确定,即使表达式中还有部分尚未检验,整个评估工作仍告结束。
“用户定制类型”的&&和||操作符,会将expr1 && expr2解析成expr1.operator&&(expr2),会存在如下两个问题:
- 当函数调用动作被执行时,所有动作都被评估完毕,没有所谓的骤死式语义。
expr1和expr2两个表达式的评估顺序不定,而"骤死式"总是由左向右评估。
所以不要重载&& 或 ||。
八、了解各种不同意义的new和delete
8.1. operator new vs new operator
new operator:1. 分配对象的内存空间;2. 调用constructor为分配的内存设定初值。
// new operator
Widget *p = new Widget();
//上面的代码会产生如下的动作
// operator new
void *p = operator new(sizeof(Widget)); //分配空间
p->Widget(); //调用构造函数
operator new:只分配空间,不调用构造函数。
operator new声明如下:
void * operator new (size_t size);
可以重载operator new,加上额外的参数,但第一个参数必须总是size_t类型。
// 调用operator new
void *rawMemory = operator new (sizeof(string));
//调用new operator
string *ps = new string("Memory Management");
8.2. Placement new
有时候有一些分配好的原始内存,需要在上面构建对象,有一个特殊版本的operator new,称为placement new。
class Widget{
public:
Widget (int widgetSize);
//....
};
Widget * constructWidgetInBuffer(void *buffer, int widgetSize)
{
return new (buffer) Widget(widgetSize);
}
这是new operator的用法之一,其隐式调用operator new, 它的形式如下:
void * operator new (size_t, void *location)
{
return location;
}
这样的operator new 即称为placement new。它不需要做任何事情,只需将原始地址返回,之后operator new会调用构造函数构造对象。
8.3 删除(Deletion)与内存释放(Deallocation)
delete operator和operator delete同new一样:
delete ps;
// 等价如下代码
ps->~string();
operator delete(ps);
如果只打算处理原始的内存,使用new operator和delete operator就行了,它的行为像malloc
和free:
void *buffer = operator new(50 * sizeof(char));
operator delete(buffer);
8.4 数组
string * ps = new string[10];
上述使用的new仍然是个new operator,内存不再以operator new分配,而是由operator new[]实现。
在new []时需要调用delete[]删除。
delete[] ps;
###《More Effective C++》- 操作符的更多相关文章
- Effective C++(10) 重载赋值操作符时,返回该对象的引用(retrun *this)
问题聚焦: 这个准则比较简短,但是往往就是这种细节的地方,可以提高你的代码质量. 细节决定成败,让我们一起学习这条重载赋值操作符时需要遵守的准则吧. 还是以一个例子开始: Demo // 连锁赋值 x ...
- [Effective JavaScript 笔记]第33条:使构造函数与new操作符无关
当使用函数作为一个构造函数时,程序依赖于调用者是否记得使用new操作符来调用该构造函数.注意:该函数假设接收者是一个全新的对象. 一个例子 function User(name,pwd){ this. ...
- More Effective C++ - 章节二 : 操作符(operators)
5. 对定制的 "类型转换函数" 保持警觉 允许编译器执行隐式类型转换,害处多过好处,不要提供转换函数,除非你确定需要. class foo { foo(int a = 0, in ...
- 【More Effective C++ 条款2】最好使用C++转型操作符
C的转型方式存在以下两个缺点: 1)几乎允许你将任何类型转化为任何类型,不能精确的指明转型意图,这样很不安全 如将一个pointer-to-base-class-object转型为一个pointer- ...
- More Effective C++: 02操作符
05:谨慎定义类型转换函数 有两种函数允许编译器进行隐式类型转换:单参数构造函数(single-argument constructors)和隐式类型转换运算符.单参数构造函数是指只用一个参数即可以调 ...
- [Effective C++ --010]令赋值操作符返回一个reference to *this
差不多最经典的就是这个了: x = y = z = ; 解读为: x = (y = ( z = )); 如果没有返回值,上述代码就不能通过编译. 其实看到标题就差不多明白这一条了,但是为什么连续赋值时 ...
- Effective java笔记(二),所有对象的通用方法
Object类的所有非final方法(equals.hashCode.toString.clone.finalize)都要遵守通用约定(general contract),否则其它依赖于这些约定的类( ...
- 《Effective Java》学习笔记——积累和激励
从一个实际案例说起 国庆长假前一个礼拜,老大给我分配了这么一个bug,就是打印出来的报表数量为整数的,有的带小数位,有的不带,毫无规律. 根据短短的两个多月的工作经验以及猜测,最终把范围缩小到以下这段 ...
- Effective C#中文版
我看的书是<Effective C#中文版——改善C#程序的50种方法>,Bill Wagner著,李建忠译.书比较老了,04年写的,主要针对C#1.0,但我相信其中的观点现在仍有价值.( ...
随机推荐
- 射频识别技术漫谈(15)——Mifare1的安全性及7字节序列号M1卡【worlsing笔记】
Mifare1的安全性主要指卡中数据的安全性,要求卡中的数据不能被非法修改或窃听.数据的安全性主要使用加密技术来保证,加密技术有两个关键因素:加密算法和密钥.现代加密技术的一大特点是加密算法公开,如果 ...
- position跟display、margin collapse、overflow、float这些特性相互叠加后会怎么样?
这是寒冬大神提出的一个题目,刚开始看到这题的时候完全不知道从什么地方回答起好,题目内容比较广泛,找不到针对点.后来我觉得这个题目应该能拆成几个点来回答:1.'display'.'position' 和 ...
- MYSQL- 创建和删除临时表
临时表可能是非常有用的,在某些情况下,保持临时数据.最重要的是应该知道的临时表是,他们将当前的客户(www.111cn.net)端会话终止时被删除 当你创建临时表的时候,你可以使用temporary关 ...
- Redis实战之征服 Redis + Jedis + Spring (二)
不得不说,用哈希操作来存对象,有点自讨苦吃! 不过,既然吃了苦,也做个记录,也许以后API升级后,能好用些呢?! 或许,是我的理解不对,没有真正的理解哈希表. 相关链接: Redis实战 Redis实 ...
- Android上实现仿IOS弹性ScrollView
[转]http://www.tuicool.com/articles/ummIJb
- 基于注解的Spring MVC整合Hibernate(所需jar包,spring和Hibernate整合配置,springMVC配置,重定向,批量删除)
1.导入jar watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdG90b3R1enVvcXVhbg==/font/5a6L5L2T/fontsize/400 ...
- 详解Android ActionBar之一:ActionBar概述与创建
前面一个系列中讲的是Fragment的内容,Android 3.0中除Fragment外,Action Bar同样也是一个很重要的知识点.我们经常使用Action Bar来替代传统的标题栏.如果是An ...
- (源)V8 Engine 编译
v8 engine编译 *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !imp ...
- Memcached source code analysis -- Analysis of change of state--reference
This article mainly introduces the process of Memcached, libevent structure of the main thread and w ...
- iOS 本地自动打包工具
1.为什么要自动打包工具? 每修改一个问题,测试都让你打包一个上传fir , 你要clean -> 编译打包 -> 上传fir -> 通知测试.而且打包速度好慢,太浪费时间了.如果有 ...