
Effective C++学习笔记
文章平均质量分 60
基于Effective C++的总结笔记
小丑快学习
这个作者很懒,什么都没留下…
展开
-
《Effective C++》条款46:需要类型转换时将模板定义为非成员函数
条款 24 中,我们说到对于一个类来说,我们如果想要使用隐式类型转换(利用非explicit构造函数),我们需要将相应的韩叔叔定义为非成员函数。但是如果现在我们将条款24中的例子模板化,那么这种隐式类型转换将不会成立。现在我们象条款24中的方式将两个实数进行乘法运算,这时我们的程序将会导致编译错误。因为当我们尝试进行隐式类型转换时,我们首先许需要知道参数的类型,但是我们们传入的参数为一个 int类型,而我们的模板函数接受 Ratianal<T>类型的参数,来推断T的类型,这时,传入的参数原创 2021-04-11 20:52:36 · 196 阅读 · 1 评论 -
《Effective C++》条款 45 成员函数模板接受所有兼容类型
智能指针能够很好的支持类型转换,以及多态的特性,那么智能指针是怎样做到和普通指针一样的功能的?答案是利用成员函数模板。如下代码:我们希望上述的代码能够通过编译,也就是说我们的智能指针类需要实现一个对应版本的拷贝构造函数以及复制运算符重载,但是我们的基类有很多,因此对于这样的要求,相应的拷贝构造函数和复制运算符都应该是模板函数。因此智能指针的基本结构应该如下:但是,我们的类型转换中并不允许将基类类的指针转换成为派生类,因此,我们应该有有效的机制防止这样转换发生 ,显然我们利用原始指针的转换关系就原创 2021-04-09 11:21:00 · 103 阅读 · 0 评论 -
《Effective C++》条款44 将与参数无关的代码抽离
模板编程可以节省时间和避免代码重复,但是有时候使用模板却有可能导致代码膨胀,也就是当完成编译后,目标代码可能会很大,即使我们自己敲打出的代码看起来非常的间接。所以在使用模板编程的时候应该小心,如何避免重复将会还能重要。如下代码,便是矩阵,同时求矩阵的逆,现在如下的使用方式:上述的sm1和sm2将会被编译器认为是两个不同的类,因此,将会调用两个不同的函数invet,然而,事实上两个invert函数只有5和10两个参数不同,其他部分完全相同,因此这也就导致了代码膨胀的问题。为了解决上述的代码膨胀的问原创 2021-04-08 11:33:32 · 185 阅读 · 0 评论 -
《Effective C++》条款43:处理模板化基类内的名称
模板化的类是可以作为基类的,但是当我们将一个模板化的类作为基类,那么当我们使用该基类的中的成员方法时,需要显示的说明传入的模板参数中含有一个相应的方法可供调用。现有一个程序,表示要将信息发送给不同类型的公司,传输方式可以是加密传输也可以是直接传输,对于传输公司的选定,我们可以用模板指定。现在,我们想要在每次传输信息前和传输信息完成后记录日志信息,然后我们这样写代码:上述的sendClear(info)是来自模板化基类的成员函数,但是这样将会是无法通过编译的,因为编译器在你给出具体模板参数的类型之前原创 2021-04-07 11:25:56 · 181 阅读 · 0 评论 -
《Effective C++》了解typename的双重意义
在模编程中,可以说关键字class和typename是等效的,但是typename还有其他的用法,typename可以用于指明模板内的为类类型。对于以下的函数模板,假如是这样做设计的。对于编程的人而言,我们清楚的知道```C::const_iterator *x`` 是一个指向迭代器的指针,但是对于编译器则不然,因为如果当C的类中有静态变量为const_iterator,或者有一个局部变量为X的变量,那么上述的语句将会理解为两个数相乘。对于依赖于模板参数的类类型参数,我们称为嵌套从属类,也就是形如`原创 2021-04-06 15:39:56 · 149 阅读 · 0 评论 -
《Effective C++》条款 37:绝不重新定义继承而来的缺省参数值
我们都知道虚函数的调用是动态绑定的,但是,对于虚函数中的默认参=参数的值确实静态绑定的,什么意思?见代码:一个表示形状的类,其中颜色可以是默认的参数也可以是自主选择的颜色。对于以下的继承体系而言考虑如下的多态使用方式:这时将会出现问题了,对于Retangle,我们对其虚函数draw指定的默认参数为Green,但是如果我们用多态的方式去调用draw函数。这时 draw() 函数将会调用基类中的默认参数,因为对于虚函数的默认参数是动态绑定的,这样可以提高编译期运行效率。所以我们对于虚函数的默认原创 2021-04-01 10:12:51 · 168 阅读 · 0 评论 -
《Effective C++》条款 35:考虑virtual函数以外的其他选择
文章目录Non-Virtual Interface 方式函数指针实现的Strategy模式由tr1::function完成的Strategy模式古典的Strategy模式当我们设计面向对象程序时,我们不一定仅是聚焦于virtual函数,其实我们可以有很多的替代方案,每种方式有自己特点和优势。加入游戏中的人物需要一个函数用以表现任务的健康状态(血量),而游戏中又具有各种不同的角色,因此,我们应该设计一个基类来表示任务角色的相同特征。相应的,该函数会有一个默认的实现方式。默认方式省略。事实上,在设计模原创 2021-03-31 16:27:57 · 242 阅读 · 0 评论 -
《Effective C++》条款34:区分接口继承和实现继承
继承关系中,由虚函数,纯虚函数、以及非虚函数的继承方式,这三种继承方式各自由各自的特点,一般三种成员函数大概目的可以分为以下:纯虚函数一般来说用于继承接口,而且是不可缺少的接口非纯的虚函数一般用于继承一个接口,以及一份缺省的实现,该实现可以被重载非虚函数一般是让派生类继承一个接口,以及一份不可更改的实现。非纯虚函数继承中的弊端以及避免方式一个表示飞机以及飞机飞行方式的类以及他的派生类之间的继承关系。上述的两种飞机A和B都选择默认的飞行方式,这时,只需要直接调用基类的飞行方式即可。倘若这时有原创 2021-03-30 17:09:38 · 155 阅读 · 0 评论 -
《Effective C++》条款33、避免遮掩而来的名字
对于继承关系中得函数得同名作用域问题,现在已经清楚编译器是如何去对作用域进行查找的了,但是我们依然需要了解在继承和虚函数的关系中,我们的同名e问题是怎样的。参考下面一段代码:上面的继承关系中,派生类和基类中的函数出现了同名的问题,这时,派生类中的同名函数将会遮掩掉所有基类中的同名函数。如下的用法将会更加清晰:即使派生类中的函数和基类中的有所不同,但是在派生类对象中,也无法调用基类中的同名函数。使用using避免同名函数遮掩问题我们可以使用using声名将派生类中的同名函数引入到派生类中,这样便原创 2021-03-30 15:25:26 · 137 阅读 · 0 评论 -
《Effective C++》条款 30、降低文件间的编译依存关系
文章目录handle classInterface class对于这样一个程序:上述的Person类中含有两个类的对象,因此该文件中应该包含相应的头文件,这就带来一个问题,当我们改变任何一个头文件,或者改变该头文件包含的任何头文件,那么Person类就会被重新编译,并且所有包含Person类的头文件也都将会被重新编译。也就是当一个头文件被修改时,不管是直接包含该头文件还是间接包含该头文件的文件都将重新编译,因此对于那些大型项目来说,这将会导致每次修改后的编译时间都将会大量增加。因此,我们期望再Pers原创 2021-03-29 17:21:36 · 233 阅读 · 0 评论 -
《Effective C++》条款29、保证异常安全
异常安全的要求程序的设计应该满足异常安全,而异常安全应该保证当异常发生时我们的程序应该处于一个合法的状态,这种合法的状态的状态有两个基本要求:不能泄露任何资源不允许数据败坏那么这两点要求具体参考如下代码:代码要求更改背景图片,但是该操作要保证原子性,因此,先对这个该操作进行上锁,操作完成后释放锁。先将原来的图片删除,然后再更改为新的图像。但是这会有个严重的问题,那就是当new运算符分配空间失败时这个程序实际上就不能保证异常安全,当发生异常,我们的互斥锁将会永远的锁上没无法释放,这就导致资源泄原创 2021-03-29 11:32:20 · 239 阅读 · 0 评论 -
《Effective C++》条款28 避免返回指向对象内部的对象或者引用
假设有一个类表示矩形,但是我们又不希望我们的类过于庞大,因此,我们将会定义一个表示点的类来表示某个点,而我们的矩形类中我们只需要保存相应的指针即可。我们在举行的类中设计两个表示举行的范围的函数,分别返回矩形的左上角和右上角。然而这样的设计本身就是一个矛盾体,因为当我们返回了point的引用我们将会改变矩形的参数,而const函数只是保证不在函数的内部不改变函数的成员,但是其返回的成员是一个点的引用,这样我们将会改变原来的矩形的参数。如我们返回的是const的引用,及如下:这样的话用户可用读取原创 2021-03-28 17:22:14 · 154 阅读 · 0 评论 -
《Effective C++》条款27、尽量的少做类型转换
C++程序的设计原则是尽可能的尽可能的避免的类型的转换,因为有时类型转换往往会带来一些编译上的错误,而有些虽然不会带来错误单数会导致逻辑上的错误,但是却很难发现。C++语言中提供了四种类型转换方式:...原创 2021-03-28 16:06:36 · 194 阅读 · 0 评论 -
《Effective C++》条款 26、尽可能延后变量的定义
变量定义原则当我们定义了一个变量时,特别是当这个变量需要构造和析构时我们需要承担这个变量的析构和构造成本,因此,我们定义变量的原则是当我们要使用该变量是我们才定义它。特别是抛出异常的程序中当我们定义变量时我们就需要考虑该异常是否一定会被用到,如下的程序:变量encrypted不一定会被用到,因为一旦发生异常,很有可能程序将会结束该函数的调用或者终止程序,所以正确的做法是将变量定义再异常抛出代码段的后面。如下的操作:使用拷贝构造函数我们定义变量有时我们需要将变量的值进行特定的初始化,如果用如下方式原创 2021-03-15 14:53:00 · 167 阅读 · 0 评论 -
《Effect C++》条款25、一个不抛出异常的swap函数
swap函数是一个常用的函数,在STL中有一个swap的基本版本,但是这个函数仅是采用简单的交换技术,效率实际上对于很多的情况是很低的。标准函数库的swap函数如下:非模板特化但是对于某些情况我们只需要交换某个指针,并不需要交换整个对象,因此此时我们调用标准库的函数的效率就会很低,因此对于某种特殊的对象的交换,我们需要写出自己的swap版本。比如下的类,我们只要交换它内部的指针便好。有一个Widget类包含PImpl的指针,我们希望有一个特化的版本能够针对特定的类,当调用swap时编译器就会调原创 2021-03-13 11:40:01 · 138 阅读 · 0 评论 -
《Effective C++》条款 23、24尽可能使用非友元的非成员函数
23 、尽量使用非成员函数有一个浏览器用于表示网页浏览器,里面有众多的函数用于清理浏览器相关的东西。当然这一特性也可以由一个人非成员函数来提供:上述两种方式我们该选择那种方式呢?我们知道对于封装性而言,非成员函数非友元函数的封装性远高于友元函数和成员函数,因此,我们应该首先选择将函数声明为非成员非友元函数。而对于C++语言而言,上述的这种函数往往会和相应的类定义在同一命名空间中,这样我们能够在其他的文件中添加该类函数到相应的命名空间中。条款24 、如果多个参数需要隐式类型转换,将函数设计为非成员函原创 2021-03-12 10:49:51 · 189 阅读 · 0 评论 -
《Effective C++ 》条款21、22
条款21、函数必须返回对象时,不可返回引用因为前面讲过,函数参数传值方式的效率没有传引用的方式效率高,因此,我们便会萌生,函数的返回值是否也是如此。然而,并不是这样的,当我们用引用返回函数的值时,有时会导致致命的错误。考虑下面一个表现有理数的类:这个类的 operator* 函数是返回的是以值得方式进行传递的对象,倘若我们将这个函数改为以引用的方式进行传值。则我们必须为返回的引用建立相应的对象。建立对象的方式不外乎两种方式,一种创建是在stack空间里面,而另一种则是创建于heap上。倘若我们原创 2021-03-11 10:14:52 · 241 阅读 · 0 评论 -
《Effective C++》条款20、尽量用const &替换值传递
const &能避免拷贝的开销在c++的参数传递过程中,有两种传值方式,一种是我们将会传递值,另一种则是我们将会传递一个const 参数引用。这两种方式有本质的区别,我们更推荐传递常量引用的方式进行参数传递。考虑从以下代码:现在我们有个函数会把Student作为参数传入没然后判断该学生信息是否合法。如果我们采用以上的方式进行值传递,那么我们在函数的局部作用域内就会为我们传入的Student对象做一个值得拷贝,也就是调用Student的拷贝构造函数对其进行拷贝,然后离开函数的局部作用域后将原创 2021-03-10 10:53:12 · 249 阅读 · 0 评论 -
《Effective C++》条款18、19 接口和类的设计
18、接口设计接口的设计不应该被误用假如设计一个表示日期的类,如果我们如下设计那么当用户使用这种接口的时候很容易出现问题,有可能会将年和月的如期写反,也有可能会填写不符合常理的日期,因此这种接口的设计并不是很好。所以,为了防止用户把参数的顺序填写错误,我们可以将年月日封装成类,这样当用户填写错误顺序时就将会收到编译器的抱怨。但是其实上述的方法依然存在问题,那就是当我们不小心将月份输错,很有可能输出错误的月份,因此应该做进一步的封装,以防止用户输入错误的月份。我们将所有的月份信息预先封装到类原创 2021-03-09 16:36:38 · 118 阅读 · 0 评论 -
《Effective C++》条款16、17 new和delete的使用
条款16、new和delete成对的使用对于delete和new的使用我们一定要小心我们使用的版本,当我们new出的是单个对象时,我们的delete使用的相应版本应该是delete,而当我们new了一个数组时我们应该调用的是 delete[ ]来释放内存,也就是说我们使用的new和delete应该配对,否则将会发生未定义错误,因此,当我们new时出现了[ ]运算符,我们的使用的delete也应该出现[ ]运算符。但是有时即使我们记住了这个准则我们还是不能避免错误,如下代码:当我们用typedef将数原创 2021-03-09 10:01:02 · 201 阅读 · 0 评论 -
《Effective C++ 》条款13、14 资源管理类(智能指针)
条款13、资源管理类当我们向系统申请资源时,我们总是需要在用完后归还,但是有时候我们总会忘记,即使我们不会忘记,但是由于异常或代码的维护都有可能导致我们的程序发生异常。void f(){ Object *obj=new Object(); ......... ......... delete obj;}假如上述程序发生错误,那么最后的delete操作将不会执行,所以我们不能总是依赖于delete操作帮我们去完成最后的内存释放操作,标准程序库中的智能指针可以帮我们完成这样的操作。auto原创 2021-03-08 10:41:16 · 166 阅读 · 0 评论 -
《Effective C++》条款12、继承中的拷贝和赋值
实现拷贝构造函数和赋值运算符时,我们不能遗忘任何一个成员,当我们自定义以上成员时,如果我们没有将其中的某个成员进行复制,编译器是没法检测出来的。这种情况下我们就会得到不正确的对象。所以当我们为某个类添加任何成员时,我们必须记得修改我们的拷贝构造函和复制运算符重载。派生类的拷贝和赋值在派生类中实现拷贝构造函数和复制运算符时我们不能遗忘基类成分,我们必须考虑我们派生类中的隐藏的基类成员的赋值。考虑以下的案例:class A {public: A() : Num(0), Name("") {}原创 2021-03-07 11:24:12 · 175 阅读 · 0 评论 -
《Effective C++》条款10、11(赋值返回和自赋值问题)
条款10、operator = 返回一个 *this的引用因为赋值我们有时会把它写成连锁的形式,因此我们的赋值运算符重载时应该能够处理这种情况。以上赋值将会被解析为如下格式:所以为满足以上的情形,赋值运算应该返回一个自己的引用,即返回 *this即可。11 赋值运算中处理自赋值赋值运算中,往往会有自赋值的出现,而往往自赋值会发生在很隐晦,因为指针和引用的使用,很可能出现自赋值这种情况的发生。所以在我们所编写的代码应该能够处理这种情况。如下代码:如果上述中赋值的对象是相同的,那么就会导原创 2021-03-06 10:54:36 · 192 阅读 · 0 评论 -
《EffectiveC++》条款09 析构和构造时不调用虚函数
在多态操作中,我们不能在析构函数和构造函数中调用虚函数,因为在基类构造和析构期间,我们不能调用派生类的函数,也就是说,构造和析构期间的虚函数不在是虚函数。考虑以下代码:上述代码我们想要在构造基类的时候调用派生类logTransaction(),但是这并不会实现,实际将会调用基类的函数,这种情况下实际派生类的logTransaction()还未初始化,因为编译器总是会在派生类之前初始基类成员,因此编译器没办法找到基类的函数调用,所以只能调用父类的函数,而父类的函数为纯虚函数,因而编译器将会发生连接错误。原创 2021-03-05 16:37:27 · 213 阅读 · 0 评论 -
《Effective C++》条款 07、08(多态下的虚析构函数,析构函数和异常)
07虚析构函数C++中明确的定义了,当一个派生类对象经由一个base class指针删除,而该base class 带有一个非虚函数的虚构函数,其结果是未定义的。实际上这种情况下,将会调用基类的析构函数,因而,派生类独有的对象将会没有释放内存,因此编译器将会报错。因此在涉及多态的操作中,无疑我们的析构函数应该设计为虚函数,同时只要一个类中有任何一个虚函数,那么我们的类都用改设计为虚函数。但是如果一个类中没有虚函数,那么这个类一般是不会用作为基类的,那么这时候我们就应该将该函数的析构函数设计为虚函数,因原创 2021-03-05 15:30:31 · 230 阅读 · 1 评论 -
《Effective C++》条款05、06(编译器自动生成的函数)
05 编译器自动合成的函数一般情况下,如果我们的类没有自定义的构造函数、拷贝构造函数、以及复制运算符重载,那么编译器就会为我们自动合成一个这些函数。因此当你写一个空类时,其实你的代码实际上是包含这些函数的,但是编译器将他隐藏了。当你需要用到这些函数时,编译器就会合成这些函数。含有引用 &和const成员变量会阻止合成赋值和拷贝在C++语言中,对于某个变量的引用,一旦我们确定了某个变量引用了某个对象,则这个引用将不能在指向其它对象。而对于const属性的成员,我们将不能对其赋值和修改,因此原创 2021-03-05 09:55:40 · 1160 阅读 · 5 评论 -
《Effective C++》条款04(确定对象被使用前已初始化)
在c++中对象在使用前应该初始化,对于初始化有两种一种是利用初始化列表初始化,另一种则是采用赋值的方式进行初始化,但是因为用列表初始化的效率会比用赋值符号初始化高效,所以,C++语言中建议用列表初始化完成对象的初始化。当用构造函数完成数据初始化时,应该确保每一个成员变量被初始化。如下的赋值方式:以上的方式不值得推崇,而使用成员列表初始化具有更高的效率,因为对于大多数的类型成员来说,先调用默认构造函数在调用赋值运算符的方式比单一的copy要低效。因此我们可以这样构造。对于没有用参数的构造函数,我们可原创 2021-03-05 09:05:12 · 157 阅读 · 1 评论 -
《Effective C++》条款03(尽可能的使用const)
条款03 尽可能的使用constconst出现形式const关键字形式多种多样,对于普通变量的使用其规则就很多,如下图所示:const关键字总结:const关键字出现在“ * ”号左边,表示被指对象为常量,如果const出现在“ * ”右边,表示指针自身为常量;如果const关键字即出现在“ * ”左边又出现在“ * ”右边,那么指针和被指对象都是常量。如下表示的指针情况相同:STL中的常量迭代器和指向常量的迭代器形如指针和const结合一样,迭代器也有两种版本,分别是指向常量的迭代器和常量原创 2021-03-04 11:08:32 · 237 阅读 · 1 评论 -
《Effective C++》条款01、02(以const,enume,inline,替换#define)
1.条款01:视C++语言为一个联邦C++语言的四大部分主要如下图所示:这也是对问题C语言和C++语言有什么不同? 的最佳回答之一。条款02:尽量以const,enume,inline,替换#define如下的一个定义:#define RATIO 1.6352对于预编译的的变量,并不会进入到符号表中,当程序报错时可能对反馈数据1.6352出错,而不是RATIO变量。所以C++语言中用以下语句代替。const double Ratio = 1.6352;作为常量,Ratio将会进入符号原创 2021-03-03 15:27:50 · 1159 阅读 · 2 评论