随笔 - 576  文章 - 0  评论 - 62  阅读 - 219万

随笔分类 -  Effective C++

1 2 3 下一页
【48】认识template元编程
摘要:1、TMP(template metaprogramming),模版元编程有两个效力:第一,它让某些事情更容易;第二,可将工作从运行期转移到编译期。 阅读全文
posted @ 2014-02-10 20:18 Andy Niu 阅读(326) 评论(0) 推荐(0) 编辑
【44】将与参数无关的代码抽离templates
摘要:1、template是产生代码的代码,这就意味着源码看起来很少,生成的目标码大量膨胀。2、考虑,如果两个方法有重复代码,我们会新建一个方法,把重复的代码放进去,原先两个方法调用第三个方法。如果两个类有重复代码,我们把重复代码移到一个新的类中,通过继承或者复合,避免重复。3、但是在template中,这种重复是隐晦的,只有一份template代码,可能会生成一系列重复的代码。考虑矩阵模板类,有两个模版形参:矩阵元素的类型T和矩阵的大小S。4、SquareMatrix 和SquareMatrix 会实例化两个模板类,这两个模板类有大量重复代码,比如:invert方法。5、如何解决呢? 建立一个模. 阅读全文
posted @ 2014-02-10 20:03 Andy Niu 阅读(825) 评论(0) 推荐(0) 编辑
【23】宁以non-member、non-friend替换member函数
摘要:1、non-member方法与member方法没有本质区别,对于编译器来说,都是non-member方法,因为member方法绑定的对象,会被编译器转化为non-member方法的第一个形参。non-member方法与member方法唯一的区别是:member方法封装性更差,因为它可以访问private成员。2、根据面向对象的要求,数据与方法应该和对象捆绑在一起,这意味着应该使用member方法。其实,这个建议是错误的。为什么?3、首先,non-member、non-friend方法提供更大的封装性。4、其次,考虑下面的需求,我只需要类中的一个方法。如果是member方法,必须把整个class 阅读全文
posted @ 2014-02-10 19:16 Andy Niu 阅读(1024) 评论(0) 推荐(0) 编辑
【22】将成员变量声明为private
摘要:1、为什么要将成员变量声明为private,语法一致性,只通过方法暴露接口。2、使用方法,可以对成员变量更精确的控制。比如:为所有可能的实现提供弹性,不同实现可以替换;控制可读可写;验证约束条件;处理事前和事后状态;在多线程环境执行同步控制。3、public会破坏成员变量的封装性,程序中到处都可修改成员变量。将成员变量声明为private,暴露一个方法接口,可以统一管理。4、对于成员变量,protected并不比public更具封装性。 阅读全文
posted @ 2014-02-10 18:41 Andy Niu 阅读(519) 评论(0) 推荐(0) 编辑
【21】必须返回对象时,别妄想返回器reference
摘要:1、考虑有理数Rational,有个友元操作符*,返回Rational对象。返回对象,导致临时对象的构造,析构。效率低,因此会想返回方法内局部对象的引用,这种方法不可行。为什么?2、调用方法是在stack上执行,方法执行完毕,局部对象销毁。假如返回方法内局部对象的引用,方法执行完,局部对象销毁,这时候,引用指向一堆垃圾。3、你可能会想,我在heap上构造一个对象,返回引用。这也不可行,首先,要求客户端负责delete,这不合理。其次,退一步说,就算客户负责,执行delete。但是有些情况,客户无法执行delete。比如:Rational d = a*b*c; a*b的结果没有暴露出来,客户无法 阅读全文
posted @ 2014-02-10 18:21 Andy Niu 阅读(303) 评论(0) 推荐(0) 编辑
【51】编写new和delete时需固守常规
摘要:1、【50】讲了,有很多理由需要写个自定义的new/delete,自定义new/delete的时候,需要遵守一些规则。2、循环申请,直到成功或者抛出异常,如下: 1 void* operator new (std::size_t size) throw() 2 { 3 if(size=0) 4 { 5 size =1; 6 } 7 while (true) 8 { 9 if(分配成功)10 {11 ... 阅读全文
posted @ 2014-01-24 19:57 Andy Niu 阅读(241) 评论(0) 推荐(0) 编辑
【50】了解new和delete的合理替换时机
摘要:1、有时候,我们替换掉编译器提供的new或者delete。首先思考,为什么想要替换?下面是三个常见理由: a、用来检测运用上的错误,超额分配一些内存,再额外的空间放置一些内存; b、为了强化效能,编译器提供的new/delete是通用的,通用就意味着冗余和效率低下,为什么?这个很好理解,因为他要支持很多情况下,也必须考虑很多情况。我们重写new/delete,也就是说,对于特定情况,给出特定的实现。 c、为了收集使用上的统计数据。 阅读全文
posted @ 2014-01-24 19:39 Andy Niu 阅读(482) 评论(0) 推荐(0) 编辑
【49】了解new_handler的行为
摘要:1、使用operator new无法获取内存时,对于旧式编译器,会返回一个null指针。对于新式编译器,会抛出一个异常。2、考虑下面的需求,当operator new 无法获取内存时,程序员期望获得通知,并调用一个方法。也就是说,向系统传递一个回调方法。这就是new_handler: typedef void (* new_handler) (void); new_handler是一个指针方法,形参为空,没有返回值。 new_handler set_new_handler(new_handler p) throw (); set_new_handler 方法就是传递一个回调方法,返回老的... 阅读全文
posted @ 2014-01-24 19:18 Andy Niu 阅读(381) 评论(0) 推荐(0) 编辑
【52】写了placement new也要写placement delete
摘要:1、Widget* pw = new Widget; 调用了两个方法:第一个方法是operator new 负责分配内存;第二个方法是在分配的内存上构造Widget,即调用Widget的default构造方法。2、那么问题来了,如果operator new分配内存成功,而Widget构造抛出异常,这种情况下,pw还没有赋值,这不就导致资源泄露了? 不会造成资源泄露,因为C++运行系统保证调用delete释放资源。3、正常的operator new 如下: void* operator new(std::size_t) throw (std::bad_alloc); 正常的operator... 阅读全文
posted @ 2014-01-24 18:44 Andy Niu 阅读(459) 评论(0) 推荐(1) 编辑
【45】运用成员函数模版接受所有兼容类型
摘要:1、所谓智能指针,就是资源管理类,行为像指针。因为要对不同的类型的指针进行封装,所以智能指针是类模版。2、那么问题来了。对于原始指针,支持隐式类型转换,也就是说,父类指针可以指向子类指针。因此,我们当然期望,父类智能指针可以指向子类智能指针,但是,不同Base和Derived实例化出来的智能指针,是不同的类型,之间没有任何关系,更谈不上继承关系,肯定不能赋值。3、怎么解决呢? 提供一个成员方法模版,对兼容的类型进行构造或者赋值。也就是copy构造模版和copy赋值模版。4、这里出现了一个问题,以copy构造模版为例,当copy构造模版的形参与类模版的形参一致,那么就退化为普通的copy构造,. 阅读全文
posted @ 2014-01-23 19:36 Andy Niu 阅读(437) 评论(0) 推荐(0) 编辑
【46】需要类型转换时请为模版定义非成员函数
摘要:1、假设【24】中的Rational是类模版,同时有non-member的方法模版,现在考虑下面的调用: Rational a(1,2); Rational result = a*2; // Error2、为什么? 调用方法的时候,如果没有完全匹配的方法,编译器会尝试进行隐式类型转换。现在考虑模版的实例化,调用方法的时候,编译器根据模版实参,确定模版形参,然后实例化一个方法。但是,实例化方法的时候,从不进行隐式类型转换。思考为什么?假如考虑隐式类型转换,那么存在隐式类型转换的情况多了,不可能都去实例化。也就是说,模版实例化的时候,不考虑隐式类型转换,因此也就没有实例化方法。没有生成方法,... 阅读全文
posted @ 2014-01-23 18:54 Andy Niu 阅读(314) 评论(0) 推荐(0) 编辑
【24】若所有参数皆需类型转换,请为此采用non-members函数
摘要:1、令class支持隐式类型转换,往往是个糟糕的主意。但有些情况是合理的,比如数值类型。考虑,有理数Rational有分子,分母两个字段,缺省参数值为0,1。Ration a = 2;我们期望构造一个分子为2,分母为1的有理数,这是非常合理和自然的。因此,Rational的构造方法为Ration(int numerator =0, int denominator =1);不添加explicit。2、考虑Rational 有个成员方法 operator*,如下: const Ration operator*(const Rational& rhs) const; Rational a ( 阅读全文
posted @ 2014-01-23 18:33 Andy Niu 阅读(584) 评论(0) 推荐(0) 编辑
【55】让自己熟悉Boost
摘要:1、网址:http://boost.org2、有很多C++组织和网站,但是Boost库有两个优势:a、和标准委员会关系密切;b、加入C++标准的各种功能的测试场。 阅读全文
posted @ 2014-01-23 17:59 Andy Niu 阅读(148) 评论(0) 推荐(0) 编辑
【54】让自己熟悉包括TR1在内的标准程序库
摘要:1、C++0X,不确定哪一年出来,意指200X版的C++2、C++标准程序库的主要机能有:STL,iostreams,locals等。3、TR1:Technical Report 1,只是一份规范。添加了智能指针,泛型方法指针,正则表达式等内容。 阅读全文
posted @ 2014-01-23 17:55 Andy Niu 阅读(337) 评论(0) 推荐(0) 编辑
【53】不要轻忽编译器的警告
摘要:1、严肃对待编译器发出的警告信息,因为编译器作者对所发生的事情有更好的领悟,尽量争取“无任何警告”。2、不要过度依赖编译器的警告,因为不同编译器对待事情的态度不同。 阅读全文
posted @ 2014-01-23 17:49 Andy Niu 阅读(157) 评论(0) 推荐(0) 编辑
【31】将文件间的编译依存关系降至最低
摘要:1、一般情况下,使用一个东西,只要说我有这个东西就行了,不需要定义。但是,对于类,必须要有定义,为啥? 因为创建对象,需要知道分配多大空间,这种情况,必须询问类的定义式,才知道。2、怎么解决上面的问题? 问题的关键是,必须知道对象占用多大空间。可以这样做,使用指向实现的类(pimpl idom)。包含一个指向对象的指针,这种情况,不需要直到对象占用多大空间。3、声明一个方法的时候,对于方法形参中的对象,不需要include类的定义,为什么? 这个事情是在调用端做的,方法声明端不需要inlucde。4、还有一种办法是,采用接口,接口中没有字段。 阅读全文
posted @ 2014-01-21 19:54 Andy Niu 阅读(809) 评论(0) 推荐(0) 编辑
【30】透彻了解inlining 的里里外外
摘要:1、inline方法相当于文本替换,不需要承担方法调用的额外开销,同时还有潜在的优势,文本替换后,编译器会进行代码优化。而对于方法调用,编译器没有能力进行代码优化。2、显而易见,inline方法往往会导致目标代码膨胀变大。但是,对于方法本体很小的情况,可能会出现,替换后的文本比方法调用的代码还要小。这也意味着,一般情况下,只有方法本体比较小的情况,才应该声明为inline。3、特别注意:inline只是一个申请,而不是命令。同时,对于class定义中的方法实现,也暗示着申请inline。申请inline告诉编译器,进行文本替换,但最终能不能文本替换,还要取决于编译器。4、编译器拒绝对业务复杂( 阅读全文
posted @ 2014-01-21 19:37 Andy Niu 阅读(400) 评论(0) 推荐(0) 编辑
【29】为“异常安全”而努力是值得的
摘要:1、异常安全(出现异常,保证安全)有三个保证: a、基本承诺:抛出异常,对象处于有效状态下; b、强烈保证:抛出异常,对象要么修改成功,要么会滚到初始状态,可认为操作的原子行; c、不抛出保证:保证不抛出异常。2、为了异常安全,常常需要使用资源管理类shared_ptr。为了做到强烈保证,即操作的原子性。常常使用策略:copy and swap。考虑指向实现的类(pimpl idom),修改这样对象使用copy and swap策略:首先对对象做个copy副本,修改副本,然后交换指针。3、异常安全保证,符合木桶理论的短板效应,异常安全保证等于最弱者。 阅读全文
posted @ 2014-01-21 19:04 Andy Niu 阅读(182) 评论(0) 推荐(0) 编辑
【26】尽可能延后变量定义式的出现时间
摘要:1、定义一个对象,往往意味着必须承担构造成本,离开作用域,必须承担析构成本。即使这个对象没有使用过。2、你可能会认为,不会定义一个不使用的对象。考虑下面的情况下,定义一个对象,执行其他操作,然后使用对象,假如在执行其他操作的时候,出现异常,就导致构造和析构了一个没有使用的对象,解决办法是:一个对象的定义和使用,放在一起,中间尽量不要插入其他操作。也就是说,等到真正使用的时候,才去定义。3、如果当前不知道对象的初始值,就不要定义,不然会导致一次copy构造,一次copy赋值。而应该等到确定初始值的时候,直接使用初始值去copy构造。4、当出现循环怎么办?放在循环外,还是放在循环内? 放在循环外. 阅读全文
posted @ 2014-01-21 18:38 Andy Niu 阅读(649) 评论(0) 推荐(0) 编辑
【39】明智而审慎第使用private继承
摘要:1、private继承意味着,根据某物实现出,继承父类的实现,关闭父类的接口,并不是Is-A的关系,不满足里氏代换,继承的内容访问权限都修改为private。2、那么问题来了,复合也表达根据某物实现出,那到底使用private继承还是复合呢?3、优先使用复合,除非是下面的情况:a、使用对象的protected成员;b、想要重新定义virtual方法。4、考虑下面的极端情况,有个empty类,占用空间大小为0,也就是说,没有字段和virtual方法,如果使用继承,子类大小不增加。如果使用复合,C++规定大小为0的独立对象,插入一个char到空对象,考虑到对齐,实际占用int。也就是说,在这种极端 阅读全文
posted @ 2014-01-21 18:18 Andy Niu 阅读(231) 评论(0) 推荐(0) 编辑

1 2 3 下一页
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示