Effective C++学习总结(上)

Effective C++学习总结

最近我又看了一遍Effective C++,记录了一些重点,强化记忆以防自己忘记。

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

C++主要由四部分次语言组成——C、面向对象C++、泛型模板编程、STL,各部分编程守则不同,视状态的变化而变。

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

原因:#define不被视为语言的一部分,由于可能被预处理器提前取走,而不会被编译器发现,导致没有进入记号表,可能会获得不明确记号的编译器错误。对于浮点型常量,可能导致目标码出现多次数字。

const:1.尽量使用const string代替const char* const。2.#define 不能创建class专属常量成员。

enum:1.enum是类似于#define的做法,一种记号名称。2.取const的地址合法,取enum和#define的地址不合法,它们不会占有非必要的内存空间。

inline:1.类似函数的宏定义缺点太多,会导致很多问题。2.内联函数是一个真实的函数,能够完成很多宏不能完成的事。

条款3 尽可能使用const

const是一种对象不可改动的语义约束。

const对指针,在左边表示指向的数据是常量,在右边表示指针本身是常量,同时出现,说明都是,const在类型前后出现并没有区别。

迭代器中的const:1.const iterator等同于T * const。2.const_iterator等同于const T *。

函数中的const,1.const在最前面表示返回值是const的。2.const在参数列表括号后面表示函数是const成员函数,目的是为了确认此成员函数可作用于const class对象上,函数本身不改变对象内容,同时可以通过该函数可以操作const class对象。除了是否const外一样,可以被视为重载。

bitwise constness:不能改变任何一个比特,因此不能更改任何非静态成员变量。编译器强制执行。

logical constness:可以更改某些比特,但只要客户端检测不出的情况下才可以。给这些变量设置mutable,表示可能会被更改,可以在const成员函数中被更改。

避免const和non-const成员函数出现重复代码,可以使用常量移除,const成员函数不变,在non-const中调用const成员函数,即转型。const_cast<char&>(static_cast<const Test&>(*this)[postion])

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

原因:读取未初始化的值会导致不明确的行为,因为有时会初始化,有时不会。C次语言是不保证初始化的,而在C++的其他部分,就有所变化。

除内置类型外,初始化是在构造器中进行。

初始化不等于赋值!赋值在构造函数体内进行,初始化在成员初始化列表中进行。初始化列表是直接调用copy构造器,而在构造函数体内赋值,是首先调用默认构造器,然后再用copy运算符。

成员变量初始化顺序与成员初始化类别顺序无关,是与声明顺序一样的。先基类初始化再派生类。

“不同编译单元内定义之non-local static对象”初始化次序:编译单元即文件,函数内的static对象称为local static对象。解决方法是以local static对象替代non-local static对象。将non-local static对象放入函数中,在别的编译单元中调用。

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

四个——构造函数、copy构造函数、copy运算符和析构函数——都是public且inline。

默认构造函数和析构函数给编译器一个地方放置隐藏的代码——例如调用基类和成员类的构造函数和析构函数,指定vtpr等。编译器产生的析构函数是非虚函数,除非他的基类声明有virtual析构函数。当有用户声明的构造器,编译器就不会再创建默认构造器。

copy构造函数、copy运算符,编译器版本是将每个非静态成员拷贝到目标对象。当成员变量含有copy构造函数,则在该对象中是使用此copy函数,否则按bitwise初始化。

编译器拒绝生成内含引用和常量的类对象的copy assignment操作符。当基类的copy assignment为private,编译器同样拒绝生成派生类的copy assignment操作符。

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

拒绝编译器自己产生函数,主要有两种方法:1.将相应的函数声明为private,并且不对其实现。2.继承一个将函数设为private的基类,派生类也不能自动产生此函数。

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

原因:当delete一个基类的指针时,而且基类的析构函数非虚,此时派生成分没被删除,会造成内存泄漏。

体现多态的基类是带有virtual函数的,使得派生类能够重写该函数。因此只要有virtual函数一定有virtual析构函数。

无端为类的析构函数设定virtual,也是不正确的,会为类增加不必要的空间和时间开销(虚函数表和指针等)。

string类不含任何virtual函数,如果被继承会导致内存泄漏的问题。同理任何stl容器也不能被继承

为你希望它成为抽象的那个class声明一个纯虚析构函数。使其不能实体化。

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

C++不喜欢析构函数吐出异常。因为当析构函数吐出异常,程序可能过早结束或者导致不明确行为,例如容器中多个元素的析构函数吐出一个以上异常就会造成这种情况。

解决方法1.如果析构函数中抛出异常就结束程序(调用abort完成)2.吞下析构函数中因调用而发生的异常。

但以上方法并不能对异常的情况做出反应。

比较好的方法是将在析构函数中会发生异常的操作函数交给用户,给用户操作的权利,并对可能发生的问题(异常)作出反应。这将异常发生的位置放在其他函数中,而不是由析构函数吞下或结束程序(同时应该在析构函数中加入双保险,保证用户没有操作时,析构函数仍可以操作)。

如果必须对这个操作函数异常进行处理(做出反应),处理的位置必须在交给用户那个其他函数中,而不是析构函数中。因为析构函数吐出异常会导致程序可能过早结束或者导致不明确行为。

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

原因:派生类的析构函数和构造函数会调用基类的析构函数和构造函数,从而调用基类的virtual函数,此时的virtual函数是基类版本的virtual函数,即在基类构造期间,virtual函数不是virtual函数,派生对象还是基类对象,此时没有下降到派生类,并不会产生多态。

解决方法:把该virtual函数改为non-virtual,然后将派生类构造函数传递必要的信息给基类构造函数。根据这个信息可以使得在基类的构造期间,调用正确的函数。将该信息放在一个static函数中,可以避免意外指向没有初始化的成员变量。

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

原因:为了实现连锁赋值。使得operator= return *this 返回值是&。但此协议并无强制性,只是为了方便。

条例11 在operator=中处理“自我赋值”

自我赋值包括很多,例如引用、指针指向同一元素的赋值,当然也包括在同一继承体系下。

解决方法:1.传统方法是在operator=的最前面加入证同测试,检测是否是自我赋值。2.利用swap函数,拷贝后交换

同时,用于资源管理的类中operator=也要注意异常安全性,具备异常安全就具备自我赋值的安全性,例如针对new可能会产生的异常,delete原指针值尽量延后,可以通过临时变量来delete原对象,当new产生异常,原指针不会指向空位置。

普适的概念:确定任何函数如果操作一个以上的对象,而其中多个对象是同一个对象时,其行为仍然正确。

条例12 复制对象时勿忘其每一个成分

copy函数编译器是会在需要的时候自动生成的,而声明自己的copy函数,编译器不会在其必然出错时告知我们。不会提醒我们有些成员没有拷贝。

同时继承也会导致这个问题。派生类的copy函数往往忘记copy基类的成员,解决方法是调用基类的copy函数。

复制对象时确保1.复制所有本地成员变量2.调用所有基类的对应copy函数。

不应该为了减少重复代码,在copy assignment中调用copy构造函数,反之也不可以。应该将共同部分放在一个函数中,供两个函数调用。copy assignment是针对已初始化的对象的赋值,copy构造器是初始化。

条例13 以对象管理资源

用户自己管理资源,对申请的内存资源负责很麻烦,维护成本很高。为了确保申请的资源总是会被释放,可以将资源放在对象内,通过该对象的析构函数自动释放资源,依靠对象析构函数自动调用机制。

auto_ptr正是这种对象。一种“类指针对象”——“智能指针”,析构函数自动对所指的对象调用delete(不要让多个auto_ptr指向同一对象,auto_ptr其copy构造函数和运算符会使原指针变null,从而使得复制的auto_ptr含有唯一所有权)。关键点在于1.获得资源立即放进管理对象内。2.管理对象运用析构函数确保资源被释放。

auto_ptr的替代方案是引用计数型智慧指针,追踪一共有多少指针指向某个资源,在无人指向的时候删除资源,例如share_ptr。

share_ptr和auto_ptr两者都在析构函数中delete而不是delet[],不能处理数组资源,尽管可以通过编译。

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

由于不是所有的资源都是堆产生的内存资源,建立自己的资源管理类很重要。

针对copying,有两种选择1.禁止复制 将copy函数设为private 2.对底层资源引用计数,通常可以内含一个share_ptr,将资源和删除器放在share_ptr中,从share_ptr中get资源给获取器。

复制有两种:1.复制底部资源(深度拷贝)2.转移底部资源的所有权。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值