《C++ Gotchas》读书笔记

阅读指南:

虽然这些原则都知道,但是应该怎样解决呢?所以,在阅读时着重关注解决方案(用红色标注).

 

 

C++Gotchas: Avoiding Common Problems in Coding and Design

 

"This may well be the best C++ book I have ever read. Iwas surprised by the amount I learned."-Matthew Wilson, DevelopmentConsultant, Synesis Software

 

C++Gotchas is the professional programmer's guide to avoiding and correctingninety-nine of the most common, destructive, and interesting C++ design andprogramming errors. It also serves as an inside look at the more subtle C++features and programming techniques.

 

Thisbook discusses basic errors present in almost all C++ code, as well as complexmistakes in syntax, preprocessing, conversions, initialization, memory andresource management, polymorphism, class design, and hierarchy design. Eacherror and its repercussions are explained in context, and the resolution ofeach problem is detailed and demonstrated.

 

AuthorStephen Dewhurst supplies readers with idioms and design patterns that can beused to generate customized solutions for common problems. Readers will alsolearn more about commonly misunderstood features of C++ used in advancedprogramming and design. A companion Web site, located athttp://www.semantics.org, includes detailed code samples from the book.

 

Readerswill discover:

 

Howto escape both common and complex traps associated with C++

 

Howto produce more reusable, maintainable code

 

AdvancedC++ programming techniques

 

Nuances(细微差别)of the C++ language

 

 

C++ Gotchas shows how to navigate through the greatestdangers in C++ programming, and gives programmers the practical know-how they need to gain expertstatus.

 

Chapter1. Basics

 

Gotcha#1: Excessive(过多的) Commenting(注释)

很多注释都不是必要的.他们通常使源代码难以阅读和维护,并且经常把维护者引入歧途.

lead sb.astray 使人堕落, 把人引入歧途

如果注释还没有代码本身表达的意思清晰,那么这样的注释就不是必须的,加上这样的注释反而变得更加糟糕.因为1.注释会分散读者阅读代码的注意力.2.增加了维护量,因为维护代码时,必须同时维护注释.Third,this necessary maintenance is often not performed,那么代码就和注释不一致了.

错误的注释比没有注释还让人难以阅读,因为维护者不会假设注释是错误的.

最好的注释就是不写注释,让代码本身就能清晰表达自己的意思.

Programmers also have a strong incentive(n.动机) not to"waste" lines of source text.If a construct(n.结构) (function,public interface of a class, and so on) can be presented in a conventional(常规的)and rational(合理的) format on a single "page" of about 30-40 lines, itwill be easy to understand. If it goes on to a second page, it will be abouttwice as hard to understand. If it goes onto a third page, it will beapproximately four times as hard to understand.

cede vt.放弃

还有一种令人讨厌的注释就是修改注释,例MOD BY NFH.20060912

这种注释有用吗?一点也没有,但是还必须一直驻留在代码中.一个解决办法是使用版本控制工具.

一个最好的避免写注释的方法是为函数,类,变量取一个好名字.

另一个减少注释的方式是使用标准组件(如swap, sort, and copy等等 ),标准组件是不用写注释的,因为他们有自己的专门文档

例:

swap(a, a+1 );

sort(a, a+max );

copy(a, a+max, ostream_iterator<T>(cout,"\n") );

 

Gotcha#2: Magic Numbers

portfolion.公事包

魔鬼数字有万害而无一利.Use enumerators or initialized constantsinstead. 

例:

constlong patienceLimit = 40000;

 

classPortfolio {

   // . . .

   enum { maxContracts = 10, idlen = 10 };

   Contract *contracts_[maxContracts];

   char id_[idlen];

};

由此可见,在类中用枚举,其他地方用const.因为const long patienceLimit = 40000; 不能直接放在类中.并且枚举时不一定要大写.

魔鬼数字的缺点:

1.表达的意思太少,10只能代表10,不能代表数组元素个数.===>用最少的东西表达最多的意思.

2.难于维护

3.数字没有地址,例

const long patienceLimit = 40000;

 

const long *p1 = &40000; // error!

const long *p2 = &patienceLimit; // OK.

const long &r1 = 40000; // legal, but see Gotcha#44

const long &r2 = patienceLimit; // OK.

 

Gotcha #3: Global Variables

fallacious adj.靠不住的

self-serving adj.自私的

impede v.阻止

Global variables impede code reuse and make code hardto maintain. They impede reuse because any code that refers to a globalvariable is coupled to it and may not be reused without being accompanied bythe global variable. They make code hard to maintain because it's difficult todetermine what code is using a particular global variable, since any code atall has access to it.

Users of global variables often cite convenience as areason for using them.实际上,使用全局变量很不值得.because maintenance typically consumesmore time than initial development, and use of global variables impedesmaintenance.

代替使用全局变量的优点:

1.Simply wrapping access in a function permitsextension through the use of overloading or default argument initializationwithout the necessity of significant change to source code:

Environment *theEnv();

Environment *theEnv( EnvCode whichEnv = OFFICIAL );

2.可以延迟初始化.并且最重要的还可以在不改变代码的情况下使用单件模式或go to amultithreaded design;

extern Environment * const theEnv = new OfficialEnv;

 

Environment *Environment::instance_ = 0;

Environment &Environment::instance() {

   if( !instance_ )

       instance_= new OfficialEnv;

   return*instance_;

}

 

Avoid global variables. Safer and more flexiblemechanisms are available to achieve the same results.

我的感受:到目前为止,代替全局变量的方法只有用函数封装它,但是这样仍然不能解决程序的耦合性,照样的无法重用和维护,用函数封装带来的好处就上面的两小点,太少了.继续探索有没有其他好办法.

 

Gotcha#4: Failure to Distinguish Overloading from Default Initialization

void test(int i=0);

虽然可以:

test();

test(0);

但实际上void test(int i=0);和void test();是不一样.

void (*p)() = test;   //error

void (*p)(int) = test; //right

对于后者,应该:

void (*p)() = test;   //right

由此可见,void test(int i=0);和void test(int);的本质才是一样.

 

Gotcha #5: Misunderstanding References

引用和指针的最大区别:

引用是它的initializer的别名。它和指针最大的区别是指针有它自己的存储空间,而引用没有自己的存储空间。这儿需要澄清一个事实:

例:

#include "stdafx.h"

int main(int argc, char* argv[])

{

    int i= 16;

    int& ref = i;

 

    char* tt = 0;

    int h = *tt;

    printf("%d",h);

 

    return 0;

}

在Debug(代码没经过优化)下,引用是有自己的存储空间的。

11:       int i=16;

00401028  mov         dword ptr [ebp-4],10h

12:      int& ref = i;

0040102F  lea         eax,[ebp-4]

00401032  mov         dword ptr[ebp-8],eax   //Debug下引用有自己的存储空间,和指针完全相同

13:

14:       char*tt = 0;

00401035  mov         dword ptr [ebp-0Ch],0

15:       int h= *tt;

0040103C  mov         ecx,dword ptr[ebp-0Ch]

0040103F  movsx       edx,byte ptr [ecx]

00401042  mov         dword ptr[ebp-10h],edx

在Release(最大速度优化)下,引用是没有自己的空间的,因为引用确实不需要自己的空间,因为它是initializer的别名,任何对引用的操作,都可以用initializer本身去代替。上面产生的汇编代码就只有一句:

00401000  movsx       eax,byte ptr ds:[0]

在Release(最大速度优化)下,如果连printf("%d",h);也不要了,那么上面的那段代码不会产生任何一条汇编代码。由此可见VC的编译优化是多么的厉害。任何没有用到的东西,在优化编译之后都去掉得干干净净。当然明显程序会挂的,因为优化后没有产生挂的那条代码,程序也不会挂。

 

下面两种情况也是以前不知道的:

A reference to an array preserves the array bound. Apointer to an array does not:

int ary[12];

int *pary = ary; // point to first element

int (&rary)[12] = ary; // refer to whole array

int ary2[3][4];

int (*pary2)[4] = ary2; // point to first element

int (&rary2)[3][4] = ary2; // refer to whole array

 

This property can be of occasional use when passingarrays to functions. (See Gotcha #34.)

 

It's also possible to bind a reference to a function:

 

int f( double );

int (* const pf)(double) = f; // const pointer tofunction

int (&rf)(double) = f; // reference to function

 

There's not much practical difference between aconstant pointer to function and a reference to function, except that thepointer can be explicitly dereferenced. As an alias, the reference cannot,although it can be converted implicitly into a pointer to function and thendereferenced:

 

a = pf( 12.3 ); // use pointer

a = (*pf)(12.3); // use pointer

a = rf( 12.3 ); // use reference

a = f( 12.3 ); // use function

a = (*rf)(12.3); // convert ref to pointer and deref

a = (*f)(12.3); // convert func to pointer and deref

 

Gotcha#6: Misunderstanding Const

 

Gotcha#7: Ignorance of Base Language Subtleties

(未看)

Gotcha#8: Failure to Distinguish Access and Visibility

 

Gotcha#9: Using Bad Language

 

Diction(n.用语)

cliquishadj.小集团的

只有纯虚函数,没有纯虚基类,而应该叫做虚基类。

C++中没有方法,而应该叫做成员函数。

pretentiousadj.自命不凡的

在谈论面向对象设计的时候,你说"message"和"method,"可能显得你很牛比,但是当具体到C++实现的时候,你就要说"function call" 和"memberfunction."了。在C++中时没有“method”之说的。

constructed的对应是destroyed,而不是destructed.

cast(or type conversion) operator指static_cast, dynamic_cast, const_cast, andreinterpret_cast

memberconversion operator指:

classC {

   operator int *()const; // a conversionoperator

   // . . .

};

虽然两者有时效果一样,但是你不要混淆了,应该知道谁是谁。

 

NullPointers

因为NULL在不同的平台上可能定义成不同的形式,所以使用NULL可能会影响你程序的移植性。Infact, there is no way to represent a null pointer directly in C++, but we'reguaranteed that the numeric literal 0 is convertible to a null pointer valuefor any pointer type. 所以real C++ programmers still use 0 to represent thenull pointer value.

他妈的以后再也不用NULL了,全部使用0代替:)

 

Acronyms(n.只取首字母的所写词)

C++程序员有使用只取首字母的所写词的弊病, though perhaps not to theextent managers do.Table 1-2 may be of use(有用) the next time one of yourcolleagues tells you that the RVO won't be applied to a POD, so you'd betterdefine a copy ctor.

Table 1-2. Meanings of common acronyms Acronym

POD    Plain olddata, a C struct

POF    Plain oldfunction, a C function

RVO    Returnvalue optimization

NRV    Named RVO

ctor  Constructor

dtor  Destructor

ODR    Onedefinition rule

记住这些常用的只取首字母的所写词,免得别人说起来你一点都不知道,那就丢人了。

 

Gotcha #10: Ignorance of Idiom

rhetoricadj.花言巧语的

meritn.价值

Iapprove of the quote and the chastening(精练的) sentiment(n.观点) behind it.

contourn.轮廓

handicappedadj.残疾的 n.残疾人

(没看懂)

 

Gotcha#11: Unnecessary(多余的) Cleverness(n.聪明)

provison.限制性条款,附文

(没看懂)

 

Gotcha#12: Adolescent Behavior

这部分告诉我们编程时不要像小孩子一样任性.为此,作者列举了某些程序员是如何任性的以及作为一个程序员,我们应该有什么样的职责。其它就没将什么了。

emotionaldevelopment n.情绪发展

cliquen.私党 vi.结党

impenetrableadj.费解的

decreen.法令 v.颁布

 

Chapter2. Syntax

typosn.打字稿

Gotcha#13: Array/Initializer Confusion

主要介绍了下面这种情况

int*ip = new int(12);

int*ip = new int[12];

inta[12]; // array of 12 ints

intb(12); // an int initialized to 12

Thestandard vector template is nearly as efficient as the hand-coded version butis safer, faster to program, and self-documenting. In general, prefer vector tolow-level arrays.

 

Gotcha#14: Evaluation Order Indecision(不确定)

cropup v.突然出现

a= f()+g() ? p() : q(); 等同于a = (f()+g()) ? p() : q(); 而不是a = f()+(g() ? p() :q());

不确定的情况主要有:

intresult2 = f( i, ri *= 2 );

a= p() + q();

 

Gotcha#15: Precedence Problems

学会使用指向成员变量和成员函数的指针就行。例

class C {

public:

   void f( int );

   int mem;

};

void C::f(int i){}

 

int main(int argc, char* argv[])

{

    void (C::*pfmem)(int) = &C::f;  //只能指向公有的成员函数

    int C::*pdmem = &C::mem;   //只能指向公有的成员变量

    C *cp = new C;

    cp->*pdmem = 13;     //->*是一个整体,是一个二元操作符,不能分开,它和->没有任何关系,不要混淆了

    (cp->*pfmem)(12);

 

    C c;

    void (C::*pfmem1)(int) = &C::f;

    int C::*pdmem1 = &C::mem;

    (c.*pfmem1)(12);

    c.*pdmem1=13;

 

    return 0;

}

注:->和.*叫做指向成员的指针操作符

 

Gotcha#16: for Statement Debacle(n.崩溃)

(没看,应该没多大用)

 

Gotcha#17: Maximal Munch Problems

(没怎么看,应该没什么用)

list<vector<string>>lovos; // error!

list<vector<string> > lovos;

 

Gotcha#18: Creative Declaration-Specifier Ordering

(没意思)

 

Gotcha#19: Function/Object Ambiguity

把这5例子搞懂就行了。

Strings( "Semantics, not Syntax!" ); // explicit initializer

Stringt;  // default initialization

Stringx(); // a function declaration

 

String*sp1 = new String();  // no ambiguityhere . . .

String*sp2 = new String;  // same as this

 

Gotcha#20: Migrating Type-Qualifiers

typedefint A[12];

externconst A ca; // array of 12 const ints

typedefint *AP[12][12];

volatileAP vm; // 2-D array of volatile pointers to int

volatileint *vm2[12][12]; // 2-D array of pointers to volatile int

 

typedefint FUN( char * );

typedefconst FUN PF; // earlier: function that returns const int

                     // now: illegal

 

 

看这本书的意义不大,暂停!

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值