Code rule(2)

Guideline F-1: The use of preprocessor facilities (in particular #define) is to be avoided if alternative language facilities can be used. There are several reasons for this:

  • Constants defined by preprocessor directives can be evaluated differently depending on the scope, do not have type checking, can cause portability problems, can be impossible to debug.
  • Macros are: error-prone due to the need of parentheses around each argument, prone to side-effects when an argument is (unexpectedly by the caller) evaluated twice, hard to understand by the average developer, virtually impossible to debug, lacking of type checking, and often not properly recognized by cross reference tools, profilers, debuggers, ...

Alternatives:

  • Enumerated types can be defined using enum
  • Constants can be defined using const or enum
  • Macros representing functions can be replaced by static inline functions
  • Certain operations like min and max can be implemented with templates in a type-safe way.
  • Simple type substitutions can be defined using typedef

In C, static array initialization cannot be done with a constant and a #define is acceptable.

预处理器是一个处理代码输入并把输出作为另外一个程序输入的程序。在要交替变更使用语言工具时,要避免使用预处理器(尤其是#define),因为:

  • 用预处理器指令定义的常量,根据作用域的不同值可能不同,没有类型检查,可能导致移植问题,不能调试。
  • 宏指令:由于对每个参数需要圆括号,因此易于出错,当参数被估值两次时易于产生副作用,一般开发者难于理解,不能调试,缺乏类型检查,并且经常不能被交叉引用工具、分析工具、调试器等正确认识。

 

那么可以用下面的替代:

枚举变量使用enum,常量使用constenum;宏替代的函数使用static inline函数;像minmax这样的操作可以使用template,简单的类型替换可以使用typedef定义。

C中,静态数组初始化使用#define

 

C语言中,一些内置的宏:
这些内置的宏,不需要自己再define,直接拿来用就可以.
__LINE__
表示当前行号.
__FILE__
表示编译单元的文件名
__DATE__ 表示编译时的日期字符串
__TIME__ 表示编译时的时间字符串
__STDC__ 是否是符合标准,如果是结果为1,否则为0,这个主要是针对编译器编译

 

CPP处理预处理指令,#include,#define(undef), #if(ifdef,ifndef,else if,elif,endif), #error, #line, #pragma, #,##

 

#pragma用于向编译器提供信息.(由于是指令,所以无法用于宏定义,C99定义了_Pragma运算符)
#pragma once
可以用于防止头文件被重复包含(另外一种方式是使用条件编译)

 

 

Guideline G-1: Dangerous constructs
The use of goto, longjmp, and setjmp is not allowed. They do not just lead to code that is difficult to comprehend, but in addition there are limitations for where they can be used, e.g., it is not permitted to jump past a statement that instantiates a local object that has a destructor.

goto是本地的:它只能跳到所在函数内部的标号上,而不能将控制权转移到所在程序的任意地点.

setjmp()和longjmp()是非局部跳转,结合一个jmp_buf类型的变量,可以在栈上跳过若干调用帧,返回到当前函数调用路径上的某一个函数中。

setjmp()和longjmp()不能用于C++. 这是因为longjmp()不能识别对象。特别是当跳出某个作用域时,它不会调用对象的析构函数(调用析构函数的唯一证据是包含该对象的右大括号)。而析构函数的调用在C++中是必需的操作。实际上,C++标准中已经说明,使用goto跳入某个作用域(有效地跳过构造函数的调用),或使用longjmp()跳出某个作用域而且这个作用域的栈中有某个对象需要析构时,程序的行为是不确定的

Guideline G-2: Empty blocks
Empty blocks in conditional or loop statements must be braced. E.g.,

while (*(++foo) != '') {}

Guideline G-3: Switch: default branch
A switch statement shall always contain a default branch to catch unexpected cases. The default branch must always be put at the end of the switch statement.

One particular exception: if a switch is used on an enumerated type, it may be desirable to explicitly leave out a default branch. The compiler will then warn if a possible value of the enumeration is not covered in the switch, and this approach will thus improve consistency between the definition of the enumeration and the code that uses it. An example is a function that returns string representations of enumerated values.

Switch语句都要有一个default分支,并放在末尾,例外的情况是如果switch用于枚举类型时,可能这时default是明确需要省去的。

 

Guideline G-7: Multiple return statements
In general, functions shall have only one return statement and this must always be the last statement of the function. This makes it easier to understand the code flow.

In certain cases, multiple return statements may actually be desirable to reduce the complexity of the function (although a high complexity is typically a sign that the function itself should be split in smaller functions). A good example is input parameter checking. Instead of multiply nested if-else statements, single if blocks with an early return can keep the function readable. This pattern is also called the https://acos.alcatel-lucent.com/wiki/themes/fusionforge/images/http.pngGuard Clause pattern.

一般来说,函数只有一个返回语句并放在最后,这方便理解代码流。 但是,在实际中多返回语句有助于降低函数复杂性(虽然高复杂性是函数需要分解的标志)。入参检查就是一个很好的例子。用提前的return来代替多层嵌套的if-else能更好保持函数的可读性。

For example:

unsigned long notification_trigger(NotificationId notif, void *ptr)

{

    if (ptr == NULL) {

        return ERR_COM_INV_PARAM;

    }

 

    if (notif >= nr_notifications) {

        return ERR_COM_INV_PARAM;

    }

 

    if (is_notification_initialized() != TRUE) {

        return ERR_COM_NOT_READY;

    }

 

    .... (real code handling) ....

}

An alternative method to avoid nested if-else blocks, while still having a single return statement, is the use of a do...while(0) block with multiple break statements, for example:

do…while(0)和多个break语句,也能替代多层嵌套的if-else

T_status handle_request(unsigned short size)

{

    T_status ret = /* no error */;

    Message* messageP = 0;

 

    do {

        if (prepare_something() != 0) {

            ret = /* some useful value */;

            break;

        }

 

        messageP = new Message(size);

        if (messageP == 0) {

            ret = /* no resources */;

            break;

        }

 

        if (fill_message(messageP) != 0) {

            ret = /* blabla */;

            break;

        }

 

        if (send_message(messageP) != 0) {

            ret = /* more bla bla */;

            break;

        }

    } while (0);

 

    if (ret ! = 0) {

        /* cleanup */

        delete messageP;

    }

 

    return ret;

}

 

 

 

Guideline H-1: Scope
Declarations must limit the scope and visibility of each identifier as much as possible.

声明必须尽可能限制每个标识符的范围和可见性。

Guideline H-2: Always initialize local variables
Local variables in a function should always be initialized as their default value is undefined (they are created on the stack which is not automatically cleared). This includes simple values, arrays, pointers, dynamically allocated memory buffers, etc. But, take into account the next guideline:

函数中局部变量应该初始化,因为它们的默认值是未定义的(它们在栈上创建,但是stack不会自动清除)。这包括简单值,数组,指针,动态分配的内存缓冲区,等等。但是,考虑到下一个准则:

Guideline H-3: Late declaration and initialization
Generally, the declaration must be as close as possible to the first use of the declared object. This improves code readability, avoids unwanted hiding of the variable by another local variable of the same name, avoids unnecessary creation if the object is only conditionally used, and makes it more likely that the variable can be immediately initialized with the correct initialization data avoiding an unnecessary first initialization with default values. This also reduces memory and CPU resources.

An important exception: the body of loops shall not contain declarations unless they effectively depend on the loop iteration. This avoids repeated initialization of objects that are always the same.

一般来说,声明必须尽可能接近第一次使用声明的对象。这提高了代码的可读性,避免变量被另一个同名的局部变量隐藏起来;如果对象只是有条件地使用,可以使变量更可能立即初始化为正确的数据,避免不必要的第一次初始化为默认值,避免了不必要的创建。这也减少了内存和CPU资源。

一个重要例外:循环体不得包含声明,除非他们有效地依赖于循环迭代。这就避免了重复初始化相同的的对象。

effective C++》条款----尽可能地推迟变量的定义
不仅要将变量的定义推迟到必须使用它的时候,还要尽量推迟到可以为它提供一个初始化参数位置。

好处:

1.避免对对象进行不必要的构造,还可以避免无意义的缺省构造函数的调用。

2.在对变量初始化的情况下,变量本身的用途不言自明。在这里定义变量,有益于表明变量的含义。

总之,尽可能延后变量定义式的出现。这样可以增加程序清晰性并改善效率。

Guideline H-4: Prefer initialization over assignment
Variables are preferably initialized within the same statement where they are declared, i.e., int a = 42, thus preferring initialization over assignment. This is particularly important for pointers, which shall be initialized to 0 (NULL) or to a real object.

In C++, this also means preferring initializer lists rather than assignments in the body of the constructor. Example for initializer lists:

变量的声明和初始化最好在相同的语句,比如int  a = 42,最好在初始化时赋值。这对指针特别重要,指针需初始化为0(NULL)或一个真正的对象。

c++,这也意味着倾向于在构造函数初始化列表而不是在构造函数主体初始化。初始化列表的例子:

class Base;

 

class Refers

{

public:

    Refers(Base* base);

    ~Refers();

 

private:

    Refers(const Refers&);

    Refers& operator=(const Refers&);

 

    Base* base_m;

};

 

Refers::Refers(Base* base): base_m(base)

{

}

 

Refers::~Refers()

{

}

 

 

class Base

{

public:

    Base(char* c);

    virtual ~Base();

 

private:

    Base(const Base&);

    Base& operator=(const Base&);

 

    char* c_m;

};

 

Base::Base(char* c): c_m(c)

{

}

 

Base::~Base()

{

}

 

 

class Derived: public Base

{

public:

    Derived(char* c);

    virtual ~Derived();

 

 

private:

    Derived(const Derived&);

    Derived& operator=(const Derived&);

 

    unsigned long count_m;

    unsigned long maxcount_m;

    Refers ref_m;

 

};

 

Derived::Derived(char* c):

    Base(c),

    count_m(0), maxcount_m(0),

    ref_m(this)

{

}

 

Derived::~Derived()

{

}

 

 

int main()

{

    Derived d("When pigs fly");

 

    return 0;

}

In C++, initializing an object with another object through a copy constructor is preferred over a definition + assignment because it avoids the creation of a redundant temporary object. I.e., use

c++,通过拷贝构造函数初始化对象优于通过赋值函数初始化对象,因为它避免了建立一个冗余的临时对象。比如宜使用copy 构造函数

Foo a = b; // uses copy constructor

rather than

而不是使用默认构造函数和赋值函数

Foo a;  // temporary object created

a = b;  // uses assignment operator

See also Scott Meyers' "Effective C++ Third Edition" (First printing, May 2005), item 4 (Make sure that objects are initialized before they’re used.).

<<effective C++>>条款----确定对象使用前已经被初始化

对于内置的数据类型,可以手工完成初始化:

int x=0;  

const char* text="A C-style string"  

double d;  

std::cin>>d;//以读取input stream方式完成初始化  

对于内置类型以外的数据类型,则通过构造函数完成初始化。

在构造函数中完成初始化时,要区分赋值和初始化。在构造函数体内的是赋值,在初始化列表中的才是初始化。

下面的是赋值:

class Point{  

public:  

Point(int x_, int y_)  

 {  

 x=x_;  

     y=y_;  

   }  

 int x,y;  

}  

下面才是初始化,要注意,初始化顺序要和声明顺序一致。在一些情况下必须使用初始化方式,例如const和引用。

class Point{  

public:  

Point(int x_, int y_):x(x_),y(y_)  

  {  

   }  

 int x,y;  

}  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值