C++1(Effective C++)

1.const,#define,宏

1.1  对于单纯变量,最好以const对象或enum替换#define

#define ASPECT_PATIO 1.653     ASPECT_PATIO 可能没进入记号表,得追踪源头;ASPECT_PATIO 替换1.653可能导致目标码出现多份1.653

const与#define区别

const常量有数据类型,编译器类型检查,const常量调试,宏没有;const可以用来修饰函数参数,函数返回值;常量函数

1.2   对于形似函数的宏,最好改用inline函数替换#define

宏:

写一个标准宏MIN,输入两个参数并返回较小的一个

#define MIN(A,B)((A)<=(B)?(A):(B))  注意;宏是方便的产生嵌入代码的唯一方法;三重条件操作符对编译器来说可以产生比if—then—else更优化的代码;

宏中参数必须括号括起来。

宏定义可以实现类似于函数的功能,但是它终归不是函数,而宏定义中括弧中的“参数”也不是真的参数,在宏展开的时候对“参数”进行的是一对一的替换。

1)谨慎地将宏定义中的“参数”和整个宏用用括弧括起来。所以,严格地讲,下述解答:   

#define MIN(A,B) (A) <= (B) ? (A) : (B)
#define MIN(A,B) (A <= B ? A : B )
都应判0分;   
2)防止宏的副作用。   
宏定义#define MIN(A,B) ((A) <= (B) ? (A) : (B))对MIN(*p++, b)的作用结果是:   
((*p++) <= (b) ? (*p++) : (b))   
这个表达式会产生副作用,指针p会作2次++自增操作。  
除此之外,另一个应该判0分的解答是:   
#define MIN(A,B) ((A) <= (B) ? (A) : (B)); //加了";"

inline:一个函数被不断调用;函数只有简单的几行,且函数不包括for,while,switch等语句。

2.确定对象被使用前已经被初始化

2.1   为内置型对象进行手工初始化,因为C++不保证初始化他们

2.2  构造函数成员初值列代替赋值操作;初值列出的成员变量,排列次序与他们在class中声明次序相同  A::A(int a,int b,string c,string d):aa(a),bb(b),cc(c),dd(d){}

2.3  为免除“跨编译单元初始化次序”的问题,请以local static对象替换non-local static对象

static对象:寿命构造出来到程序结束,不是stack对象,heap-based对象,而是包括global对象,定义于namespace作用域的对象,class内,函数内,file作用域内被声明为static的对象。

local-static对象:函数内的static对象,反为non-local static对象。

编译单元:指产出单一目标文件的那些源码。基本上它是单一源码文件加入所含的头文件。

Singleton单例模式常用的一个实现手法。

解决问题:”定义于不同编译单元内的non-local 对象“的初始化次序并无明确定义。

class FileSystem{.....};
FileSystem &tfs()
{
static FileSystem fs;
return fs
}
class Directory{...};
Directory::Directory(params)
{
...
std::size_t disks=tfs().numDisks()     //numDisks()  是FileSystem的成员函数
...
}
Directory&tempDir()
{
static Directory td;
return td;
}

3. 编译器暗自为class创建default构造函数,copy构造函数,copy assignment操作符,以及析构函数。

注意:

Empty(const Empty &rhs){}copy构造函数参数不是Empty rhs因为会死循环,一直copy构造

声明了一个构造函数,编译器是不再为它创建default构造函数

4.若不想使用编译器自动生成的函数,就该明确拒绝

问题:不希望class支持特定机能,不声明就可以了,但copy构造函数和copy assignment操作符有人调用,则编译器会声明,如何阻止?

答:1.将成员函数声明为private故意不实现,但不绝对安全,member函数和friend函数还是可以调用。

         2.阻止copying动作设计base class内,copying动作在基类的private中。

5.别让异常逃离析构函数

析构函数绝对不要吐出异常。如果一个析构函数调用的函数可能抛出异常,析构函数应该捕捉任何异常,然后吞下它们或者结束程序。

如果客户需要对某一个操作函数运行期间抛出的异常做出反应,那么class应该提供一个普通函数(不是在析构函数中)执行该操作。

6.宁以pass-by-reference-to-const替换pass-by-value(不适用于内置类型,STL和函数对象)

原因有两:

 一.传递效率高,没有任何构造函数和析构函数被调用,因为没有任何新对象被创建。传值可能调用数据成员的构造动作,本类的构造函数,被继承类的构造函数的构造动作等等,同理还有相同步骤的析构函数。

二.以by reference方式传递参数可以避免对象切割问题。

子类对象实参->基类形参  子类中特性被弃

7.必须返回对象时,别妄想返回其reference

返回reference时,这reference是某物的另一个名称,得存在这个对象,可以创建新对象

一.stack上创建新对象(糟糕代码)

const Rational &operator*(const Rational& lhs,const Rational& rhs)
{
Rational result(lhs.n*rhs.n,lhs.d*rhs.d);//栈上创建对象
return result;
}

没有避免调用构造函数;返回了local对象,而local对象在函数退出之前被销毁了

二.heap内构造一个新对象(糟糕代码)

const Rational &operator*(const Rational& lhs,const Rational& rhs)
{
Rational *result=new Rational(lhs.n*rhs.n,lhs.d*rhs.d);//堆上创建对象
return  *result;
}

存在一个重要问题,有new则应有delete,如果多个*运算,你无法合理delete

三.返回pointer或者reference指向一个local static对象

const Rational &operator*(const Rational& lhs,const Rational& rhs)(糟糕代码)
{
static Rational result;
result=...;
return  result;
}

出现问题:

1.多线程安全性

2. bool operator==(const Rational& lhs,const Rational& rhs);

if((a*b)==(c*d)){

    ...

}

else{

...

}

比较结果总相等,两次operator*调用有改变对象的值,但由于返回都是reference,调用端看到永远是static Rational对象的“现值”

你可能感兴趣的:(c++)