4.1:谨慎定义类型转换函数

<1>容易的方法是利用一个最新的编译器特性:explicit关键字

<2>C++编译器把">>"作为一个符号来解释,在两个">"间没有空格,语句会产生语法错误。

<3>隐式类型转换函数

示例代码如下:

 1 #include<iostream>
 2 using namespace std;
 3
 4 template<class T>
 5 class Array
 6 {
 7 public:
 8     class ArraySize
 9     {
10         public:
11             ArraySize(int numElements):theSize(numElements)
12             {
13                 cout<<"flag ArraySize(int  numElements)"<<endl;
14             }
15             int size() const
16             {
17                 cout<<"flag  size()"<<endl;
18                 return theSize;
19             }
20         private:
21             int theSize;
22     };
23
24     Array(int lowBound,int highBound)
25         {};
26
27     Array(ArraySize arsize)
28     {
29         cout<<"flag Array"<<endl;
30     };
31 };
32 void main()
33 {
34     Array<int>  a(10);
35 }
36
37 /*
38 flag ArraySize(int  numElements)
39 flag Array
40  */

4.2:自增,自减操作符前缀形式和后缀形式

<1>重载函数间的区别决定于它们的参数类型上的差异,但是不论increament或者decrement的前缀还是后缀都只有一个参数。

为了解决这个语言问题,C++规定后缀形式有一个int类型参数,当函数被调用时,编译器传递一个0作为int参数的值给该函数。

<2>前缀自增:增加然后取回;后缀自增:取回然后增加

<3>前缀形式

示例代码如下:

 1 UPInt & UPInt::operator++()
 2 {
 3     *this += 1;   //增加
 4     return *this; //取回值
 5 }
 6 const UPInt UPInt::operator++(int)
 7 {
 8     UPInt oldValue = *this;   //保留原值
 9     ++(*this);          //增加
10     return oldValue;    //返回原值
11 }

<4>后缀操作符函数没有使用它的参数。这个参数仅仅只是为了区别前缀函数调用。

<5>如果在函数内部没有使用参数,许多编译器会显示警告信息。为了避免这些信息,最常用的方式就是省略掉参数名称

<6>很明显后缀函数必须返回一个对象,但是为什么是const对象呢?假设不是const:

假设不是 const 对象,下面的代码就是正确的:

这组代码与下面的代码相同:

i.operator++(0).operator++(0);

很明显, 第一个调用的operator++函数返回的对象调用了第二个operator++函数。 有两个理由导致我们应该厌恶上述这种做法:

第一,是与内置类型行为不一致。当设计一个类遇到问题时,一个好的准则是使该类的行为与int 类型一致。而int 类型不允许连续进行两次后缀 increment:

int i;

i++++; // 错误!

第二个原因,是使用两次后缀 increment 所产生的结果与调用者期望的不一致。

如上所示,第二次调用operator++改变的值是第一次调用返回对象的值,而不是原始对象的值。因此如果:

i++++; 是合法的,i 将仅仅增加了一次。这与人的直觉相违背,使人迷惑(对于int类型和 UPInt 都是一样),所以最好禁止这么做。

C++禁止int 类型这么做,同时你也必须禁止你自己写的类有这样的行为。

最容易的方法是让后缀 increment 返回 const 对象。当编译器遇到这样的代码:

i++++; // same as

i.operator++(0).operator++(0);

它发现从第一个 operator++函数返回的 const 对象又调用 operator++函数,然而这个函数是一个 non-const 成员函数, 所以 const 对象不能调用这个函数。

如果你原来想过让一个函数返回 const 对象没有任何意义,现在你就知道有时还是有用的,后缀 increment和 decrement 就是例子。 (更多的例子参见 Effective C++ 条款 21)

如果你很关心效率问题, 当你第一次看到后缀 increment函数时,你可能觉得有些问题。

这个函数必须建立一个临时对象以做为它的返回值, (参见条款M19) ,上述实现代码建立了一个显式的临时对象(oldValue) ,这个临时对象必须被构造并在最后被析构。前缀 increment 函数没有这样的临时对象。

由此得出一个令人惊讶的结论,如果仅为了提高代码效率,UPInt 的调用者应该尽量使用前缀 increment,少用后缀 increment,除非确实需要使用后缀 increment。

让我们明确一下,当处理用户定义的类型时,尽可能地使用前缀 increment,因为它的效率较高。

我们再观察一下后缀与前缀 increment 操作符。它们除了返回值不同外,所完成的功能是一样的,即值加一。

简而言之,它们被认为功能一样。那么你如何确保后缀increment和前缀increment的行为一致呢?

当不同的程序员去维护和升级代码时,有什么能保证它们不会产生差异?除非你遵守上述代码里的原则,这才能得到确保。

这个原则是后缀 increment和 decrement 应该根据它们的前缀形式来实现。你仅仅需要维护前缀版本,因为后缀形式自动与前缀形式的行为一致。

正如你所看到的,掌握前缀和后缀 increment和decrement 是容易的。

一旦了解了他们正确的返回值类型以及后缀操作符应该以前缀操作符为基础来实现的规则,就足够了。

4.3:不要重载“&&”,“||”,或“,”

<1>char *p; if((p!=0)&&(strlen(p)>10))......

注意这个if条件的写法顺序

<2>运算符重载要求:

4.4:理解各种不同含义的new和delete

<1>new operator 称为new操作符与operator new称为new操作

string *ps = new string("Memory Management");

使用的是new operator ,即就是new操作符,这种用法是不可改变的,功能总是相同的。

完成两部分任务:第一,分配足够的内存以便容纳所需类型的对象;第二,它调用构造函数初始化内存中的对象。

而我们所能改变的是如何为对象分配内存。

new操作符调用一个函数来完成必需的内存分配,你能够重写或重载这个函数来改变它的行为。

new操作符为分配内存所调用函数的名字是 operator new。

原型如下:

void * operator new(size_t size);

调用形式与其他函数一样:

void *rawMemory=operator new(sizeof(string));

placement new旨在完成一种情况,那就是假如我们已经申请到了内存,想要在该内存上构建对象,就选择placement new

三种情况代码如下:

 1 #include<iostream>
 2 using namespace std;
 3
 4 class  Test
 5 {
 6     int a;
 7 public:
 8     Test(int data = 10):a(data)
 9     {
10         cout<<"Construction :"<<this<<endl;
11     }
12     ~Test()
13     {
14         cout<<"Destroy :"<<this<<endl;
15     };
16 };
17 void  main()
18 {
19     //new操作符(new operator)
20     Test *pa = new Test[2];  //完成两部分任务
21     delete []pa;
22     pa = NULL;
23
24     //new函数(operator  new)
25     void  *pb = operator  new(sizeof(Test));  //仅仅只是分配一块内存,类似于malloc
26     operator  delete(pb);
27     pb = NULL;
28
29     //placement new
30     void  *suffer = malloc(sizeof(Test));
31     Test *pc = new(suffer)  Test(100);  //在已经申请的内存上建立自己的对象
32     pc->~Test();
33     free(suffer);
34     suffer = NULL;
35 }

总结:

如果想在堆上创建一个对象,应该用new操作符,它分配内存,同时又为对象调用构造函数。

如果仅仅想分配内存,就用operator new函数,它不会调用构造函数

如果你想定制自己的在堆对象被建立时的内存分配过程,应该重载写你自己的operator new函数,new操作符会调用你定制的operator new

如果想在一块已经分配好的内存里建立一个对象,使用placement new

<2>delete操作符与operator delete的关系与new操作符与operator new的关系一样

delete p;

导致编译器生成类似的代码:

p->~Class();

operator delete(p);

转载于:https://www.cnblogs.com/Braveliu/archive/2013/01/06/2847167.html

More Effective C++ (运算符)相关推荐

  1. Effective C++ 类与函数设计和申明

    Effective C++ 类与函数的设计和申明 在看<Effective C++>这本书的过程中,我无数次的发出感叹,这他妈写得太好了,句句一针见血,直接说到点上.所以决定把这本书的内容 ...

  2. C++编程进阶8(最好不要实现类型转换运算符、单形参的构造函数与类型转换、临时对象与RVO)

    二十九.最好不要实现类型转换运算符 示例代码 class fraction { public:fraction(int numerator=0, int denominator=1);operator ...

  3. C++编程进阶4(C++中的强制类型转换运算符、不要返回自定义类内部成员的指针,引用和迭代器)

    十五.C++中的强制类型转换 C语言中的强制类型转换方式有两种 T i = (T)exp; T i = T(exp); 这两种方式没有差别,C++中也支持这两种方式,但是和C++中的强制类型转换运算符 ...

  4. C++编程进阶3(如何写出正确的operator=、operator运算符的返回值以及是否应该是成员函数的讨论)

    八.如何写出一个安全的operator= 首先,operator=的返回值通常是一个类的引用,这一点需要和C++标准库的容器类的operator=保持一致 其次,如果一个类中含有指针成员,那么要防止自 ...

  5. C++知识点43——解引用运算符和箭头运算符的重载及智能指针类的实现

    一.概念. 在自定义行为类似指针的类时,需要重载*和->.C++中的智能指针就重载了这两个运算符.->必须是成员函数,*也应该是成员函数.与内置类型保持一致,这两个函数通常都是const的 ...

  6. 万字长文带你一文读完Effective C++

    Effective C++ 视C++为一个语言联邦 STL Template C++ C Object-oriented C++ 一开始C++只是C加上一些面向对象特性,但是随着这个语言的成熟他变得更 ...

  7. effective c++_【阅读笔记】Effective C++()

    全文参考自Effective C++, Scott Meyers 程序来自本人 https://github.com/hxz1998/ccl 1. 让自己习惯C++ C++高效编程守则视状况而变化,取 ...

  8. 【《Effective C#》提炼总结】提高Unity中C#代码质量的21条准则

    原则1   尽可能地使用属性而不是可直接访问的数据成员         ● 属性(property)一直是C#语言中比较有特点的存在.属性允许将数据成员作为共有接口的一部分暴露出去,同时仍旧提供面向对 ...

  9. C++(16)--运算符重载(自定义Integer类)

    运算符重载 1.运算符重载--重点 2.友元函数--难点(流运算符重载) <老九学堂C++课程><C++ primer>学习笔记.<老九学堂C++课程>详情请到B站 ...

最新文章

  1. linux mysql 5.0.45_RedHat糸列Mysql-5.0.45的安装
  2. 【嵌入式开发】嵌入式 开发环境 (远程登录 | 文件共享 | NFS TFTP 服务器 | 串口连接 | Win8.1 + RedHat Enterprise 6.3 + Vmware11)
  3. jquery自动补全
  4. javascript 西瓜一期 03 机器语言与高级语言
  5. shell之旅--将目录下的文件重命名为md5码+后缀名
  6. (转)JavaScript事件冒泡简介及应用
  7. Normal Equation----machine learning
  8. 《学术小白的学习之路 02》情感分析02 之基于大连理工情感词典的情感分析和情绪计算
  9. debian10编译安装mysql
  10. java读写yml文件
  11. Foxmail设置的学习
  12. 轩辕实验室丨基于信息熵的车载网络流量异常检测防御方法
  13. 利用python做微信公众号标题的词云图
  14. 超高速定位文件,电脑必备软件
  15. JVM探秘:垃圾收集器
  16. windows10下安装MSYS2+MinGW64
  17. Revit二次开发——图元(元素)编辑
  18. 百度搜索和百度信息流有什么区别?
  19. 灵活自定义 PDF转换成Word转换器下载
  20. SE-Net:Squeeze-and-Excitation blocks

热门文章

  1. C++ operator操作符重载(++,--,-,+,())
  2. Sky Line 与 ArcEngine的粘合剂 Composite UI AB?
  3. mysql使用方法_Mysql的常用用法
  4. 测试框架之GTest
  5. mysql udb_MySQL InnoDB的一些参数说明
  6. AUTOSAR从入门到精通-AUTOSAR PPT介绍
  7. CNN常用卷积方法一览
  8. python的逆袭之路_Python领域最伟大工程师Kenneth Reitz的逆袭之路
  9. argparse模块_Argparse:一个具体案例教会你python命令行参数解析
  10. mysql select db error_select error:不能用DB-library(如isql)不能用DB-library(如isql