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分;
((*p++) <= (b) ? (*p++) : (b))
这个表达式会产生副作用,指针p会作2次++自增操作。
#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对象的“现值”