MoreEffectiveC++Item35(操作符)(条款5-8)
条款5 对定制的"类型转换函数"保持警惕
条款6 区别increment/decrement操作符的前值和后置形式
条款7 千万不要重载&&,||,和,操作符
条款8 了解不同意义的 new 和 delete
条款5 对定制的"类型转换函数"保持警惕
1.C++有默认类型转换,如 int---char
2.单自变量constructors(能够以单一变量成功调用起constructor,起构造函数可以有多个参数但是除第一个参数外,其他参数必须有初省值)
class Name
{
public:
Name(const string & s); //可以将string转化成Name
...
}; class Person
{
public:
Person(string name,int age = );//可以将 string 转化成Person
....
};
对于自定义类型的类型转换,有一个规则"没有任何一个转换程序可以内含一个以上的‘用户定制转换行为’(亦即单自变量constructor亦即隐式类型转换操作符)",
必要的时候编译器可以先进行内置类型之间的转换,再调用带单自变量的构造函数.
或者先调用隐式类型转换操作符在进行内置类型之间的转换.
但不可能连续进行两次用户定制的类型转换.
3.隐式类型转换操作符 关键词 operator + 类型名 (你不能为该函数定义返回值,其返回值已经反映在类型名上)
class Rational
{
public:
operator double const;//将 Rational转换成double
....
};
//在一下情况会被调用
Rational r(,)// r 的值为1/2
double d = 0.5 * r//先将 Rational转换成double 再进行运算
隐式类型转换操作符type()"需转则转,能转则转".例如设计者定义了一个有理数类Rational,同时定义了 operator int(),而没有定义<<,这种情况下如果对于语句"cout<<a;",编译器应该报错来提醒设计者,但实际上a会被转为int然后输出,这背离了设计者的初衷。
为了防止以上现象出现,我们可以定义一个函数来取代类型转换操作符.虽然使用时有些不便
class Rational
{
public:
...
double asDouble() const; //将 Rational 转换为double
}; Rational r(, );
cout << r; // 错误!(编译时不会出错但是打印时会出错) Rationa 对象没有 operator<<
cout << r.asDouble(); // 正确, 用 double 类型 正确打印 r
在C++库函数中的 string 类型没有包括隐式地从 string 转换成 C 风格的 char*的功能,而是定义了一个成员函数 c_str 用来完成这个转换
4.单自变量的构造函数由于隐式的转换可能会出现更加隐蔽的错误
template<class T>
class Array {
public:
Array(int lowBound, int highBound);
Array(int size);
T& operator[](int index);
bool operator==( const Array<int>& lhs,
const Array<int>& rhs);
...
} Array<int> a();
Array<int> b();
...
for (int i = ; i < ; ++i)
if (a == b[i]) { //判断两个数组是否完全相等(这里的[]被落下 但是编译不会出错 但是与判断两个数组是否完全相等的的意义偏离)
do something for when
a[i] and b[i] are equal;
}
else {
do something for when they're not;
}
5.解决隐式转换带来的不便
a.explicit 关键字
template<class T>
class Array {
public:
...
explicit Array(int size); // 注意使用"explicit
...
}; if (a == b[i]) ... // 错误! 不可以使用隐式转换
b.编译器不支持explicit
template<class T>
class Array {
public:
class ArraySize { // 类嵌套类
public:
ArraySize(int numElements): theSize(numElements) {}
int size() const { return theSize; }
private:
int theSize;
};
Array(int lowBound, int highBound);
Array(ArraySize size); // 注意新的声明
...
}; bool operator==( const Array<int>& lhs,
const Array<int>& rhs);
Array<int> a();
Array<int> b();
for (int i = ; i < ; ++i)
if (a == b[i]) ...//现在是一个错误,int调用隐式类型转换 但是不能转换ArraySize,所以会报错
允许编译器执行隐式转换弊大于利,所以非必要不要提供转换函数
条款6 区别increment/decrement操作符的前值和后置形式
// 前缀形式:增加然后取回值
UPInt& UPInt::operator++()
{
*this += ; // 增加
return *this; // 取回值
} // postfix form: fetch and increment
const UPInt UPInt::operator++(int)
{
UPInt oldValue = *this; // 取回值
++(*this); // 增加
return oldValue; // 返回被取回的值 返回值为const类型防止 i++++的调用
}
1.++i 是返回的是原对象的引用,因而可以执行类似于++++a这样的式子.
c++中有一条原则就是“绝对不让用户更改临时对象”(异常处理除外),因为临时对象是由编译器产生的,我们无法主动使用,同时它的生存期也无法由我们来掌握,所以除了用临时对象来充当传入参数或返回值的"载体"外,任何对临时对象的更改都是无意义的,也就被编译器严厉禁止
i++ 如果返回值不用const修饰的话 那么 i++++ 调用时 即调用了 i.operator++(0).operator++(0),第一次++ 返回的是一个临时变量 然而第二次操作用的是该临时变量 所以第二次++ 不产生效果
2.前置++效率高于后置++(中间不产生临时变量)
3.如果要同时定义++i和i++,i++的定义最好以++i为实现基础,这样可以保证它们行为的一致
条款7 千万不要重载&&,||,和,操作符
1.重载&&和||没有办法知道左面表达式与右面表达式 哪一个先计算,所以千万不要重载
2.千万不要重载 ,
void reverse(char s[])
{
for (int i = , j = strlen(s)-;
i < j;
++i, --j) // 啊! 逗号操作符!
{
int c = s[i];
s[i] = s[j];
s[j] = c;
}
}
for循环最后一个成分必须是一个表达式,如果表达式中含有逗号 那么逗号左侧一定先回被评估 然后以逗号的右侧值为代表
不要重载的意义在于这些你无法控制
3哪些可以重载 ,哪些不可以
//这些不可以重载
. .* :: ? :
new delete sizeof typeid //这些可以重载 operator new operator delete
operator new[] operator delete[]
+ - * / % ^ & | ~
! = < > += -= *= /= %=
^= &= |= << >> >>= <<= == !=
<= >= && || ++ -- , ->* ->
() []
千万不要让 strlen调用nullptr
条款8 了解不同意义的 new 和 delete
1.new operator = new 分配内存且初始化(调用ctors),不可重载
string *ps = new string("Memory Management");
//它等于以下代码
void *memory = // 得到未经处理的内存
operator new(sizeof(string)); // 为 String 对象
call string::string("Memory Management") //初始化
on *memory; // 内存中的对象
string *ps = // 是 ps 指针指向
static_cast<string*>(memory); // 新的对象
2.operator new : C++标准库函数,只分配内存不初始化,可重载
//函数 operator new 通常这样声明:
void * operator new(size_t size);//参数 size_t 确定分配多少内存 你能增加额外的参数, 重载函数 operator new,返回 void* 但是第一个参数类型必须是 size_t。
3. delete operator = 析构 + 释放内存 operator delete的作用也就只有一种:释放内存 () 作用相反于 new operator , operator new
4.重载的operator new必须具有void* 返回类型而且第一个参数必须为size_t类型(就算是int类型都不行),此外根据是定位new还是普通new,operator new决定是否增加指针参数(只要满足返回类型为void*而且第一个参数类型为size_t,其他参数随便加,只不过普通new表达式只调用只含有一个size_t参数的operator new版本,而定位new表达式只调用含有一个size_t类型和指针参数类型的operator new版本,自己重载的其他版本可以按需要添加参数
5.operator new[], operator delete[]数组版的operator new 和 operator delete
string *ps = // 调用 operator new[] 以分配足够的内存
new string[]; // 10个string对象的内存 然后针对每个元素调用default ctor
delete [] ps; //为数组中的每一个元素调用dtor 然后调用operator delete[] 释放内存
重载的operator delete必须具有返回类型void,它可以定义为接受单个void*类型形参,也可以定义为接受void* 和size_t类型两个形参,如果提供了size_t类型的形参,就有编译器用第一个形参所指对象的大小自动初始化size_t形参(但是第一个形参不是void*型么?void* 型是不具备所指对象大小的信息的,可能是编译器又在背后做了其他事情吧),对于编译器自动初始化size_t形参的做法,这种类似的做法已经出现过许多次了,比如定位new中的operator的第一个size_t形参,以及后自增操作符大的哑元参数等(编译器确实背着我们干了许多事)
重载的operator new[]必须具有返回类型void*,并且接受的第一个形参类型为size_t,用表示存储特定类型给定数目元素的的数组的字节数值自动初始化操作符的size_t形参”,注意,operator new[]返回的地址之前还有4个字节用来存储元素数目(编译器背着我们做的,就算自己重载了也一样)
注意delete 和 new 搭配使用 operator new与operator delete
MoreEffectiveC++Item35(操作符)(条款5-8)的更多相关文章
- MoreEffectiveC++Item35(效率)(条款16-24)
条款16 谨记80-20法则 条款17 考虑使用 lazy evaluation(缓释评估) 条款18 分期摊还预期的计算成本 条款19 了解临时对象的来源 条款20 协助完成"返回值的优化 ...
- MoreEffectiveC++Item35(异常)(条款9-15)
条款9 使用析构函数防止内存泄漏 条款10 在构造函数中防止内存泄漏 条款11 禁止异常信息传递到析构函数外 条款12 理解"抛出一个异常''与"传递一个参数"或调用一个 ...
- MoreEffectiveC++Item35(基础议题)(条款1-4)
条款1:区别指针和引用 条款2:最好使用C++转换操作符 条款3: 绝对不要以多态的方式处理数组 条款4: 避免无用的缺省构造函数 条款1:区别指针和引用 1.指针(pointer) 使用[*/-&g ...
- MoreEffectiveC++Item35 条款27: 要求或禁止对象产生于heap中
一 要求对象产生在heap中 阻止对象产生产生在non-heap中最简单的方法是将其构造或析构函数声明在private下,用一个public的函数去调用起构造和析构函数 class UPNumber ...
- MoreEffectiveC++Item35 条款25 将constructor和non-member functions虚化
1.virtual constructor 在语法上是不可将构造函数声明成虚函数,虚函数用于实现"因类型而异的行为",也就是根据指针或引用所绑定对象的动态类型而调用不同实体.现在所 ...
- MoreEffectiveC++Item35 条款26: 限制某个class所能产生的对象个数
一 允许零个或一个对象 我们知道每当即将产生一个对象,我们有一个constructor被调用,那么我们现在想组织某个对象的产生,最简单的方法就是将其构造函数声明成private(这样做同事防止了这个类 ...
- 新式转型操作符[条款9] --《C++必知必会》
在旧式转型(cast)下面隐藏着一些见不得人的.鬼鬼祟祟的东西.他们的语法形式使其在一段代码中通常很难引起人们的注意,但它们可能会搞一些可怕的破坏活动,就好比你冷不丁被一个恶棍猛击一拳似的.让我们阐明 ...
- Effective C++ 笔记2(构造,析构,赋值)
条款5:了解C++默默编写并且调用了哪些函数 1. 构造函数,析构函数,拷贝赋值函数,拷贝构造函数. class Empty { public: //默认构造函数 Empty(){}; //拷贝构造 ...
- 转:Effective c + + notes
补充自己的. 转自:http://blog.csdn.net/ysu108/article/details/9853963#t0 Effective C++ 笔记 目录(?)[-] 第一章 从C转向C ...
随机推荐
- SDUT OJ 2607
/*http://acm.sdut.edu.cn/sdutoj/problem.php?action=showproblem&problemid=2607*/ 题目大意:给出一个字符串,求出里 ...
- matlab基本操作总结
1.clear;//清除工作平台变量2.close all;//关闭打开的图形窗口3.I = imread('C:\Users\dell\Desktop\rice.jpg');//读取图像,存储在I数 ...
- MySQL-5.7 备份与恢复
一.备份分类 按介质分类: 物理备份 指通过拷贝数据库文件方式完成备份,适用于数据库很大,数据重要且需要快速恢复的数据库. 逻辑备份 指通过备份数据库的逻辑结构和数据内容的方式完成备份,适用于数据库不 ...
- Visual Studio 2015 初体验
据微软介绍每次发布的新版本,都承载着为开发者提供最高效的Visual Studio开发体验的使命.Visual Studio 2015亦延续了这一趋势,为开发者带来了进一步的生产力创新,包括调试和诊断 ...
- mysql搜索不区分大小写
mysql搜索是不区分大小写的,这种情况下我们有两种方法解决 知识前提: BINARY binary不是函数,而是一个类型转换运算符,它用来强制字符串为一个二进制字符串,可以理解为在字符串比较的时候区 ...
- 快用Visual Studio(三)- 代码重构
什么是代码重构 编写代码 | 找到代码 | 修改代码 关于重构的工具 Bracket Matching Selection Cursors Intelligence Parameter hints E ...
- CentOS 6.2配置本地yum源
转载自http://www.cnblogs.com/centoser/articles/2411694.html#undefined 一.挂载本地光盘到系统:把Cent6.2安装光盘放入光驱,在终端命 ...
- Excel导出失败的提示
未处理System.InvalidCastException HResult=-2147467262 Message=无法将类型为“Microsoft.Office.Interop.Excel.App ...
- mybatis批量保存的两种方式(高效插入)
知识点:mybatis中,批量保存的两种方式 1.使用mybatis foreach标签 2.mybatis ExecutorType.BATCH 参考博客:https://www.jb51.net/ ...
- 第九篇:Spark SQL 源码分析之 In-Memory Columnar Storage源码分析之 cache table
/** Spark SQL源码分析系列文章*/ Spark SQL 可以将数据缓存到内存中,我们可以见到的通过调用cache table tableName即可将一张表缓存到内存中,来极大的提高查询效 ...