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

随笔分类 -  More Effective C++

上一页 1 2
【M3】绝对不要以多态方式处理数组
摘要:1、考虑下面的情况,有个方法,如下: void Print(ostream& s, const Base array[], int size) { for(int i=0; i< size; ++i) { s<<array[i]; } }2、对于Base baseArray[10]; Print(cout, baseArray, 10) 没有问题。在C++中,子类对象组成的数组,可以复制给父类的引用或者指针。数组名也可以认为是个地址。 考虑: Derived derivedArray[10]; Print(cout, derivedArray, 10) 会... 阅读全文
posted @ 2014-03-02 17:10 Andy Niu 阅读(565) 评论(0) 推荐(0) 编辑
【M2】最好使用C++转型操作符
摘要:1、C语言中的转型操作符有两个问题: a、是个通用的转换操作符,也就是说,可以从一个类型转换到其他类型。通用必定是低效率和冗余的,因为要考虑很多情况。 b、在代码中,难以辨认出哪些是类型转换。2、C++提供了新式的转换操作符,有static_cast, dynamic_cast, const_cast, reinterpret_cast, 可以认为对不同的类型转换进行了细化,精确地指出意图,效率高。另外一方面,很容易在代码中辨认出这些类型转换。3、考虑,C++新式转换符的用法。 a、const_cast:去除对象的常量性。 b、dynamic_cast:用于向下转型或者跨系转型,类型必... 阅读全文
posted @ 2014-03-02 15:28 Andy Niu 阅读(244) 评论(0) 推荐(0) 编辑
【M26】限制某个class所能产生的对象数量
摘要:1、每当产生一个对象,必定调用构造方法。因此,禁止产生对象的做法就是,将所有的构造方法声明为private。2、只有在类的内部才可以访问private成员,有两层含义:在类的内部可以访问this的private成员,同时可以访问同类对象的private成员。3、将构造方法声明为private,只是限制了在外部调用构造方法产生对象,还是有办法可以产生对象。办法有: a、类暴露一个static方法,在static方法内部调用private构造方法,产生对象返回。 b、在类中声明友元方法,或者友元类,这样的话,就可以访问该类的private构造方法。4、考虑,只产生一个对象。该怎么办?使用友元方.. 阅读全文
posted @ 2014-03-02 11:19 Andy Niu 阅读(551) 评论(0) 推荐(0) 编辑
【M16】谨记80-20法则
摘要:1、开始编写代码时,不要过多考虑效率,而应该首先考虑逻辑的清晰性和代码的可读性。2、后期通过测试找到效率的瓶颈所在,而不是靠猜测。然后,针对性地去解决。也就是80%的时间去解决这20%的代码。 阅读全文
posted @ 2014-02-25 20:16 Andy Niu 阅读(234) 评论(0) 推荐(0) 编辑
【M8】了解各种不同意义的new和delete
摘要:1、首先考虑new operator,new operator 可以认为做了三件事情:a、调用operator new分配一块内存;b、在这块内存上调用构造方法构造对象;返回指针。2、operator new的声明如下: void* operator new(size_t size) 和C中的malloc一样,operator new只负责分配内存。3、考虑,string* ps = new string("hello"); 相当于做了下列事情: a、void* memory = operator new (sizeof(string)); b、在*memory上面,调用s 阅读全文
posted @ 2014-02-25 20:05 Andy Niu 阅读(414) 评论(0) 推荐(0) 编辑
【M21】利用重载技术避免隐式类型转换
摘要:1、考虑UPint 的加法+,UPint a, b, result; 为了使result = a+10; result= 10+a; 都能通过编译,操作符重载如下: const UPint operator+(const UPint& lhs, const UPint& rhs); 注意:不能使用成员操作符,否则result= 10+a;编译错误,因为隐式类型转换不能转换为this指针。2、在result = 10+a;调用的过程中,10会隐式转换为UPint(条件是UPint单一形参构造方法不是explicit),这导致临时对象的产生,效率降低。3、怎么解决这个问题呢? 增加 阅读全文
posted @ 2014-02-25 19:15 Andy Niu 阅读(329) 评论(0) 推荐(0) 编辑
【M6】区别increment/decrement操作符的前置(prefix)和后置(postfix)形式
摘要:1、考虑++(--的情况是一样的),前置是累加然后取出,后置是取出然后累加。2、重载方法根据形参表的不同区分,问题来了,前置和后置形式都没有形参,因此没法区分。怎么办? 对于后置增加一个形参int,在方法内并不使用这个形参,因此去掉形参名。3、考虑UPint(unlimited precision int)类,对于前置,返回引用,实现如下: UPint& UPint::operator++() { *this+=1; return *this; }4、考虑后置,后置返回老的对象。为了保证前置和后置的结果一致性,和代码的复用,使用后置调用前置。如下: const UPint... 阅读全文
posted @ 2014-02-25 18:54 Andy Niu 阅读(642) 评论(0) 推荐(0) 编辑
【M7】千万不要重载&&,||和,操作符
摘要:1、C++对于真假值表达式采用“骤死式”评估方法,比如&&,||。 if( p!=NULL&& strlen(p)>10) 如果p为NULL,后面的strlen不会被执行。 if(index upper) 如果index += -= *= /= %= ^= &= |= > >>= = && || ++ -- , ->* -> () []7、对于允许重载的操作符,必须有个好理由,才去重载。 阅读全文
posted @ 2014-02-25 18:29 Andy Niu 阅读(841) 评论(0) 推荐(0) 编辑
【M22】考虑以操作符复合形式(op=)取代其独身形式(op)
摘要:1、对于内置类型,x = x+y 与x+=y的结果相同。2、x=x+y 与 x+=y的结果相同,但二者做的事情差别很大。 a、x=x+y做的事情:方法内有个局部对象,值为x+y,返回局部对象,返回值是个临时对象,这个临时对象赋值给x。 b、x+=y做的事情:直接在x上操作,修改x的内容,并返回x的引用。3、从上面的分析可以知道,操作符复合形式(+=)比单独形式(+)效率高很多,复合形式避免了局部对象和临时对象的构造和析构,以及对x的copy赋值。4、为了避免重复代码和保持代码的一致性,应该让+调用+=,如下: template const T operator+(const T& l. 阅读全文
posted @ 2014-02-24 21:03 Andy Niu 阅读(288) 评论(0) 推荐(0) 编辑
【M15】了解异常处理(exception handling)的成本
摘要:1、为了在运行期处理异常,程序必须做大量额外的工作。比如,即使抛出异常,也必须保证离开作用域的栈上对象执行析构方法。因此,必须记录try语句的进入点和离开点,记录catch语句能够处理的异常等。这就意味着,程序目标码变大,执行速度慢。2、即使从未使用任何异常处理,还是必须要付出最低代价,付出一些空间,放置某些数据结构,付出一些时间,保持数据结构的正确性。3、即使自己的程序没有使用throw,try,catch语句,使用的其他程序库可能有异常处理,因此也要付出代价。4、对于try语句,没有异常抛出的情况下,代码膨胀5%-10%,速度也下降这个数。5、如果抛出异常,影响很大,速度可能会比正常情况下 阅读全文
posted @ 2014-02-12 19:21 Andy Niu 阅读(373) 评论(0) 推荐(0) 编辑
【M20】协助完成“返回值优化(RVO)”
摘要:1、方法返回对象,会导致临时对象的产生,这降低了效率,const Rational operator* (const Rational& lhs,Rational& rhs)。有没有什么解决办法呢?2、试图返回引用,方法内的局部对象,在方法执行完后销毁,这个引用指向垃圾。不可行。3、试图返回指针,这导致奇怪的语法Rational c = *(a*b); 并且要求客户执行delete,这不合理。就算客户正确执行delete,有些情况,方法返回值没有暴露指针,客户无法执行delete,比如:Rationald = *(a*b*c); 不可行。4、因此,必须返回对象。那该怎么办呢? 阅读全文
posted @ 2014-02-12 18:59 Andy Niu 阅读(346) 评论(0) 推荐(0) 编辑
【M1】仔细区别pointers和references
摘要:1、引用必须初始化,而且不能修改指向,这一点和const对象十分相似。2、引用和指针用法不同,对于引用int& ri = a; rhs是个对象,引用前面加&;对于指针,int* pi = &a; rhs是个对象地址,指针前面加*。注意:引用往往是用指针实现的,从int& ri = a; 可以认为a赋给ri,再取地址。3、引用是一个对象的别名,不能指向为空,而且自始至终是这个对象的别名。指针是地址,可以指向为空,也可以指向不同的对象。4、考虑对引用初始化:引用是一个对象的别名。引用不能指向为空,但是通过一定技巧,可以让引用指向为空。考虑,先让指针指向为空(0),解 阅读全文
posted @ 2014-02-12 18:13 Andy Niu 阅读(424) 评论(0) 推荐(0) 编辑
【M19】了解临时对象的来源
摘要:1、首先,确认什么是临时对象。在swap方法中,建立一个对象temp,程序员往往把temp称为临时对象。实际上,temp是个局部对象。C++中所谓的临时对象是不可见的,产生一个non-heap对象,并且没有命名,便是一个临时对象。2、临时对象的来源有:隐式类型转换,方法返回值,抛出异常的时候。3、隐式类型转换:在初始化或者赋值的过程中,如果类型不匹配,需要一个适配的临时对象,编译器会构造这个临时对象,使调用成功。 注意:这种适配,只能执行一次,不能执行多次。为什么?假如可以执行多次,从一个对象到另一个对象,存在很多转换路径,编译器不可能找出所有的路径。就算可以找出所有的路径,存在多条转换路径. 阅读全文
posted @ 2014-02-11 20:24 Andy Niu 阅读(423) 评论(0) 推荐(0) 编辑
【M13】以by reference 方式捕捉exceptions
摘要:1、catch语句的参数有三种方式:by pointer,by value,by reference。2、首先考虑,by pointer。这意味着抛出端必须抛出一个对象的地址。那么问题来了: 如果这个对象分配在栈上,离开作用域,对象销毁,catch语句中指针指向一堆垃圾。 因此,这个对象不能分配在栈上,也就是说,离开了作用域对象仍然存在,没有销毁。那就只能是static对象,或者是heap对象。那么问题又来了,在catch语句的客户端,客户到底要不要执行delete呢?客户不知道这个对象是static还是heap对象。如果执行delete,而抛出端的对象是static,对不是在heap上分.. 阅读全文
posted @ 2014-02-11 19:38 Andy Niu 阅读(428) 评论(0) 推荐(0) 编辑
【M12】了解“抛出一个exception”与“传递一个参数”或“调用一个虚函数”之间的差异
摘要:1、方法参数的声明语法和catch语句的语法是一样的,你可能会认为主调方法调用一个方法,并向其传递参数,与抛出一个异常传递到catch语句是一样的,是的,有相同之处,但也有更大的不同。2、主调方法调用一个方法,控制权转移,被调方法执行完,控制权最终还会返回到主调方法。但是,抛出异常到达catch语句,控制权不会再回到抛出端。3、那么问题来了,抛出异常到达catch语句,控制权不再会到抛出端,这意味着,抛出的异常离开了作用域,自动销毁,那么catch语句还怎么捕获异常对象呢? 解决办法是:建立一个临时对象,对抛出的异常进行copy构造。特别注意的是,不管什么情况,这个临时对象是必不可少的。根本. 阅读全文
posted @ 2014-02-11 18:56 Andy Niu 阅读(659) 评论(0) 推荐(0) 编辑

上一页 1 2
< 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

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