条款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)的更多相关文章

  1. MoreEffectiveC++Item35(效率)(条款16-24)

    条款16 谨记80-20法则 条款17 考虑使用 lazy evaluation(缓释评估) 条款18 分期摊还预期的计算成本 条款19 了解临时对象的来源 条款20 协助完成"返回值的优化 ...

  2. MoreEffectiveC++Item35(异常)(条款9-15)

    条款9 使用析构函数防止内存泄漏 条款10 在构造函数中防止内存泄漏 条款11 禁止异常信息传递到析构函数外 条款12 理解"抛出一个异常''与"传递一个参数"或调用一个 ...

  3. MoreEffectiveC++Item35(基础议题)(条款1-4)

    条款1:区别指针和引用 条款2:最好使用C++转换操作符 条款3: 绝对不要以多态的方式处理数组 条款4: 避免无用的缺省构造函数 条款1:区别指针和引用 1.指针(pointer) 使用[*/-&g ...

  4. MoreEffectiveC++Item35 条款27: 要求或禁止对象产生于heap中

    一 要求对象产生在heap中 阻止对象产生产生在non-heap中最简单的方法是将其构造或析构函数声明在private下,用一个public的函数去调用起构造和析构函数 class UPNumber ...

  5. MoreEffectiveC++Item35 条款25 将constructor和non-member functions虚化

    1.virtual constructor 在语法上是不可将构造函数声明成虚函数,虚函数用于实现"因类型而异的行为",也就是根据指针或引用所绑定对象的动态类型而调用不同实体.现在所 ...

  6. MoreEffectiveC++Item35 条款26: 限制某个class所能产生的对象个数

    一 允许零个或一个对象 我们知道每当即将产生一个对象,我们有一个constructor被调用,那么我们现在想组织某个对象的产生,最简单的方法就是将其构造函数声明成private(这样做同事防止了这个类 ...

  7. 新式转型操作符[条款9] --《C++必知必会》

    在旧式转型(cast)下面隐藏着一些见不得人的.鬼鬼祟祟的东西.他们的语法形式使其在一段代码中通常很难引起人们的注意,但它们可能会搞一些可怕的破坏活动,就好比你冷不丁被一个恶棍猛击一拳似的.让我们阐明 ...

  8. Effective C++ 笔记2(构造,析构,赋值)

    条款5:了解C++默默编写并且调用了哪些函数 1.  构造函数,析构函数,拷贝赋值函数,拷贝构造函数. class Empty { public: //默认构造函数 Empty(){}; //拷贝构造 ...

  9. 转:Effective c + + notes

    补充自己的. 转自:http://blog.csdn.net/ysu108/article/details/9853963#t0 Effective C++ 笔记 目录(?)[-] 第一章 从C转向C ...

随机推荐

  1. 20145310《Java程序设计》第2次实验报告

    20145310<Java程序设计>第2次实验报告 实验内容 初步掌握单元测试和TDD 理解并掌握面向对象三要素:封装.继承.多态 初步掌握UML建模 熟悉S.O.L.I.D原则 了解设计 ...

  2. arm-linux工具

    arm-linux工具的功能如下: arm-linux-addr2line 把程序地址转换为文件名和行号.在命令行中给它一个地址和一个可执行文件名,它就会使用这个可执行文件的调试信息指出在给出的地址上 ...

  3. HTTP-java访问https资源时,忽略证书信任问题,代码栗子

    java程序在访问https资源时,出现报错 sun.security.validator.ValidatorException: PKIX path building failed: sun.sec ...

  4. Windows7使用无线网卡建立WiFi热点

    在Windows7下设置热点需要用到命令netsh wlan,具体的设置步骤如下: 1.配置热点 以管理员身份打开命令行模式,输入命令 netsh wlan set hostednetwork mod ...

  5. linux 搭建Java环境

    一.下载jdk/jre文件 下载链接 二.安装Java环境 1.解压文件到     /usr/java    目录 # tar zxvf jre-8u60-linux-x64.gz 2.配置环境变量 ...

  6. Tensorflow平台快速搭建:Windows 7+TensorFlow 0.12.0

    Tensorflow平台快速搭建:Windows 7+TensorFlow 0.12.0 1.TensorFlow 0.12.0下载 2016年11月29日,距离TensorFlow 宣布开源刚刚过去 ...

  7. 线程实现Runnable接口比继承Thread的优势

    1.适合多个相同程序代码的线程去处理同一资源,把虚拟CPU(线程)同程序的代码.数据有效分离,较好地体现了面向对象的设计思想.2.可以避免由于java单继承特性带来的局限.3.增强了程序的健壮性,代码 ...

  8. CentOS 7 SSH远程证书登陆

    SSH远程证书登陆是使用"公私钥"认证的方式来进行SSH登录. 1.创建公私钥 创建方式有很多种,比如说通用ssh连接工具创建,然后把公钥上传到Server主机对应的用户目录下: ...

  9. PHP Fatal error: Call to undefined function imagecreatetruecolor()

    是因为没有安装gd apt-get install libgd2-xpm-dev* apt-get install php5-gd

  10. eclipse创建文件package,source folder和folder区别及相互转换

    原文:http://blog.csdn.net/u014079773/article/details/66973910 https://www.cnblogs.com/shihaiming/p/735 ...