More Effective C++笔记(一)(精心整理)
一、基础议题
条款1:仔细区别pointers和references
- 指针使用*和->,引用使用“.”
- 引用必须指向一个已初始化的对象,不能为null,而指针可以指向某个对象,也可以为null
- 指针可以被重新赋值指向另一对象,而引用不可变更指向。
条款2:最好使用C++转型操作符
使用C++的转型操作符可以被很好的辨识,精确指明意图。
- static_cast:基本拥有与C旧式转型相同的威力,以及相同限制。几乎什么都可以转,但是不能将struct转int,double转指针,不能移除常量性。
- const_cast:用来将某个对象的常量性移除。
- dynamic_cast:用来执行继承体系中安全的向下转型或跨系转型。可以将指向基类对象的指针或引用转型为指向派生类的指针或引用。如果转型失败,对于指针返回null,对引用返回异常。
- reinterpret_cast:转换结果与编译器息息相关,所以不具备移植性。常用来转换函数指针类型,比如一个函数接受void*的函数指针,你可以把返回值为int类型的函数,转化为void*函数指针放进去调用。
条款3:绝不要以多态方式处理数组
派生类通常比其基类对象有更多的数据成员,两者大小不同,而数组内存的计算是以sizeof(数组中的对象)计算的,所以给基类传递派生类类型的数组,会导致数组大小不一致而出现未知错误。
条款4:非必要不提供default constructor
一个类如果缺乏default constructor将会带来:
- 产生数组时,一般而言没有任何方法为数组对象指定constructor自变量。
- 将不适用于template-based container classes,对模板来说,被实例化的目标类型必须要有default constructor。
二、操作符
条款5:对定制的“类型转换函数:保持警觉
有两种函数允许编译器进行转换:单参数构造函数(single-argument constructors)和隐式类型转换运算符。单参数构造函数是指只用一个参数即可调用的构造函数。该函数可以是只定义了一个参数,也可以是虽定义了多个参数但第一个参数以后的所有参数都有缺省值。
隐式类型转换运算符只是一个成员函数:operator关键字,其后跟一个类型符号。
1.explicit关键字是为了解决隐式类型转换而特别引入的。构造函数前用explicit声明,防止隐式转换。
2.定义类似功能的函数,而抛弃隐式类型转换,使得类型转换必须显示调用。例如 String类没有定义对Char*的隐式转换,而是用c_str函数来实施这个转换。
条款6:区别increment/decrement操作符的前置和后置形式
前置式和后置式操作符的实现(++)
后置operator++(int) 的叠加是不允许的,如i++++。
原因有两个:
一是与内建类型行为不一致(内建类型支持前置叠加);
二是其效果跟调用一次 operator++(int) 效果一样,这是违反直觉的。另外,后置式操作符使用 operator++(int),参数的唯一目的只是为了区别前置式和后置式而已,当函数被调用时,编译器传递一个0作为int参数的值传递给该函数。
防止后置式叠加的实现:通过返回const对象。
注:处置用户定制类型时,尽可能使用前置式,因为后置式会产生一个临时对象。
条款7:千万不要重载&&,||和,操作符
char *p = NULL;
if(p != 0 && strlen(p) > 10) { }
上面的代码不会报错,虽然 p 是空指针,但 && 符号采用"骤死式"评估方式,如果 pi == 0 的话,不会执行后面的语句。执行顺序从左到右。如果重载了&&这些操作符,会破坏"骤死式"评估方式,事先需要对两个参数都进行评估,并且未明确定义调用动作中的各参数评估顺序。
逗号操作符情况类似,比如f:
for(int i = 0, j = strlen(s) – 1; i < j; ++i, --j)
编译器首先评估逗号左侧,然后评估右侧,最后整个逗号表达式的结果以逗号右侧的值为代表。
不能重载:
. .* :: ?:
new delete sizeof typeid
static_cast dynamic_cast const_cast reinterpret_cast
你能重载:
operator new operator delete
operator new[] operator delete[]
+ - * / % ^ & | ~
! = < > += -= *= /= %=
^= &= |= << >> >>= <<= == !=
<= >= && || ++ -- , ->* ->
() []
条款8:了解各种不同意义的new和delete
1. new operator 和operator new区别
string *ps = new string(“hello”);
new operator 第一用来发分配足够内存,放置某类型对象,第二它调用一个constructor为刚才分配的内存中的那个对象设初值。
Void * operator new(size_t size);
Operator new唯一任务是分配内存,可以被重写或重载,改变其行为。取operator new返回的内存并将之转换成一个对象是new operator的事。
2. placement new:特殊版本的operator new,可以在分配好的原始内存上构建对象。
注:如果你打算将对象产生于heap,使用new operator;如果只是分配内存,调用operator new;如果打算在heap object产生时自己决定内存分配方式,请写自己的operator new,并使用new operator;如果在已分配(并拥有指针)的内存中构造对象,使用placement new
3. delete operator 和operator delete
作用和区别于new一一对应,delete operator先析构对象,再释放内存,而operator delete只释放内存。
4. 数组anrray的内存分配方式
内存不再通过operator new分配,而是通过operator new[]分配,然后对数组中每个对象调用constructo。释放内存通过相应的operator delete[]。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!