【15】在资源管理类中提供对原始资源的访问
摘要:1、为什么要这样做? 因为有的接口需要原始资源,比如方法void DoSth(const Person* p); 而现在我有一个shared_ptr person_ptr; 调用DoSth(person_ptr)编译失败,因为方法要一个Person指针,而person_ptr是一个对象。那怎么办?只能让资源管理类暴露资源。2、怎么暴露呢?两种办法:一种是提供显式方法,一种是隐式类型转换操作符:operator Person* () const ; 好像是一种特殊的方法:没有返回值,没有形参,重载了返回值的类型。根据准则:让接口容易被正确使用,不易被误用。应该使用显式方法,因为显式方法明确告诉.
阅读全文
posted @
2014-01-09 19:56
Andy Niu
阅读(353)
推荐(0) 编辑
【14】在资源管理类中小心copying行为
摘要:1、为什么要使用资源管理类? 资源管理类的思路就是,栈上的对象,封装堆上分配的资源,确保一定会释放资源。auto_ptr和shared_ptr就是资源管理类,行为上像指针。2、auto_ptr和shared_ptr用来管理,堆上分配的内存,在auto_ptr和shared_ptr的析构方法中,执行delete。对于其他资源,auto_ptr和shared_ptr并不适合作为资源管理类。考虑互斥锁mutex,有普通方法lock和unlock,提供一个资源管理类Lock,管理mutex。资源管理类Lock被复制,将发生什么情况?该怎么办? a、禁止复制; b、采用引用计数法,也就是share...
阅读全文
posted @
2014-01-09 19:33
Andy Niu
阅读(415)
推荐(0) 编辑
【02】尽量以const,enum,inline替换#define
摘要:1、考虑为什么? 首先,#define不是语言的一部分,而是预编译过程。也就是在编译器编译之前,进行文本替换。考虑#define Pi 3.1425;在编译之前,Pi都会被文本替换为3.1415,因此编译器看不到Pi这个标识。这会增加调试难度。同时文本替换导致目标码中出现多份3.1415。2、尽量不使用#define,就要使用替代办法。3、对于#define Pi 3.1415这样形式,使用const替换const double Pi = 3011415;以const替换#define,有两个特殊情况: a、定义常量指针,使用两个const,限制指向对象不能修改,指向不能修改; b、cl...
阅读全文
posted @
2014-01-08 19:45
Andy Niu
阅读(300)
推荐(0) 编辑
【01】视C++为一个语言联邦
摘要:1、C++是个多重范型编程语言:面向过程,面向对象,函数编程,泛型形式,元编程形式。2、C++是一个语言联邦,包括四个次语言: a、C语言,C++以C语言为基础。但C语言有下列局限:没有模版,没有异常,没有过载。 b、面向对象C++,包括继承,封装,多态,抽象。 c、Template C++,泛型编程,可以生成代码的代码。 d、STL,标准库容器,迭代器,算法,方法对象。
阅读全文
posted @
2014-01-08 19:12
Andy Niu
阅读(273)
推荐(0) 编辑
【13】以对象管理资源
摘要:1、为什么要以对象管理资源,它解决什么问题? 考虑下面的需求,客户需要一个动态分配的资源。我提供一个工厂方法,返回一个指针,指向资源。那么问题来了,谁来释放资源呢?只有客户知道什么时候不再使用资源,因此,应该是客户负责释放资源。如果客户没有成功释放资源,造成资源泄漏,注意,这里泄漏的资源不光是一块内存,还可能包括这块内存保存的其他资源。把成功释放资源的希望,寄托在客户身上,显然是不靠谱的。因为:a、客户可能忘记delete;b、代码异常,导致没有执行到delete。2、它是如何解决这个问题的? 我们知道,栈上分配的对象有个特点,超出作用域,不管什么情况,都会自动调用析构方法。因此,可以把堆..
阅读全文
posted @
2014-01-08 19:04
Andy Niu
阅读(238)
推荐(0) 编辑
【20】宁以pass-by-reference-to-const替换pass-by-value
摘要:1、首先理解需求,被调用方法修改了形参,如果期望在主调方法中的实参也发生变化,必须使用pass-by-reference。因为C++缺省情况下(继承C方式),以by-value传递对象,在被调方法中修改的是实参的副本。2、在被调方法中,必须修改形参,但是期望主调方法中的实参不发生变化,这种情况下,必须使用pass-by-value。3、在被调方法中,不修改形参。应该使用pass-by-reference-to-const替换pass-by-value(有例外情况)。为什么? a、因为pass-by-value的成本大。要创建新对象,调用构造方法和析构方法。主要有:父类构造方法-->父类字
阅读全文
posted @
2014-01-07 18:57
Andy Niu
阅读(299)
推荐(0) 编辑
【40】明智而审慎地使用多重继承
摘要:1、多种继承,首先带来一个问题。那就是子类会从多个父类中继承相同的名称。这样,在调用的时候,必然导致歧义。2、考虑Base1中有个public方法XXX,Base2中有个private方法XXX,Derived继承Base1和Base2,那么分析一下,Derived d; d.XXX()到底调用哪个方法? 你可能会想,虽然两个都匹配,但是只有Base1的XXX方法可访问,因此调用Base1的XXX方法。这种想法是错的,编译报错:对XXX的访问不明确。为什么? 因为编译器决议调用哪个方法的时候,第一步,先把所有匹配的名称都拿来,不管是不是可以访问,然后在里面找到一个最优匹配,如果两个匹配程度..
阅读全文
posted @
2014-01-03 21:18
Andy Niu
阅读(425)
推荐(0) 编辑
【32】确定你的public继承塑模出Is-A关系
摘要:1、public继承表示Is-A关系,也就是满足里氏代换。与之相对应的,private继承表示根据某物实现出,不满足里氏代换。子类对象初始化父类引用,编译通不过。2、考虑下面的需求,企鹅继承鸟,Bird暴露virtual Fly接口。现在出问题了,因为Penguin不会飞,不提供Fly接口。怎么办? a、Penguin在重写的Fly方法中,抛出一个异常。 b、上面的解决办法不好,因为它表示的意义不是说Penguin不会飞,而是说尝试让Penguin飞,会出现错误。最大的问题是:运行期才发现错误。我们知道:好的接口可以防止无效的代码通过编译。现在思考,解决解决这个问题。这个问题的关键是:并不..
阅读全文
posted @
2014-01-02 21:02
Andy Niu
阅读(379)
推荐(0) 编辑
【10】令operator=返回一个reference to *this
摘要:1、令operator= 返回一个reference to *this,为什么? 这只是一个协议,并无强制性。但是,为了与基本类型的行为保持一致性,强烈建议这么做。设计class 有一个宝典:一旦有疑惑,请参考int类型的行为,并遵循之。2、不光operator=,与它类似的赋值相关运算,也应该遵循该协议。
阅读全文
posted @
2013-12-31 19:40
Andy Niu
阅读(211)
推荐(0) 编辑
【34】区分接口继承和实现继承
摘要:1、接口继承和实现继承表示的意义 接口继承:父类提供的接口,子类仍然提供。 实现继承:子类复用父类的代码实现。2、子类继承父类,可分为下列情况: a、继承接口,继承实现---对应non-virtual方法。 b、继承接口,继承缺省实现,允许子类修改实现---对应virtual方法。 c、只继承接口---对应pure virtual方法,父类只提供接口,强制子类提供实现。 b、关闭接口,继承实现:private继承,表示根据某物实现出,子类关闭接口。3、现在,考虑下面的场景:父类有个virtual方法XXX(),目的是让子类继承缺省实现,允许子类修改。现在有个子类,应该重写XXX,但...
阅读全文
posted @
2013-12-31 19:20
Andy Niu
阅读(453)
推荐(0) 编辑
【33】避免遮掩继承而来的名称
摘要:1、首先讲讲作用域,内层作用域的名称会遮掩外层作用域相同的名称。而且遮掩规则是:只看名称,不关心名称的类型。也就是说,内层double类型的x会遮掩外层int类型的x。2、继承有两层含义:从行为和状态看,子类更加具体化;从范围看,父类是大范围,子类是小范围,可以认为子类作用域嵌套在父类作用域中。3、那么问题来了,子类作用域嵌套在父类作用域中,根据上面的遮掩规则,子类名称会遮掩父类的名称,不关心名称的类型。4、这也就意味着,子类不能过载父类的方法,因为会遮掩。如果确实要过载父类的方法,怎么办呢? 使用using Base::XXX。明确告诉编译器,我要继承父类的XXX方法(这个XXX名称对应着.
阅读全文
posted @
2013-12-31 18:30
Andy Niu
阅读(357)
推荐(0) 编辑
【37】绝不重新定义继承而来的缺省参数值
摘要:1、由于【36】绝不重新定义继承而来的non-virtual方法,现只考虑带有缺省参数值的virtual方法。2、为什么绝不重新定义继承而来的缺省参数值? 缺省参数值是静态绑定,virtual方法是动态绑定。现在考虑父类virtual方法带有缺省参数值,子类重写了virtual方法,父类指针指向子类对象,调用virtual方法,导致的结果是:用父类的缺省参数值初始化形参,调用子类重写的方法实现。这肯定不是程序员预期的结果。3、怎么解决这个问题? 使用NVI,父类通过non-virtual方法调用virtual方法,父类non-virtual方法使用缺省参数值,父类virtrual方法不使用..
阅读全文
posted @
2013-12-27 19:01
Andy Niu
阅读(404)
推荐(0) 编辑
【36】绝不重新定义继承而来的non-virtual方法
摘要:1、绝不重新定义继承而来的non-virtual方法,为什么? 首先想想,non-virtual方法是干什么的?也就是说,它的使用场景。父类的non-virtual方法,其实就是告诉子类,继承实现,子类决定是否继承接口(也就是子类决定是否继续提供接口)。如果重新定义了non-virtual方法,就不符合它的使用场景。2、如果想重新定义继承而来的方法,应该把父类方法修改为virtual方法,因为virtual方法的使用场景是:父类告诉子类,继承接口,修改实现。3、如果重新定义了继承而来的non-virtual方法,会出现什么问题呢? 因为non-virtual方法是静态绑定,也就是说,调用n..
阅读全文
posted @
2013-12-27 18:42
Andy Niu
阅读(342)
推荐(0) 编辑
【38】通过复合塑模出Has-A 或根据某物实现出
摘要:1、什么是复合? 复合是类型之间的一种关系,某种类型的对象内含其他类型的对象。2、为什么需要复合,他解决什么问题? 为了代码复用。3、复合有两层含义:Has-A和根据某物实现出。在应用域中,表示Has-A,比如:Person有一个地址,有一个所属公司。在实现域,表示根据某物实现出,比如缓冲区,互斥器等概念。4、复合可以分为:关联,聚集,组合。关联是1对1的关系,聚集和组合是多对1的关系。组合比聚集的关系更紧密,举例来说,大雁聚集成雁群,两个翅膀(和其他的部位)组合成大雁。5、关联与依赖有区别,关联表示内含一个对象,依赖表示执行一个动作的时候,依赖形参。比如:Person关联地址,Perso..
阅读全文
posted @
2013-12-27 18:31
Andy Niu
阅读(333)
推荐(0) 编辑
【35】考虑virtual方法以外的其他选择
摘要:1、使用Non-Virtual Interface 实现方法模版 将虚方法声明为private,子类重写private的虚方法,在父类中通过public方法调用虚方法。NVI的优点是:可以在虚方法之前和之后做一些事情。2、使用方法指针实现策略模式 在类中关联一个方法指针,方法指针的形参是类对象的引用。实例化一个对象的时候,把一个方法传递给对象,这就意味着每个对象都有一个方法。这种方式提供了一些有趣的弹性:a、同一类型的不同对象可以有不同的计算方法;b、在运行期可以更改对象的计算方法。这种方式的缺点是:计算方法不能访问protected成员,要解决这个问题,只能弱化类的封装。3、使用tr1:..
阅读全文
posted @
2013-12-24 19:22
Andy Niu
阅读(223)
推荐(0) 编辑