《Effectirve C++》笔记(条款1~20)

本文是《Effective C++》笔记,涵盖条款1至20,内容涉及C++编程中的最佳实践,如使用const、enum、inline替代#define,理解和使用构造函数、析构函数、拷贝构造和赋值运算符,以及如何处理多态和资源管理。文章强调了避免异常逃离析构函数,理解编译器默认生成的函数,以及在设计类时要考虑其作为类型的整体性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

条款1:视C++为一个语言联邦

C是多种范式汇集的语言,包括C的过程、C面向对象、Template泛型(STL模板库)、模板元编程、lambda。(后面自己补充可能由曲解原文意思)

条款2:尽量以const、enum、inline替换#define

单纯变量用const或enum替换define,形势函数的宏用inline函数替换。

class ttt{
    enum{NumTurns=5};
    int scores[NumTurns];
}

用枚举替换更像define,因为枚举和define均不可取地址,而const的变量可以被取地址。

条款3:尽可能使用const

const char *p;指针非常量,数值为常量--我还是喜欢声明时把*和变量放一起,构成一个整体,这样也容易理解,const就是对这个整体修饰的这个整体不可变也就是*p也就是值不可变。char * const p;指针常量,值非常量 对函数指定const:void a() const;此时函数不可调用非const成员(可以读非const数据成员),如某个数据成员需要被const方法操作,可以用mutable修饰:mutable boos bbb;

const char& operator[](std::size_t position)const {
  XXXXX
  return pText[position];
}
char& operator[](std::size_t position){
  return const_cast<char&>(stati_cast<const CTextBlock&>(*this)[position]);
}
避免代码重复可用const_cast去掉const符号。先将非const调用过程的this转换为const class object调用,实现对const函数调用,并将返回值的const去掉。注意可以用非const函数使用此方式,不要用const函数通过此方式调用非const函数。

条款4:确定对象使用前已先被初始化

内置(class、struct内)对象需要手工初始化。

构造函数最好用初始化列表,而不是在函数体内进行赋值(函数体内赋值前会自动先对成员进行构造,若没有初始化列表构造时的值是随机的,这样等于两次操作),初始化列表次序随意,但初始化顺序以声明顺序为准。 不同文件中的外部对象初始化顺序不一定,若跨文件调用(以extern方式引用全局变量)不保证其已经被初始化。尽量使用内置对象。

条款5:了解C++默认编写并调用哪些函数

这个对么?这里说编译器会自动创建构造、拷贝、析构函数,但《深度探索C++对象模型》中说的是只有必要的时候才会创建出来。文中说如果有自定义的构造函数则不会创建,但探索中说的是也会在自定义构造前面加东西如果必要。。。。我更原因相信深度探索里写得,因为那样更合理,更高效,通过编译期的复杂判断保证了runtime时的高效。

条款6:若不想使用编译器自动生成的函数,就该明确拒绝

将拷贝构造、赋值操作符声明为私有可以禁止此行为。可通过建立一个具有私有拷贝和赋值函数的类并继承此类以避免每个类都重复私有化函数的撰写。

条款7:为多态基类声明virtual析构函数

带多态性质的base class应具有虚析构函数,而不是普通析构函数(这样会导致以base class指针释放派生类时不调用派生类的析构函数,派生类空间未释放),也不是纯虚析构函数(析构调用过程是最深层次的base class的析构最先被调用,若为纯虚将无法调用)

继承stl中的也要注意是否有虚析构,若没有不建议继承。

可以用组合,感觉还是多用组合好。接口要保证虚析构+所有其他均为纯虚函数,没有构造函数。

条款8:别让异常逃离析构函数

析构函数不应该抛出异常:对象在函数结束时自动析构,若出现异常会中断后续对象的析构,若未中断后续析构再出现异常会有多个异常同时出现,这些行为都不可控。

析构函数中若有可能出异常的调用,应自己try catch,并在catch中消除异常不继续上传,或直接调用std::abort结束程序。对于可能发生异常的方法最好提供普通接口供用户直接调用处理,并在析构时亦自动调用以减少一部分错误可能。

条款9:绝不在构造和析构过程中调用virtual函数

对于C++:在构造和析构中不要调用虚函数,因为这样的调用不会真正调用到派生类。java C#没事。

编译期间会自动添加代码完成vptr的指向,并完成基类、成员对象的构造代码的添加以及数据成员构造代码,这些都会增加到函数代码之前(初始化列表的初始化值对应添加到相应构造初始值中)。这样来说构造函数中的代码执行时vptr已经正确指定了,只要派生类重写了就已经指向派生类了。

析构是先调用函数本体,然后进行从深层次到当前的内存释放。

也就是说,如果保证这个构造函数是最后的派生类的构造函数时,是可以在构造、析构中调用虚函数的。如果是基类的构造和析构那么真出现了调用绝对不会调用到派生类。

条款10:令operator=返回一个reference to *this

赋值采用的右结合律 x=(y=(z=15)));

赋值操作符要返回一个对当前对象的引用:

MyStr& operator =(const MyStr& str) {
    return *this;
}

 

条款11:在operator=中实现“自我赋值”

避免x=x发生时出现自己的资源被自己释放的问题。要精心处理复制过程的new delete以及拷贝过程,包括对源和目标的分析。其余的任何函数也应注意被操作的多个对象实质是一个对象时的问题。

MyStr& operator =(const MyStr& str) {
    if(this == &str) return *this;
    return *this;
}

 

条款12:复制对象时勿忘其每一部分

赋值、拷贝函数应确保对所有成员变量及base class成分的复制。不要一个copy调用另一个copy而应该共同机制放在第三个函数。

条款13:以对象管理资源

通过对象的构造函数创建资源,并在析构函数释放。尽量不要在一个函数内new后delete以防止delete之前return导致资源未释放。(资源取得时机便是初始化时机“Resource Acquisition Is Initialization,RAII”)。

可以使用std::auto_ptr,auto_ptr在调用赋值/拷贝函数时,会将源中存储的指针改为null,避免共同管理同一个对象,否则析构时会多次释放导致不可控。

sharedptr通过引用计数方式记录(引用计数型智慧指针reference-counting smart pointer,RCSP),而不是在赋值/拷贝时直接转移所有权(源定义为null),以避免对源智能指针的操作导致错误问题。注意环状引用时会出错,两个指针互指且不再使用也不会自动释放。

std::tr1::shared_ptr的析构函数是delete不是delete []不要对动态分配得到的array使用,std::tr1::shared_ptr spi(new int[10]);此时最后只会释放int[0]这一个空间。boost中的boost::scoped_array和boost::shared_array可以进行动态分配空间的delete,但一般用string、vector等容器即可。

c++11中auto_ptr已经被弃用。应使用shared_ptr。

C++ Technical Report 1 (TR1)是ISO/IEC TR 19768, C++ Library Extensions(函式库扩充)的一般名称。TR1是一份文件,内容提出了对C标准函式库的追加项目。这些追加项目包括了正则表达式、智能指针、哈希表、随机数生成器等。TR1自己并非标准,他是一份草稿文件。然而他所提出的项目很有可能成为下次的官方标准。这份文件的目标在于「为扩充的C标准函式库建立更为广泛的现成实作品」。

C++ tr1是针对C标准库的第一次扩展。即将到来的下一个版本的C标准c++0x会包括它,以及一些语言本身的扩充。tr1包括大家期待已久的smart pointer,正则表达式以及其他一些支持范型编程的东东。草案阶段,新增的类和模板的名字空间是std::tr1。

C11,先前被称作C0x,即ISO/IEC 14882:2011,是目前的C编程语言的正式标准。它取代第二版标准ISO/IEC 14882:2003(第一版ISO/IEC 14882:1998公开于1998年,第二版于2003年更新,分别通称C98以及C03,两者差异很小)。新的标准包含核心语言的新机能,而且扩展C标准程序库,并入了大部分的C++ Technical Report 1程序库(数学的特殊函数除外)。最新的消息被公开在 ISO C++ 委员会网站(英文)。

条款14:在资源管理类中小心copying行为

对RAII对象复制要同时复制器管理的资源。常见的复制行为是抑制复制、引用计数。

条款15:在资源管理类中提供对原始资源的访问

应提供对原始资源获取的接口,有些需要直接操作原始资源的情况。可通过get函数或者通过operator A() const {}方式提供隐式转换函数。

条款16:成对使用new和delete时要采取相同形式

new和delete要同时使用[]或者都不用。

条款17:以独立语句将newed对象置入智能指针

对于智能指针的使用应独立一个语句,而不是把对象创建、智能指针构建作为一个其他函数的参数,这样可能出现内存泄漏。

如双参数函数,第一个参数是智能指针,第二个是其他操作,当new出新的对象以后,先执行了第二个参数操作并出现异常,此时new的新对象并未作为参数使智能指针构造,process(std::tr1::shared_ptr(new Widget), priority());应分为两个函数。

条款18:让接口容易被正确使用,不容易被误用

shared_ptr可以定制删除器(第二个参数),防止DLL问题,可用于自动解除互斥所。cross-DLL problem对象在动态链接库dll中被new创建,却在另一个dll内被delete销毁。

保证接口一致性、误用上要有足够的约束。

条款19:设计class犹如设计type

每个类都是一个类型,应充分考虑一个类型的各方面,包括重载函数和操作符、内存分配于释放、对象的创建和销毁。

条款20:宁以pass-by-reference-to-const替换pass-by-value

用const引用替换传值,这样高效也能避免切割问题,对于内置类型传值更好。

多态只有在引用、指针才有效,若将派生类以值的方式给予父类可能出现切割情况。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值