Notes on Code Complete II: Variables

464 2014-08-16 12:00

# III Variables

# 10 General Issues in Using Variables

Abstract 变量的声明与使用遵循精准确定、单一集中原则

Keywords proximity principle

尽可能显式声明变量类型

在靠近使用的地方初始化变量,而不是一开始的附近。因为 Principle of Proximity(相关的东西要放一起)

  1. 可能在使用的时候早就被其他(很可能后来新加的)代码修改了
  2. 集中性的一次性初始化多个变量,会造成这些变量将在整个代码中用到的错觉
  3. 围绕这小片代码将来可能会加循环之类的结构,导致bug

在靠近使用的地方声明、定义变量

尽量使用final和const

初始化0是一个不错的选择,Intel处理器0xCC, 有些debugger容易识别的有0xDEADBEEF

尽量让变量短命 live time = 最初到最后对变量引用的行数,比如,一开始选择最保守的visibility,需要时再拓展

使用常量不要hard-coding,用类似private static final int COLOR_BLUE = 0xFF; 或者color = ReadTitleBarColor() 越早绑定优化程度越高,越迟绑定灵活性越高(更高的灵活性会导致更高的复杂性)。看如何权衡。

Use each variable for EXACTLY one purpose.

避免hybrid coupling: pageCount=-1表示错误

# 11 The Power of Variable Names

Abstract 变量的命名极为重要且有章可循

Keywords 8~20, qualifier, naming conventions

命名要充分、准确。当然一串对于该变量的描述是最准确的,但是,很可能会长得离谱。

按what命名,不按how命名。能用problem domain的名称就不要用计算机方面的名称。

研究表明平均变量名长度10~16是debug最容易的,其次是8~20。

Good example: numTeamMembers, teamMemberCount, numSeatsInStadium, seatCount, teamPointsMax, pointsRecord.

太长或太短的命名就完全不能用吗?当然不是,更长的变量适合于少用到的变量或者全局变量,更短的变量适合于局部或者loop里的变量。

为了避免命名冲突,有namespace的编程语言用namespace,没有的在variable名称前加前缀。

如果命名要用qualifier(限定词),比如Total, Sum, Average, Max, Min, Record, String, or Pointer, 把限定词放到命名最后。除了num,放在前面代表总数,放在后面代表index,所以尽量不要用num,而是用Count后缀和Index后缀。(我个人喜欢用i前缀)

用约定俗成的反义词

  • begin/end
  • first/last
  • locked/unlocked
  • min/max
  • next/previous
  • old/new
  • opened/closed
  • visible/invisible
  • source/target
  • source/destination
  • up/down

循环: 如果index仅限于循环内部,用i,j,k,如果1)循环外部也要用 2)循环过长 3)nested 则考虑有意义的名称

状态: 用比flag更有意义的状态变量名称

临时变量: 对临时变量持怀疑态度,能不用就不用

bool: 用done, error, found, success or ok. 用is前缀是一个不错的做法。用正向含义的名词,不要用类似于notFound这种。

不用的语言和项目有不同的naming conventions,P277有例子

如果两个变量的命名可以互换而不会影响程序,两者的命名是有问题的。

用数组file[]不要用file1, file2.

# 12 Fundamental Data Types

Abstract 数据类型们有着各自的使用规则。本章单独列出了许多C语言的注意事项,足以说明C语言在数据类型方面的使用很容易出错。

Keywords int, float, char, string, bool, enum, named constants, array, user-defined types

Avoid “magic numbers”(literal numbers such as 100 or 47524 出现在程序中而不加解释)唯一能够出现的数字literals应该是0和1,作为初始量或者增量。

Chars and Strings

  • All strings in Java are Unicode. 但是C/C++处理Unicode要用专门的库
  • 在软件开发早期就考虑国际化的问题

Strings in C

  • 区分string pointers和char arrays
    • C几乎所有的字符串操作都是通过strcmp(), strcpy(), strlen()等完成的,StringPtr = "text";此处“text”是指向literal text的指针,=并没有拷贝内容的作用
  • char string[NAME_LENGTH + 1] = { 0 }; 尽量用array of chars
  • strncpy(), strncmp() 不用strcpy(), strcmp(),因为至少有一个parameter保证函数一定会终止

bool: 当判断条件太多,用bool variable表示可读性更高

Enum

  • 与switch合用的时候,default处理错误的case
  • 为enum定义first last 元素用于enumeration (First(0), A(0), B(1), C(2), Last(2)) 这样一个loop就能搞定了

Array

  • 在C里面可以用宏#define ARRAY_LENGTH(x) (sizeof(x)/sizeof(x[o]))表示数组的长度

用户自定义类型的时候尽量以现实世界的实体命名,尽量用class而不是typedef

# 13 Unusual Data Types

Abstract 什么时候使用struct? pointer有哪些注意事项?使用全局数据导致的问题如何解决?

Keywords struct, pointer, memory corruption, global data

# struct

group related data

C/C++ struct vs. class: In C++, structs and classes are pretty much the same; the only difference is that where access modifiers (edit: for member variables, methods, and for base classes) in classes default to private, access modifiers in structs default to public. However, in C, a struct is just an aggregate collection of (public) data, and has no other class-like features: no methods, no constructor, no base classes, etc. Although C++ inherited the keyword, it extended the semantics. (This, however, is why things default to public in structs—a struct written like a C struct behaves like one.)

# pointer

= position + how to interpret contents from that position

最常见的错误是指针指向了不该指向的地方,(这时候向该内存写数据)会导致memory corruption.

解决策略很简单,尽早甚至一开始就规避这种错误

  • 把指针操作限制在函数或者类的内部
  • 定义声明和初始化在一起(proximity principle)
  • 对指针持怀疑态度,无论是指针本身,还是指针指向的变量,使用之前都要检查,怀疑他们超出范围了
  • 用额外的dog-tag空间(和原空间在一起)以检查memory corruption. e.g. isOK tag for free
  • 不要链条似地使用指针
  • 画图让问题更直观
  • 预留空间以在空间耗尽时备用
  • free/allocate pointers 放在同一scoping level
  • delete前要检查(并填充专门的垃圾数据),delete完要归NULL
  • 能不用指针还是不用指针的好

# C++ Pointer Pointers

pointers vs. references?

  • The most significant differences are that a reference must always refer to an object, whereas a pointer can point to NULL; and what a reference refers to can’t be changed after the reference is initialized.

Use pointers for “pass by reference”. Use const references for “pass by value”.

用smart pointer: unique_ptr vs. shared_ptr (there can only be one unique_ptr to any resource, any attempt to make a copy of a unique_ptr will cause a compile-time error.)

# C-pointer Pointers

使用显示类型的指针

尽量避免type-casting

使用 sizeof() 内存分配时决定变量大小

# Global Data

全局变量因为scope过大,导致该变量会对其他模块产生不可预料的影响,不到万不得已,不用全局变量。

不要直接访问类成员变量就好像他们是全局变量似的,即便编程语言允许你这样做。

  • 用access routines (like getter, setter in Java) 而非直接访问成员有什么好处呢?好处极多(P352),简单来讲
    • 模块化、灵活性:允许变量和内部的实现与外部的交互分离,而外部的访问方式保持不变,代码改变的时候(变量变量,变化的情况还蛮多的)就不会影响到太多其他文件。getter,setter两者的访问级别可以不同。

同样因为模块化,在调用数据成员时,能用access routines尽量用access routines,即便在类的内部也不例外

Don’t pretend you’re not using global data by putting all your data into a monster object and passing it everywhere 这是不必要的开销,要用就光明正大地用

© 2010-2018 Tian
Built with ❤️ in San Francisco