Effective C++ 第三版中文版 笔记

条款54 TR1 和 条款55 Boost


TR1 即 Technical Report 1 是 C++ standard committee 推出C++“0x”(与此类似的还有C++“89”,即89年的C++标准版本,这里指200X年讲推出的新版本) 之前的 一份技术文档那个,我们可以从中发现一部分即将被实现和规范的内容


C++标准程序库 包含了 STL , iostreams,locales ,并包含了C99标准程序库。


TR1 添加了智能指针tr1::shared_ptr , 一般化函数指针 tr1::function  hash-based容器,正则表达式以及另外10个组件的支持


TR1本身只是一份规范 ,Boost库是他的实作版本, 但是Boost并不仅限于此, 因为Boost本身是一个community ,所以他还在逐渐添加新的内容。



条款 2 尽量以const ,enum ,inline 置换 #define

关于常量的定义

#define PI 3.14

const int PI = 3.14

编译器在预编译的时候已经将PI替换为3.14, 所以当发生了任何错误的时候你在Debug或者Error list里能看到只是3.14 而不是PI , 如果恰巧这个#define 并不是你所写的,那这将使你非常苦恼。

str * const  stringaaa = "aaa"

当常量做常量指针时,记住将指针声明为const。 当然也可以const  str * const stringaaa = "aaa" 同时将所指向的值设为const

当常量做成员变量时

因为是常量,所以须声明为static 以致保留一份。

Class A

{

static const int PI ;

}

const int A::PI = 3.14;

编译器有时候会要求你写一个声明再写一个定义,因此可分开写。取决于编译器。


当#define 用于定义函数的时候。 #define COMPARE(a,b) f((a) > (b) ? a : b)

写这种宏时候你必须为所实参加上括号,否则会有意想不到的错误。//f 是一个被定义的函数

这样的写法同样和上述常量有同样的编译期问题,以及非常的难认。

所以可以用一个inline 函数代替

template<typename T>

inline T Compare (const T& a ,const T& b)

{

return f(a > b ? a : b);

}

这里inline可以告诉编译器尝试把函数放入寄存器中以提升效率。 const T& 以reference传递参数 省却了by value拷贝构造函数的开销。

结论, 避免使用#define可以在DEBUG阶段产生效果。但是对运行时的性能并不会产生影响。毕竟这只是一个预编译阶段就被干掉的东西。


条款3: 尽可能使用const

1.const 出现在星号的左边则说明被指物是常量,如果const出现在星号的右边则表示指针是常量

2.Iterator 迭代器 本身是一个指针, const Iteratort表示迭代器的指针地址不可改变,但是对象是可以变化的。

如果想保持对象也不能改变,应该使用const_iterator.

3.const 作为函数的返回类型的时候,当返回值为左值的时候往往能避免一些错误,比如 if( a*b = c ) 这里应该是 == 而不是 = ,*是一个被重载的操作符,如果返回值是const那么他就会报错。

4.两个只是constness不同的函数是可以被重载的

5.在一个const 的函数中 void fun() const 如果有一个变量声明为mutable, : mutable int count ;则表明在这个const函数中这个变量是一个特例,是可以被更改的。


条款4: 确定对象使用以前已经被初始化

1.使用初始化列表 initialization list 来初始化类 ,这样的代码更加高效,如果再构造函数体中进行初始化的,那仅仅是复制,编译器会先调用一次默认构造函数来对这些值进行赋值,然后再进入接下来的赋值阶段。

2.当一个类中调用另外一个类的static 类型时 不能保证这个static就被初始化类, 所以最好的办法是把这个类型的调用变成一个函数而不是直接调用。
并且这个函数是一个Singleton 
TestClass getNum()
{
static TestClass ts
return ts;
}  

TestClass getnum()
{
if(ts == null) // ts is defined in the class as member
ts = new TestClass();
return ts;
}


条款5: 了解C++ 默默编写并调用哪些函数

1.一个空的Class 编译器会为其声明一个 构造函数,一个析构函数,一个拷贝构造函数和一个赋值函数

2.但是只有当这个类被用到的时候,他们才会被编译器生成出来。默认的析构函数是non-virtual的,除非base-class是virtual的。
当你声明了一个构造函数之后,那么编译器将不会为你生成一个没有实参的默认构造函数。

3.拷贝构造函数和赋值函数执行的就是将成员变量赋值到新的对象中去。
但是如果成员变量是reference 或者 const的话,那么默认的这个两个函数将不会这行这样的操作。

4.如果一个基类的拷贝构造函数或者赋值函数是private的那么, 编译器就不会为他的继承类生成一个默认的拷贝构造函数和赋值函数。

由此我们引出了条款6

可以生成这样一个基类
class uncopyable
{
public:
uncopyable();
~uncopyable();
private:
uncopyable (const uncopyable & uc);
uncopyable& operator= (const uncopyable & uc); 
}
这样继承类就不会自动生成一些拷贝构造函数和赋值函数,而是需要你手动去生成。

这里的运算符重载,的返回值是引用类型的, 以为当返回值作为左操作数的时候,如果返回值为by value类型,那样就会指向一个野指针,因为by value的返回值其本体已经在操作符函数结束的时候被释放掉了。

如有一个 uncopyable& operator[](const int& n)
{
return test[n];  // n 为uncopyable class的一个char类型的局部变量
}
有一个对象 uc
uc[5] = ‘X’;
这样就会发生错误。
同理对于赋值函数
(x = y) =3;
无论这样的表达式是否合理,但是如果是by value的运算符重载的话,改变函数的返回值为3就不合法 ,纵使他合法因为返回的也只是一个返回值的副本而不是真正指向的对象的内部值,这样的行为也不是你想要的。



阅读更多
换一批

没有更多推荐了,返回首页