随笔 - 576  文章 - 0  评论 - 62  阅读 - 219万
01 2014 档案
【转】C++对象内存分配问题
摘要:原文:http://blog.csdn.net/c504665913/article/details/7797859如果一个人自称为程序高手,却对内存一无所知,那么我可以告诉你,他一定在吹牛。用C或C++写程序,需要更多地关注内存,这不仅仅是因为内存的分配是否合理直接影响着程序的效率和性能,更为主要的是,当我们操作内存的时候一不小心就会出现问题,而且很多时候,这些问题都是不易发觉的,比如内存泄漏,比如悬挂指针。笔者今天在这里并不是要讨论如何避免这些问题,而是想从另外一个角度来认识C++内存对象。 我们知道,C++将内存划分为三个逻辑区域:堆、栈和静态存储区。既然如此,我称位于它们之中的对象分. 阅读全文
posted @ 2014-01-24 20:04 Andy Niu 阅读(4449) 评论(0) 推荐(1) 编辑
【转】placement new
摘要:原文:http://www.cnblogs.com/wanghetao/archive/2011/11/21/2257403.html1. placementnew的含义placementnew是重载operatornew的一个标准、全局的版本,它不能够被自定义的版本代替(不像普通版本的operatornew和operatordelete能够被替换)。void*operatornew(size_t,void*p)throw(){returnp;}placementnew的执行忽略了size_t参数,只返还第二个参数。其结果是允许用户把一个对象放到一个特定的地方,达到调用构造函数的效果。和其他普 阅读全文
posted @ 2014-01-24 20:02 Andy Niu 阅读(250) 评论(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) 编辑
【转】C++ 笔试面试题目
摘要:原文:http://blog.csdn.net/txgc1009/article/details/6700830许多面试题看似简单,却需要深厚的基本功才能给出完美的解答。企业要求面试者写一个最简单的strcpy函数都可看出面试者在技术上究竟达到了怎样的程度,我们能真正写好一个strcpy函数吗?我们都觉得自己能,可是我们写出的strcpy很可能只能拿到10分中的2分。读者可从本文看到strcpy函数从2分到10分解答的例子,看看自己属于什么样的层次。此外,还有一些面试题考查面试者敏捷的思维能力。 分析这些面试题,本身包含很强的趣味性;而作为一名研发人员,通过对这些面试题的深入剖析则可进一步增. 阅读全文
posted @ 2014-01-23 19:08 Andy Niu 阅读(407) 评论(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) 编辑
【47】请使用traits classes表现类型信息
摘要:1、考虑下面的需求,对迭代器移动d个单位。因为不同类型的迭代器,能力不同,有的迭代器(vector,deque内置迭代器)可以一步到位移动到指定位置,有的迭代器(list内置迭代器)必须一步一步移动,怎么解决?2、很显然,需要判断迭代器的能力,也就是哪一类,不同分类的迭代器采用不同的做法。这需要在运行时判断迭代器的分类,效率低,有没有更好的办法呢?3、很自然地,我们想到增加一个中间层,对不同分类的迭代器封装,对外暴露迭代器的类型信息,也就是在编译器就确定它的分类。具体做法如下: a、每种集合的内置迭代器,都有一个值表示自己的分类,使用typedef 为它的分类取个别名,对外暴露别名; b、.. 阅读全文
posted @ 2014-01-21 17:54 Andy Niu 阅读(311) 评论(0) 推荐(0) 编辑
【19】设计class犹如设计type
摘要:设计class 的时候,需要好好考虑下面的问题:1、新type的对象应该如何被创建和销毁?2、对象的初始化和对象的赋值该有什么样的差别?3、新type的对象如果pass by value,意味着什么?4、什么是新type的合法值?5、新type需要某个继承体系吗?6、新type需要什么样的转换?7、什么样的操作符和函数对新type的对象是合理的?8、该如何访问新type对象的成员?9、什么是新type未声明的接口?10、新type有多么一般化?是否应该使用template? 阅读全文
posted @ 2014-01-20 21:29 Andy Niu 阅读(167) 评论(0) 推荐(0) 编辑
【18】让接口容易被正确使用,不易被误用
摘要:1、所谓软件设计,就是“让软件做出你期望它做的事情”的步骤。首先是构想,考虑对外暴露的接口,然后实现。2、客户没有正确使用自己提供的接口,自己也要负担一部分责任,思考自己的接口是不是简单明了,容易理解。3、对于多个形参的接口,最好表明每个形参的类型和有效范围。4、尽量限制哪些事能做,哪些事不能做,尽早暴露错误。如果没有好的理由,与内置类型保持一致。5、减少用户出错的可能性,考虑,在堆上获取的资源,期望用户使用资源管理类对它封装,但是用户可能忘记。那么,我就对资源封装,并返回给用户一个资源管理对象。对于引用次数为0,执行其它操作的资源管理类,应该在内部指定删除器。 阅读全文
posted @ 2014-01-20 21:24 Andy Niu 阅读(316) 评论(0) 推荐(0) 编辑
【12】赋值对象时勿忘其每一个成分
摘要:1、copy构造和copy赋值,我们称为copy方法。当我们声明自己的copy方法时,就意味着,告诉编译器,我自己定义copy构造和copy赋值,不要你自动生成。这种情况下,自己要保证复制对象的时候,不要忘记每一个成分。2、开始定义一个类的时候,在copy构造我们会初始化每一个字段,在copy赋值我们会为每个字段赋值,不会出错。后来,当我们在类中增加一个字段,很可能会忘记初始化这个字段,或者为它赋值。这种情况,要特别注意。3、当出现继承的时候,存在潜在的问题。子类会继承父类的字段,即使是private字段也继承下来,只不过不能访问。这种情况下,不要忘记父类中的字段。4、copy构造的时候,在初 阅读全文
posted @ 2014-01-20 21:02 Andy Niu 阅读(267) 评论(0) 推荐(0) 编辑
【11】在operator=中处理“自我赋值”
摘要:1、自我赋值,看起来愚蠢,但是却合法。有些自我赋值一眼就可看出来。有些自我赋值是潜在的。比如:a[i] = a[j]; *px = *py; 甚至不同类型的指针,都指向同一个地址,也是自我赋值,这一类自我赋值,很难识别,因此对自我赋值要有一定的防范。2、对于资源管理类auto_ptr和shared_ptr,自我赋值是安全的。如果自行管理资源,比如Widget中有个Bitmap* pb;copy赋值如下:1 Widget& Widget::operator=(const Widget& rhs)2 {3 delete pb;4 pb = new Bitmap(*(rhs.pb)) 阅读全文
posted @ 2014-01-20 20:35 Andy Niu 阅读(1229) 评论(0) 推荐(0) 编辑
【25】考虑写出一个不抛异常的swap函数
摘要:1、swap交换对象值,std提供了实现方法模版的实现,它的实现是交换对象值。 1 namespace std 2 { 3 template 4 void swap(T& a,T& b) 5 { 6 T temp(a); 7 a = b; 8 b = a; 9 }10 }2、考虑下面的情况下,资源管理类Widget,内部只有一个指针WidgetImpl,该指针指向一个对象,这个对象中包含真正数据。对这种资源管理类执行swap,如果调用std中的swap,有哪些动作?对于Widget,copy构造一次,copy赋值两次,同时对于WidgetImpl,copy构造一次,cop... 阅读全文
posted @ 2014-01-20 19:56 Andy Niu 阅读(615) 评论(0) 推荐(0) 编辑
理解shared_ptr<T> ---2
摘要:1、引用计数字段不能放在资源管理类中。我们的解决办法是,把引用计数和资源绑在一起,进行二次封装。但是这样存在一个大问题,不同类型的资源管理类不能兼容。也就是说,shared_ptr不能赋值给shared_ptr。2、你可能会想,使用模版成员方法去解决,但是这里有个问题。因为进行了两次封装,u_ptr的类型还是不一样,也不能赋值。你可能会想,我在u_ptr中也建立模版成员方法,这也是错的。思考下,我们要保证,资源管理类指向同一个u_ptr,对u_ptr进行拷贝,那么资源管理类就不是指向同一个u_ptr了,这显然是错的。3、有没有其它的办法呢? 问题的关键是,进行了两次封装。不进行两次封装就好了. 阅读全文
posted @ 2014-01-17 21:22 Andy Niu 阅读(833) 评论(0) 推荐(0) 编辑
理解shared_ptr<T>
摘要:1、shared_ptr解决什么问题? auto_ptr有个局限,拥有权转移。这往往不符合我们的需求,有时候我们期望,多个资源管理对象可以共享一个资源,当引用计数为0的时候,执行delete。shared_ptr就是为了解决这个问题。2、shared_ptr怎么解决这个问题?和auto_ptr类似,除此之外,还有几点需要注意:3、增加一个字段为引用计数,当引用计数为0的时候,执行delete。引用计数字段不能放在资源管理类中,为什么? 假设放到资源管理类中,每个资源管理对象都有一个refCount字段,共享一个资源的资源管理对象的refCount取值一样,但是怎么保持一致呢?因为这些对象之.. 阅读全文
posted @ 2014-01-17 19:05 Andy Niu 阅读(1746) 评论(0) 推荐(0) 编辑
理解 auto_ptr<T>
摘要:1、auto_ptr解决什么问题? 在堆上获取的资源,客户可能会忘记delete,或者由于异常没有执行到delete,导致资源泄漏。在栈上分配的对象,有个特点,不论出现什么情况,超出作用域后,都将调用析构方法。根据这个特点,可以使用栈上的对象管理指针,析构的时候执行delete,确保一定释放资源。2、auto_ptr就是解决这个问题的,auto_ptr就是个资源管理类,它的特点是拥有权转移。3、考虑,如果让我来设计auto_ptr,我该怎么做? auto_ptr是个资源管理类,对指针封装。有几点需要注意: a、可以对不同类型的指针进行封装,因此需要一个模板类。 b、copy构造,cop... 阅读全文
posted @ 2014-01-17 18:40 Andy Niu 阅读(829) 评论(0) 推荐(0) 编辑
【09】绝不在构造和析构过程中调用virtual方法
摘要:1、绝不在构造和析构过程中调用virtual方法,为啥? 原因很简单,对于前者,这种情况下,子类专有成分还没有构造,对于后者,子类专有成分已经销毁,因此调用的并不是子类重写的方法,这不是程序员所期望的。2、在构造方法和析构方法中,直接调用virtual方法,很容易识别。还有一种情况要注意,那就是间接调用。比如:构造方法调用init方法,而init方法调用virtual方法。3、在构造过程中,不能使用virtual从上到下调用,哪有什么办法弥补呢? 可以将子类必要的信息向上传递给父类构造方法。 阅读全文
posted @ 2014-01-16 21:11 Andy Niu 阅读(266) 评论(0) 推荐(0) 编辑
【08】别让异常逃离析构函数
摘要:1、考虑一个类Widget,析构方法抛出异常,一个集合中包含10个Widget对象,集合离开作用域,逆序撤销集合里的对象,最后一个抛出异常,倒数第二个抛出异常,问题出现了,这种情况下,要么导致程序结束,要么导致不明确行为。2、因此,绝不能在析构方法中抛出异常。考虑,数据库连接类DBConn,用户可能会忘记关闭连接,因此在DBConn的析构方法中,执行Close方法。那么问题来了,close方法可能会抛出异常,怎么办? a、在析构方法中,捕获异常,结束程序,这种方法可用性太差。 b、在析构方法中,捕获异常,吞下异常,这种方法忽略错误,程序继续运行,接下来可能会导致不明确行为。3、上面两种解决.. 阅读全文
posted @ 2014-01-16 21:00 Andy Niu 阅读(286) 评论(0) 推荐(0) 编辑
C++ 外部调用private方法
摘要:1、思考,对于C++,能不能在外部调用私有方法?2、在Java中,子类继承不能缩小父类成员的访问权限。因为在Java中,继承只是表示Is-A关系,因此,父类提供的接口,子类必须承诺仍然提供,不能缩小访问权限,但是可以放大权限。3、在C++中,继承不光表示Is-A关系,还可以表示根据某物实现出。因此,在C++中,子类可以放大或者缩小父类成员的访问权限。考虑,如果父类提供public virtual方法,子类重写为private virtual方法,我们知道,重写只不过是,子类整体拷贝父类的虚方法表,对于重写的方法偷梁换柱,替换为重写后的方法。在这种情况下,父类指针指向子类对象,父类指针通过调用p 阅读全文
posted @ 2014-01-16 20:34 Andy Niu 阅读(4821) 评论(0) 推荐(1) 编辑
【07】为多态基类声明virtual析构方法
摘要:1、考虑下面的需要,需要一个工厂方法。工厂方法的规则是:在堆上分配一个子类对象,并返回父类指针。使用完毕,delete父类指针。如果父类的析构方法不是virtual,将直接调用父类的析构方法,导致局部销毁的对象,父类成分销毁了,子类成分没有销毁。2、如果类中有virtual方法,意味着面向抽象编程,也就是会有父类指针指向子类对象,因此这种情况下,必定需要一个virtual析构方法。3、如果类中没有virtual方法,为了节省空间,析构方法是non-virtual方法。也就意味着,这个类不作为父类。如果你继承这样的类,就会导致上面的局部销毁对象。Java中有final,C#中sealed,禁止继 阅读全文
posted @ 2014-01-16 19:53 Andy Niu 阅读(362) 评论(0) 推荐(0) 编辑
C++ 构造过程和析构过程
摘要:1、C++构造和析构的过程,类似于穿衣脱衣的过程。穿衣是:先穿内衣,再穿外套。脱衣是:先脱外套,再脱内衣。C++构造过程:首先调用父类构造方法,再调用子类构造方法。C++析构过程:首先调用子类析构方法,再调用父类析构方法。2、子类不能继承父类的构造方法和析构方法,除此之外,其他的成员都能继承,包括父类的private成员,只不过子类不能访问private成员。3、思考一下,为什么子类不能继承父类的构造方法? 想一想,构造方法是干什么的?构造方法是初始化对象的成分,创建对象。从语义上讲,父类构造方法初始化父类成分,构造父类对象。子类构造方法在父类对象的基础上,初始化子类专有成分,构造子类对象。. 阅读全文
posted @ 2014-01-16 19:12 Andy Niu 阅读(4054) 评论(0) 推荐(1) 编辑
理解C# Lazy<T>
摘要:1、Lazy解决什么问题? 考虑下面的需求,有个对象很大,创建耗时,并且要在托管堆上分配一大块空间。我们当然希望,用到它的时候再去创建。也就是延迟加载,等到真正需要它的时候,才去加载。考虑一下,我该怎么解决这个问题。2、显然,这里需要加一个中间层,将大对象封装起来,暴露接口,开始并不创建大对象,等到用户真正访问对象的时候,再去创建。另外,这个中间层应该可以封装不同类型的大对象,因此需要类模版。Lazy就是为了解决这个问题。 阅读全文
posted @ 2014-01-15 20:46 Andy Niu 阅读(2825) 评论(0) 推荐(1) 编辑
【06】若不想使用编译器自动生成的函数,就该明确拒绝
摘要:1、有些情况下,我们不希望对象被拷贝,比如独一无二的对象,拷贝是没有意义的。这种情况下,我们应该禁止拷贝。也就是不提供copy构造和copy赋值,可问题是,如果我们不提供,编译器会自动生成。那该怎么办?2、我们声明copy构造和copy赋值为private,这就明确告诉了编译器,我已经声明了,你别为我生成了。由于copy构造和copy赋值是private,就禁止了外部的拷贝操作。 但仔细想,还是有问题,因为类的成员方法,友元方法,友元类还是可以访问private权限的copy构造和copy赋值,那该怎么办? 只声明,不定义。这样,就导致编译器连接的时候出错。有没有更好的办法呢?也就是说,将.. 阅读全文
posted @ 2014-01-15 20:19 Andy Niu 阅读(467) 评论(0) 推荐(0) 编辑
【05】了解C++默默编写并调用那些函数
摘要:1、如果没有声明copy构造方法,copy赋值操作符,和析构方法,编译器会自动生成这些方法,且是inline。2、如果没有声明任何构造方法,编译器会自动生成一个default构造方法,且是inline。3、编译器总是为我们生成一个合成析构方法。4、首先考虑,编译器生成的copy构造方法做什么事? copy构造方法是一个特殊的构造方法,形参是const T&。编译器生成的copy构造方法逐个成员初始化。逐个成员初始化:对于创建对象的每个non-static字段,使用现有对象的字段去初始化。 如果字段是类类型,递归调用类的copy构造方法。如果是内置类型,逐个bit拷贝。需要注意的是:对于 阅读全文
posted @ 2014-01-15 19:49 Andy Niu 阅读(372) 评论(0) 推荐(0) 编辑
理解C# Attribute
摘要:1、Attribute与Property Attribute是特性,Property是属性。2、Attribute与注释 注释:是给程序员看的,编译的时候会去掉这些信息,也就是说,程序集中没有注释的内容。 Attribute:会被编译到程序集中,在程序集的元数据中,在加载程序集的时候,可以从它的元数据中提取出这些信息。3、说了那么多,考虑下,它解决什么问题? 考虑下面的需求,一个工具类提供了很多静态方法,甲乙两个程序员合作完成,理所当然,应该标识出哪些是甲写的,哪些是乙写的,用于统计。最笨的加注释,但是它有两个问题:a、编译后生成的程序集中,没有了这些注释内容;b、只能通过人工去统计甲... 阅读全文
posted @ 2014-01-14 20:46 Andy Niu 阅读(2203) 评论(0) 推荐(2) 编辑
理解 MEF
摘要:1、它解决什么问题? 考虑下面的需求,甲程序员对外暴露接口,内部提供实现。乙程序员使用甲提供的接口,根据面向接口编程的原则,乙关联一个接口类型的引用。正常情况下,乙要使用甲的实现,必须实例化一个具体对象。有没有更好的办法呢?我们知道Java框架spring中有个依赖注入的概念。C#中能不能使用类似的方式呢?2、怎么解决? 使用MEF(Managed Extensibility Framework),示例如下: 1 namespace TestMEF 2 { 3 public interface IBookService 4 { 5 void GetBookN... 阅读全文
posted @ 2014-01-14 20:16 Andy Niu 阅读(469) 评论(0) 推荐(0) 编辑
C# explicit与implicit
摘要:1、它们解决什么问题? 考虑下面的需求,Person类有个字段age。我想使用Person p = (Person) 18 来创建一个age为18的Person对象,怎么办? 更进一步,我想使用Person p = 18 来创建一个age为18的Person对象,怎么办?2、使用explicit(显式)和implicit(隐式) 1 class Person 2 { 3 private int age; 4 public int Age 5 { 6 get { return age; } 7 ... 阅读全文
posted @ 2014-01-14 19:55 Andy Niu 阅读(2892) 评论(0) 推荐(2) 编辑
【04】确定对象被使用之前已先被初始化
摘要:1、首先区分初始化和赋值。 初始化:创建对象时赋给初始值。赋值:擦除对象当前值,并用新值代替。因此,区分初始化和赋值的关键是,看看对象当前是否已经有值了。2、创建对象的时候,如果没有初始化,对象的值是什么? 取决于对象的类型和对象定义的位置。 a、如果对象是内置类型,定义在方法外,它的值为0,定义在方法内,没有初始化。 b、如果对象是类类型,无论定义在哪里,都会调用默认构造方法,也就是说,对于类类型的对象,不存在没有初始化的情况。3、现在考虑,类中的数据成员,也就是字段。类的构造方法执行两个过程:成员初始化列表和方法内赋值。对于类类型,前者用于初始化,即copy构造;后者用于赋值,即c... 阅读全文
posted @ 2014-01-14 19:28 Andy Niu 阅读(571) 评论(0) 推荐(0) 编辑
【03】尽可能使用const
摘要:1、为什么搞出const关键字? const指定一个语义约束,指定一个对象不可修改。如果一个对象不可修改,就应该说出来。2、const与指针 const可以修饰指向之物,也可以修改指针本身。STL中的迭代器是对指针的封装,因此,迭代器也有两个概念:指向常量对象的迭代器和常量迭代器。 vector::const_iterator; //指向常量的迭代器 const vector::iterator; // 常量迭代器3、const可以与方法产生关联,可以用在方法前,方法中(形参表中),方法后。首先考虑,用在方法前,表示返回值是常量。为什么要返回const对象呢? 我们知道,方法返回值是... 阅读全文
posted @ 2014-01-13 20:43 Andy Niu 阅读(351) 评论(0) 推荐(0) 编辑
【S13】vector和string优先于动态分配的内存
摘要:1、使用new动态分配内存,必须承担如下责任: a、使用delete释放内存; b、确保使用了正确的形式,delete与new的形式要匹配; c、不能重复delete。2、使用vector和string可以消除以上的负担。每当要动态分配一个数组时,都要考虑使用vector和string替代。如果元素是字符char,使用string。否则使用vector。注意:有一种特殊情况,使用vector更合理。3、vector和string的元素分配在堆上,它们内部维护一个指针,指向堆上的元素。vector和string是深拷贝,会把元素逐个拷贝。4、vector和string,它们自己管理内... 阅读全文
posted @ 2014-01-12 22:33 Andy Niu 阅读(479) 评论(0) 推荐(0) 编辑
【28】避免返回handles指向对象内部成分
摘要:1、为什么? 很简单,你指向箱子里面的一个物品,使用这个物品。但是箱子不受你控制,箱子销毁了,里面的物品也会随之销毁。那么这种情况下,你指向的就是一堆垃圾,你还在使用这个物品,导致未定义的行为。 阅读全文
posted @ 2014-01-10 19:19 Andy Niu 阅读(151) 评论(0) 推荐(0) 编辑
【27】尽量少做转型动作
摘要:1、C++是强类型语言,保证类型错误不会发生,转型会破坏类型系统。C语言提供了强制转型语法T(expn),或者T(expn),C++提供了新式转型。 const_cast(expn) static_cast(expn) dynamic_cast(expn) reinterpret_cast(expn) 强烈建议使用新式转型,因为:一是在代码中容易识别出来,二是各转型动作的目标窄化,编译器容易诊断出错误,可以简单认为,细化分工比原来的万能方式好。2、尽量避免转型,为什么?转型往往会影响效率,转型后出现临时对象,而应该使用无需转型的替代方法,比如virtual。 阅读全文
posted @ 2014-01-10 19:13 Andy Niu 阅读(196) 评论(0) 推荐(0) 编辑
成员方法的this指针
摘要:1、我们知道成员方法中,有个隐式的this常量指针。考虑,Derived继承的成员方法中this指针的表面类型是什么?子类重写的虚方法中this指针的表面类型是什么?2、Derived继承的方法,就是Base的那个方法,this指针的表面类型是Base*,也就是把this指针当成Base来解释。3、子类重写的方法中,this指针的表面类型是Derived*,也就是把this指针当成Derived来解释。注意:子类重写父类的方法,需要重新声明。 阅读全文
posted @ 2014-01-10 18:57 Andy Niu 阅读(347) 评论(0) 推荐(0) 编辑
【43】学习处理模版化基类内的名称
摘要:1、考虑下面的场景,有个类模版template Box{}; 内部有个方法XXX,现在子类template class BigBox:public Box{},BigBox内部使用方法XXX,考虑出现什么问题?2、首先一点,类模版产生出来的类之间,没有关系。也就是Box 与Box没有关系。通过指定模版实参,可以产生不同的模板类,也可以全特化一个类,如下: template class Box{...},这种情况下,就不再实例化模版类了,Box可以声明自己的接口,可能就没有了XXX。那么,问题出现了,BigBox继承Box,但是父类Box可能有接口XXX,也可能没有接口XXX,编译器傻眼了... 阅读全文
posted @ 2014-01-10 18:37 Andy Niu 阅读(336) 评论(0) 推荐(0) 编辑
【42】了解typename的双重意义
摘要:1、在template声明中,class与typename是等价的,但是使用typename更好。2、在template实现中,模版形参是从属名称,嵌套在模版形参中的类型是嵌套从属名称,不依赖任何template参数的名称,称为非从属名称。3、嵌套从属名称,可能会导致解析困难,出现二义性。比如:C::const_iterator* x; 有两种意思。一是:模版形参C中有个静态字段const_iterator,然后计算它与x的乘积;二是:模版形参C中有个嵌套类型const_iterator,定义指向它的指针。默认情况下,C++编译器按第一种意思解释,也就是说,把它当成静态字段,而不是类型。如果我 阅读全文
posted @ 2014-01-10 18:02 Andy Niu 阅读(543) 评论(0) 推荐(0) 编辑
【41】了解隐式接口和编译期多态
摘要:1、当初为什么设计C++ Template? C++ Template是为了建立类型安全的容器,如vector,可以建立包含Person的vector,也可以建立包含int的vector。后来发现,C++ Template具备更强大的威力,泛型编程,也就是写出的代码与所处理的对象类型彼此独立。2、面向对象总是显式接口和运行期多态,这个很好理解。显式接口就是类的头文件中暴露的方法和字段,运行时多态就是运行时根据对象的真实类型决定调用哪个方法。在泛型编程中,暴露的接口是隐式接口,也就是一组有效表达式,期望类的对象具有这些接口,如果没有代码编译通不过。编译时多态是指:在编译时根据模版实参实例化出一. 阅读全文
posted @ 2014-01-10 17:41 Andy Niu 阅读(299) 评论(0) 推荐(0) 编辑
【17】以独立语句将newed对象置入智能指针
摘要:1、为什么? 考虑下面的情况:方法声明为void processWidget(shared_ptr pw,int priority)。 调用方法 processWidget(shared_ptr (new Widget), getPriority()),在调用方法之前,要做三件事: a、执行new Widget b、调用shared_ptr的构造方法 c、调用方法getPriority() 可问题是:C++不保证上面的执行顺序(C#,Java保证)。那么问题就来了,如果是acb的执行顺序,并且c步骤异常,那么new出来的资源还没有放进资源管理类,导致内存泄漏。2、如何解决上面的问题... 阅读全文
posted @ 2014-01-09 20:44 Andy Niu 阅读(148) 评论(0) 推荐(0) 编辑
【16】成对使用new和delete时要采取相同形式
摘要:简而言之,new时不带[],delete时也不带[];new时带[],delete时也要带[]。如果不匹配,要么造成多销毁对象,导致未定义行为;要么导致少销毁对象,导致内存泄漏。 阅读全文
posted @ 2014-01-09 20:03 Andy Niu 阅读(130) 评论(0) 推荐(0) 编辑
【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) 推荐(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) 推荐(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) 推荐(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) 推荐(0) 编辑
【13】以对象管理资源
摘要:1、为什么要以对象管理资源,它解决什么问题? 考虑下面的需求,客户需要一个动态分配的资源。我提供一个工厂方法,返回一个指针,指向资源。那么问题来了,谁来释放资源呢?只有客户知道什么时候不再使用资源,因此,应该是客户负责释放资源。如果客户没有成功释放资源,造成资源泄漏,注意,这里泄漏的资源不光是一块内存,还可能包括这块内存保存的其他资源。把成功释放资源的希望,寄托在客户身上,显然是不靠谱的。因为:a、客户可能忘记delete;b、代码异常,导致没有执行到delete。2、它是如何解决这个问题的? 我们知道,栈上分配的对象有个特点,超出作用域,不管什么情况,都会自动调用析构方法。因此,可以把堆.. 阅读全文
posted @ 2014-01-08 19:04 Andy Niu 阅读(238) 评论(0) 推荐(0) 编辑
C++ 初始化与赋值
摘要:1、变量三要素:类型,名称,值。定义变量的时候,提供初始值,定义时指定了初始值的对象称为已初始化的。C++支持两种初始化形式:直接初始化,赋值初始化。如下: int ival(1024); // 直接初始化 int ival = 1024; // 赋值初始化2、初始化与赋值是两个概念。初始化:创建对象时赋给初始值。赋值:擦除对象的当前值,并用新值代替。因此,判断是初始化还是赋值的关键是,看看对象当前是否已经存在值了。3、初始化调用copy构造方法,赋值调用copy复制操作符。 阅读全文
posted @ 2014-01-08 18:28 Andy Niu 阅读(507) 评论(0) 推荐(0) 编辑
C++ 变量初始化规则
摘要:1、定义变量的时候,如果没有初始化,它的值是什么呢? 它的值取决于变量的类型和变量定义的位置。2、考虑基本类型的变量,定义时没有初始化。如果定义在方法外部,初始化为0,如果定义在方法内部,不被初始化。使用未初始化的变量,导致未定义行为。未初始化的变量事实上,也有一个值,这个值是随机的。因此强烈建议,基本类型的变量都要初始化,也就是方法内部的变量。3、考虑类类型的变量,定义时没有初始化。不管定义在哪里,都会调用默认构造方法,这种情况下,必须提供默认构造方法,如果没有,编译出错。也就是说,对于类类型的变量,不存在没有初始化的情况,因为没有显式初始化,会调用默认构造方法进行隐式初始化。 阅读全文
posted @ 2014-01-07 20:32 Andy Niu 阅读(1007) 评论(0) 推荐(0) 编辑
C++ 构造和析构
摘要:1、继承关系可认为,子类在父类的基础上进行。从这个角度讲,可把它认为穿衣脱衣的过程。穿衣是:先穿内衣,再穿外套。脱衣是:先脱外套,在脱内衣。构造是:先调用父类构造方法,再调用子类构造方法。析构是:先调用子类析构方法,再调用父类析构方法。2、构造可分为两个步骤:初始化列表和方法内赋值。前者的使用场景是:初始化,即copy构造。后者的使用场景是:赋值,即copy赋值。 a、对于类类型,如果没有在初始化列表中显式初始化,会进行隐式初始化,调用默认构造方法。因此,没有默认构造方法的类成员,必须显式初始化。 b、对于基本类型,如果没有在初始化列表中显式初始化,不会进行隐式初始化。初始值依赖于对象的作.. 阅读全文
posted @ 2014-01-07 19:49 Andy Niu 阅读(442) 评论(0) 推荐(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) 推荐(0) 编辑
工作
摘要:1、身体不健康,家庭不幸福,挣再多的钱,又有什么意义呢?2、人生需要大智慧,不要贪图小便宜,因小失大。3、不要老想着挣钱,把自身的素质和能力提高,自然而然,就能挣到钱了。4、中国人很聪明,解决眼前麻烦的能力很强,而没有长远的眼光,缺乏系统的考虑。5、人不要自以为很聪明,以为别人不会知道。很偏僻的道路,遇到红色信号机,没有摄像头,自以为很聪明的人,就闯红灯了。有一定信仰的人,坚决不闯红灯。表面上看,后者吃亏了,从长远来看,后者没有。因为后者减少了出事故的情况。6、不要别人对我不好,我就对别人不好,狗咬我,但是我不会咬狗。长远来看,我是不会吃亏的。 阅读全文
posted @ 2014-01-04 21:17 Andy Niu 阅读(300) 评论(0) 推荐(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) 推荐(0) 编辑
准确理解需求
摘要:在编程中,不要别人一提需求,自己不思考,就埋头去干。应该想一想,他的真实意图,他为什么提这个需求?这个需求解决什么问题。有个经典的案例,客户提出想要一个月亮,愚蠢的人就开始想办法去实现,聪明的人会问客户,你要月亮干什么?用户说我想要照明。那直接给他一个灯泡就行了。 阅读全文
posted @ 2014-01-03 19:43 Andy Niu 阅读(986) 评论(0) 推荐(0) 编辑
C++ int与string的转化
摘要:int本身也要用一串字符表示,前后没有双引号,告诉编译器把它当作一个数解释。缺省情况下,是当成10进制(dec)来解释,如果想用8进制,16进制,怎么办?加上前缀,告诉编译器按照不同进制去解释。8进制(oct)---前缀加0,16进制(hex)---前缀加0x或者0X。string前后加上双引号,告诉编译器把它当成一串字符来解释。注意:对于字符,需要区分字符和字符表示的数值。比如:chara = 8;charb = '8',a表示第8个字符,b表示字符8,是第56个字符。int转化为string1、使用itoa(int to string) 1 //char *itoa( in 阅读全文
posted @ 2014-01-03 19:29 Andy Niu 阅读(335953) 评论(5) 推荐(8) 编辑
理解C++ 宏
摘要:1、什么是宏,它解决什么问题? 宏的本质是文本替换,考虑下面的需求,程序中多次使用圆周率Pi,在每个地方都使用3.1415,显然很愚蠢。有没有好的办法呢?使用宏,如下: #definePi 3.1415 这样,编译器在程序中遇到Pi,就使用3.1415来进行文本替换。2、特殊字符 考虑下面的需求,程序中多处使用文本字符串。我们知道文本字符串前后都要加上双引号,我很讨厌输入双引号。有没有好的办法呢?根据常识,使用下面的宏: #define Str(x) "x" String s = Str(Hello); 期望预编译为:String s = "Hello" 阅读全文
posted @ 2014-01-02 21:53 Andy Niu 阅读(2125) 评论(0) 推荐(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) 推荐(0) 编辑

< 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

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