Effective C++
读后感。
加浓美式好喝
when you fall down,just try again.
展开
-
《Effective C++》条款44
将与参数无关的代码抽离templates存在这样一种类:template<class T,int n>class A{public: ... int func() { ... cout << n * n << endl; }};和这样的两种实例化对象:A<int, 5> a1;a1.func();A<int, 10> a2;a2.func();这些看起来非常合法,但是这会具现化两个类原创 2024-03-05 17:23:52 · 398 阅读 · 1 评论 -
解读<<effective c++>> 条款02
1.我们都知道,当我们在编译代码的时候,编译器会将多个.c文件形成.o文件,然后会将多个.o文件进行链接再形成可执行程序,那么在链接的时候,代码中的各种符号都被分配了地址,然后链接器会将这些写进符号表。2.上面那条语句的常量ASPECT_RATIO也会被写进符号表吗?答案是不一定。因为编译器会将所有的ASPECT_RATIO都用值1.653进行替换,所以这个宏名称也许从未被编译器看见,于是这个宏名称甚至可能没有进入符号表。3.那么会存在什么问题呢?原创 2023-05-12 21:26:52 · 195 阅读 · 1 评论 -
解读《effective c++》条款03
这样写还有一个好处就是给调用者提供了如何调用的思想,如果参数不是const,那么调用者可能会考虑我是否能放心地将值传给这个函数,有了const,调用者便无需多虑。有两个成员函数,一个是用const修饰的,一个是没有修饰的,类似函数重载,那么这两个对象会因为自身是否为常量来绝定它调用的函数。只需要记住,当const出现在*左边,表示被指物是常量,出现在*右边,表示指针自身是常量,在*两边,表示两者都是常量。以下函数的使用是一定会修改成员变量的值的,但是用const进行了修饰,程序会报错。原创 2023-05-12 23:56:31 · 98 阅读 · 0 评论 -
解读《Effective C++》条款04
以上两段代码存在于不同的编译单元,问题是:如果某编译单元内的某个非局部静态对象的初始化动作使用了另一编译单元内的某个非局部静态对象,它所使用到的这个对象可能尚未被初始化,如上两端代码,第二段代码直接调用了tfs的函数,但是不能保证在此之前,tfs已经被初始化了,因为。也就是非局部静态对象被局部静态对象替换了。这样修改之后,这个系统程序的客户完全像以前一样地用它,唯一不同的是他们现在用的tfs()和tempDir()而不再是tfs和tempDir了,而是指向局部静态对象的引用,不再是static对象本身了。原创 2023-08-28 20:10:22 · 102 阅读 · 0 评论 -
《Effective C++》条款05
当然,如果你自己手动写了这些编译器会生成的默认函数中的一种时,那么编译器将不会生成它。意味着如果你用心设计一个class,其构造函数要求实参,你就无需担心编译器会毫无挂虑地为你添加一个无实参构造函数。这种情况下,编译器将不会生成默认的赋值重载,所以a=b;这句代码会导致报错。原因是对象中的_s成员是引用,C++不允许让引用改指向不同的对象,而_val是const,更改const成员也是不合法的。一个空类的大小不会是0,如果写出了下面这个类,那么编译器也会自动生成一些函数。原创 2023-08-30 08:31:21 · 83 阅读 · 0 评论 -
《Effective C++》条款06
将拷贝构造函数和赋值重载私有,并且故意不实现它们。这样既防止了类外成员的拷贝,也防止了friend函数调用你的私有函数。原创 2023-08-30 09:29:11 · 160 阅读 · 0 评论 -
《Effective C++》条款07
问题就是delete ps的时候,SpecialString的析构函数没有被调用,导致了该资源没有被释放。那么很明显,子类的析构函数没有被调用,那么造成的结果就是子类的资源没有被释放,所以造成了。假设把所有类里的析构函数都写出虚函数,当然不止包括基类。把基类的析构函数写成virtual就可以解决上述问题了。所以需要把每个析构函数都写成virtual的吗?原创 2023-11-13 19:58:35 · 53 阅读 · 0 评论 -
《Effective C++》条款08
1.如果析构函数抛出异常,则异常点之后的程序不会执行,如果析构函数在异常点之后执行了某些必要的动作比如释放某些资源,则这些动作不会执行,会造成资源泄漏的问题。2.当异常发生时,C++的机制会调用已经构造对象的析构函数来释放资源,此时若析构函数本身也抛出异常,那么前一个异常还未处理,又来了新的异常。首先明确一个问题,为什么析构函数不能把异常抛出来?别让异常逃离析构函数。别让异常逃离析构函数。原创 2023-11-13 22:14:27 · 39 阅读 · 0 评论 -
《effective C++》条款09
原因就是我们构造这种继承关系的时候,会先构造父类,再构造子类,然而在父类的构造函数中,调用了虚函数,那么此时,子类还没有被创建出来,也就是说子类的资源为空,所以并不会打印b,c的值。把virtual函数置为non-virtual函数,并且将子类的数据通过构造函数传递给父类即可。2.运用了static,避免了使用到了初期未初始化的成员变量。原创 2023-11-14 17:13:41 · 51 阅读 · 0 评论 -
《effective C++》条款10
这个代码,很明显输出的是5。原创 2023-11-14 17:54:07 · 43 阅读 · 0 评论 -
《Effective C++》条款11
这段代码通过delete之前的资源,并且new一个新的对象,返回这个对象,达到赋值的功能,乍一看没什么问题,但是如果是自我赋值呢?那么提前delete会造成空指针问题。通过创建临时对象,当作副本。在operator=中处理“自我赋值”在operator=中处理“自我赋值”就在一开始进行判断即可。原创 2023-11-14 18:15:06 · 39 阅读 · 0 评论 -
《Effective C++》条款12
我们可以调用B的拷贝构造函数进行初始化对象,但是实际上,我们并没有初始化A类的成员,只是因为我们自行写了对应的函数,如果是编译器生成的默认函数,那么会帮我们考虑到继承下来的A类的成员。我们都知道,我们不主动写拷贝构造函数或者赋值重载运算符,那么编译器就会自动生成默认的函数。当我们写时,编译器不会自动生成,它只会找对应我们写的函数,如果此时我们写的函数出现了问题,那编译器可不会处理。而且,我们在这个类里新增了一个成员变量,但是没有及时修改我们写的运算符,那么程序可能会出现错误。复制对象时勿忘其每一个成分。原创 2023-11-15 21:44:15 · 130 阅读 · 0 评论 -
《Effective C++》条款13
但是auto_ptr 的劣势也很明显:如果它被拷贝了并且同时指向一块内存,当其中一个指针释放这块资源时,另一个指针就会变成野指针。我们定义了ptr去接收create()函数的返回值,并且在最后进行了回收资源。利用计数的思想,当多个指针同时管理一块资源时,每释放一个指针,计数减一,直到0便释放资源。避免了野指针的问题。2.delete语句位于某个循环内,也许会进行过早的goto或者break。1.delete前的...或许有一个过早的return语句。3.delete前的...或许有一个异常抛出。原创 2023-11-15 21:59:12 · 118 阅读 · 0 评论 -
《Effective C++》条款14
一般的操作是引入智能指针shared_ptr。但是在这个例子中,我们往往想的是在出了对象作用域后进行解锁而不是直接删除该对象,但是智能指针是直接删除。所以我们需要允许指定所谓的“删除器”,那是一个函数或者函数对象,当引用次数为0时便调用。某些罕见场合下你可能希望确保永远只有一个RAII对象指向一个未加工资源,即使RAII对象被复制依然如此。此时资源的拥有权会从被复制物转移到目标物。此时这个类并不用声明析构函数,因为没有必要。析构函数会在互斥器的计数为0的时候自动调用shared_ptr的删除器。原创 2023-11-15 22:28:54 · 130 阅读 · 0 评论 -
《Effective C++》条款15
但是显示转换却显得异常麻烦,所以我们需要隐式转换,这样对于用户而言会更方便一点。因为day函数要求的参数是指针,而你传的实际上是一个对象。在资源管理类中提供对原始资源的访问。在资源管理类中提供对原始资源的访问。为什么建议用隐式类型转换?函数就可以显示转换类型。调用了智能指针提供的。原创 2023-11-16 23:01:46 · 660 阅读 · 0 评论 -
《Effective C++》条款16
很明显你开辟了100个空间,但是却只析构了一次,造成了内存泄漏。成对使用new和delete时要采取相同形式。成对使用new和delete时要采取相同形式。原创 2023-11-17 16:37:47 · 37 阅读 · 0 评论 -
《Effective C++》条款17
所以,出现问题的可能之一是对g()函数的调用出现了异常,那么”new A"的指针会被遗漏,造成了内存泄漏。假如你想通过主函数里的语句进行调用f函数。3.调用shared_ptr构造函数。1.执行“new A"原创 2023-11-17 16:54:26 · 82 阅读 · 0 评论 -
《Effective C++》条款20
除此原因以外还有什么情况下建议使用pass-by-reference-to-const?宁以pass-by-reference-to-const替换pass-by-value。宁以pass-by-reference-to-const替换pass-by-value。2.因为存在继承关系,所以每次构造一个B类,就会多出来4个string对象。这段代码用了两个不同参数的getvalue函数,结果也不一样!所以得出结论:想要传递派生类对象的信息,需要使用。这样明显不是最佳效率的调用方法。还可以避免对象切割问题!原创 2023-11-17 18:53:15 · 89 阅读 · 0 评论 -
《Effective C++》条款21
同时就需要两次delete才能保证内存不泄露,但是却没有合理的办法让operator*使用者进行那些delete调用,因为没有合理的办法让他们取得operator*返回的引用指向一个被定义于函数内部的static A对象。但是这样写是错误的,一旦使用引用返回前,一定要想清楚当前引用的对象在哪,是谁,而函数内的对象是在stack上创建的,一旦函数栈帧被销毁,那么该stack上的值也不复存在。必须返回对象时,别妄想返回其reference。必须返回对象时,别妄想返回其reference。原创 2023-11-17 20:33:39 · 112 阅读 · 0 评论 -
《Effective C++》条款23
因为非成员函数比成员函数具有更高的封装性。我们从封装开始讨论,如果某些东西被封装,它就不再可见,愈多东西被封装,愈少人可以看到他,我们就有愈大的弹性去改变它,因为我们的改变仅仅直接影响到改变的那些人事物。比较自然的做法是让allways()成为一个non-member函数并且位于A类所在的同一个namespace中。宁以non-member、non-friend替换member函数。宁以non-member、non-friend替换member函数。愈多函数访问它,它的封装性就愈低。原创 2023-11-18 18:32:17 · 137 阅读 · 0 评论 -
《Effective C++》条款24
而且如果把构造函数加上explicit,禁止了它的隐式类型转换,那么两者都不行。所以,让它成为一个non-member函数是个不错的选择。若所有参数皆需类型转换,请为此采用non-member函数。若所有参数皆需类型转换,请为此采用non-member函数。这里发生了隐式类型转换,把3转成了A对象。原创 2023-11-28 23:29:03 · 420 阅读 · 0 评论 -
《Effective C++》条款25
如果想调用std::swap去置换B对象,那么我们想做的就是置换其A指针,但是缺省的swap函数并不知道。它不只复制了三个B类对象,还复制了三个A类对象,非常影响效率!这种做法不仅能通过编译,还与STL容器有一致性,因为所有的STL容器也都是供有public swap成员函数和std::swap特化版本(用以调用前者)但是问题又来了:因为std是个特殊的命名空间。客户可以全特化std内的templates,但是不可以添加新的templates进去。思路是对的,但是这样写会报错,因为访问到了私有变量。原创 2023-11-29 23:14:21 · 358 阅读 · 0 评论 -
《Effective C++》条款26
你不只应该延后变量的定义,直到非得使用该变量的前一刻为止,甚至应该尝试延后这份定义直到能够给它初值实参为止。如果这样, 不仅能够避免构造和析构非必要对象,还可以避免毫无意义的default构造行为。但是这段代码仍然不够秾纤合度,因为s虽获定义却无任何实参作为初值。许多时候你该对对象做的第一次事就是给它个值,通常是通过一个赋值动作达成。这段代码的问题是:如果抛出了异常,那么定义的string对象将面临毫无意义的构造和析构。更受欢迎的是以passwd作为s的初值,跳过毫无意义的default构造过程。原创 2023-11-30 21:53:18 · 444 阅读 · 0 评论 -
《Effective C++》条款27
(成员函数只有一份,关键在于成员函数都有一个this指针,会因此影响成员函数操作的数据。)如果add修改了当前对象的内容,当前对象其实没有被改动,被改动的是副本。然而B类里面的add如果也修改对象,那么该对象会被改变。这使当前对象进入一种“伤残”状态:其base class成分的更改没有落实,而derived class成分的更改倒是落实了。如上描述把子类转型为A类,调用了A::add()。这两种做法都可以替代dynamic_cast。假设子类有一个唯一的函数func。原创 2023-11-30 22:52:51 · 546 阅读 · 0 评论 -
《Effective C++》条款28
因为left()的调用者能够使用被返回的引用来更改成员。但是rec应该是不可变的。所以返回引用的时候一定要注意调用者是否会修改本不应该修改的数据。3.引用返回要三思而后行。但是不代表不能使用,比如operator[] 在vector或者string等中就需要引用返回。1.返回引用前确保该数据是存在可以被修改的可能性的并且不会导致程序或者数据错误。2.如果待引用返回的数据在出了函数作用域前就被销毁了,那么程序会引发崩溃。避免返回handles指向对象内部成分。避免返回handles指向对象内部成分。原创 2023-12-01 23:14:23 · 370 阅读 · 0 评论 -
《Effective C++》条款29
如果异常被抛出,程序状态不改变。调用这种的函数需要有这样的认知:如果函数成功,就是完全成功,如果函数失败,程序会回复到“调用函数之前”的状态。:如果异常被抛出,程序内的任何事物仍然保持在有效状态下。没有任何对象或数据结构会因此而败坏,所有对象都处于一种内部前后一致的状态。2.如果new A抛出异常了,那么ptr就是指向一个已经被删除的对象,count已经被累加,实际并没有意义。1.如果new A抛出异常了,那么unlock将永远不会执行。:承诺绝不抛出异常,因为它们总是能够完成它们预先承诺的功能。原创 2023-12-05 23:18:47 · 406 阅读 · 0 评论 -
《Effective C++》条款30
虽然或许你的构造函数是空的,看起来很简单适合inlining,但是C++对于“对象被创建和销毁时发生了什么事”做了各种各样的保证。如果期间有异常被抛出,该对象已经构造好的那一部分会被自动销毁......你的程序内一定有某些代码让那些事情发生,而那些代码——7.inline函数无法随着程序库的升级而升级。inline函数被编进程序中,如果程序库设计者决定改变它,那么所有用到它的客户端程序都得改变,必须重新编译。1.inline对编译器只是一个建议,并不会强制执行。2.成员函数默认被打上了inline的标签。原创 2023-12-06 13:35:48 · 373 阅读 · 0 评论 -
《Effective C++》条款33
这种情况下,Base类内的函数与Derived类内的函数构成隐藏,所以d.f1(2)无法调用到Base类内的函数。加上using声明式就解决了,构成了函数重载。局部域会掩盖全局域,所以会输出0.5。避免遮掩继承而来的名称。避免遮掩继承而来的名称。原创 2023-12-06 23:15:14 · 430 阅读 · 0 评论 -
《Effective C++》条款34
声明简朴的impure virtual函数的目的,是让derived classes继承该函数的接口和缺省实现。意味着如果你不想自己写一个,可以使用基类的缺省版本。声明一个pure virtual函数的目的是为了让derived class只继承函数接口。令人意外的是,我们可以为pure virtual提供定义。成员函数的接口总是会被继承。原创 2023-12-18 11:35:01 · 325 阅读 · 0 评论 -
《Effective C++》条款35
这种手法称为NVI手法,在调用Virtual函数前做一些事前工作,包括锁定互斥器,制造运转日志记录项,验证class约束条件,验证函数先决条件等。事后工作包括互斥器的解除锁定,验证函数的事后条件,再次验证class的约束条件等。Func类作为基类,多个函数可以继承Func类,构造A类对象时可以传入不同的函数类,A类便可以进行多态调用不同的函数。这样创建不同的对象就可以传入不同的函数,而且函数可以在运行期变更。假如A提供一个函数,用来替换当前的函数。考虑virtual函数以外的其他选择。原创 2023-12-18 16:37:59 · 430 阅读 · 0 评论 -
《Effective C++》条款37
我们预期输出的结果是2,但是实际输出是1。那是因为虽然函数是动态绑定的,但是缺省参数不是。因为如果缺省参数是动态绑定,编译器就必须有某种办法在运行期为virtual函数决定适当的参数缺省值。影响效率,所以编译器索性不干。所以缺省参数都是静态绑定的。绝不重新定义继承而来的缺省参数值。绝不重新定义继承而来的缺省参数值。解决方案: (利用NVI手法)原创 2023-12-18 20:44:33 · 466 阅读 · 1 评论 -
《Effective C++》条款39
如果A继承B,当A被编译时B的定义必须可见,所以定义A的那个文件必须#include"B.h" , 但是如果AB移出A之外而A内含指针指向一个AB,A可以只带着一个简单的AB声明式,不再需要#include任何与B有关的东西。当你想重写A内的虚函数而A和B并不构成is-a的关系时,public继承并不是一个好方法。1.private继承一般用于复用的场景,当derived class需要访问protected base class的成员,或需要重新定义继承而来的virtual函数时,很合理。原创 2023-12-20 00:22:59 · 362 阅读 · 0 评论 -
《Effective C++》条款40
所以,尽量别使用virtual继承,或者不要在virtual base classes中放数据,这样就不用担心这些classes身上的初始化和赋值所带来的诡异事情了。使用virtual继承的那些classes所产生的对象往往比使用non-virtual继承的兄弟们体积大,访问virtual base classes的成员变量时,也比访问non-virtual base classes的成员变量速度慢。当出现将“public继承自某接口”和“将private继承自某实现”结合在一起时。原创 2023-12-20 14:34:57 · 367 阅读 · 0 评论