Effective C++ 学习笔记 (1)

来源:互联网 发布:apache图片服务器搭建 编辑:程序博客网 时间:2024/05/17 03:56
Effective C++ 学习一 从c语言世界来到C++世界~~

Item1 优先使用const 和inline来取代#define
这个准则应该理解成优先依靠compiler而不是依靠preprocessor来检查程序的错误。
定义一个常量的格式 const int NUM_LIMIT = 100;
1  当定义常量指针的时候,事情略微变得复杂
    const char * pConst const = "is a constant pointer points to const";
2 定义一个类的常量成员变得简单。
class GamePlayer{
private :
    static const int NUM_TURNS = 5;//注意这里的NUM_TURNS仅是个变量的声明
    int scores[NUM_TURNS];
};
在函数的实现部分加入NUM_TURNS 的定义,这是必须,否则linker在link stage会提示出错。
const int GamePlayer::NUM_TURNS;

3 在你需要在class A编译阶段使用class A constant时候
如 
class GamePlayer {
private:
        enum {NUM_TURNS = 5};
        
        int scores[NUM_TURNS];
......
}

#define max(a, b) ((a) > (b) ? (a) : (b))
宏定义一些简单常用函数,可以减少调用函数的开销, 它同时has many drawbacks, 
int a = 5, b = 10;
max(++a, b);
max(++a, b+10);
看看会有什么奇怪的事情发生
用inline来代替macro define定义。
inline int max(int a, int b) { return a > b ? a : b; }
更近一步使用,使用Generic programming:
template<class T>
inline const T& max(const T& a, const T& b) {return a > b ? a : b;}

Item 2 优先使用iostream 而不是stdio.h。
1 iostream提供了更好的扩展性和类型安全的。

<iostream>和<iostream.h>
iostream是将std命名空间的成员引入程序,而iostream.h将global namespace中的成员引入,这样可能会导致namespace 污染。

Item 7 准备好内存溢出情况发生后如何处理
当使用new操作符分配堆内存的时候, 如果可用内村用光, 则会抛出bad_alloc 异常。 bad_alloc异常是有operator new导致的异常,他在内存请求不能被满足的时候(内存用光)被抛出。
按照c风格,你可能定义一个宏来处理out of memory异常,如
#define NEW (PTR, TYPE) try {PTR=new TYPE;} catch (std::bad_alloc&) {assert(0);}
ps: 其中assert是一个宏其对应的h文件为c <assert.h>和c++ <cassert> 该宏检查传递给它的表达式是否是非零, 如果是零,则返回一个出错信息并且调用abort中止程序执行。

但是这样就够了吗? 答案是不够。因为它忽略了new的多种分配内存空间的方法, 可以想到的是
new TYPE; new TYPE(construction parameters); new TYPE[buffer_length]
还有更加复杂的情况, 因为在C++中,用户可以自己定义其operator new的行为,这样算来, 可能的情况何止区区3,4种。所以这种方式不能满足我们的需求。
那应该怎么做呢?
答案是通过定义自己的out of memory handler函数来处理out of memory异常。调用set_new_handler【在<new>头文件中定义】
set_new_handler的声明大概是这个样子:
typedef void (*new_handler)();
new_handler set_new_handler(new_handler p) throw();

set_new_handler的使用方式如下:
定义你自己的handler函数, 然后装载到Global namespace当中去。
void noMoreMemory() {
    cerr<< "Unable to satisfy the memory request.";
    abort();
}

int main()
{
    set_new_handler(noMoreMemory);
    int * p = new int[10000000];
}
这样当请求内存不能被满足的时候, 首先打印出错误信息, 然后才会abort 程序。这样就比简单的core dump(清空寄存器,信息转存)要好的多。

可以装载新的handler就一样可以卸载handler, 具体:
set_new_handler(null);

当operator new不能分配足够的内存时, 它会重复执行调用handler function,直到内存请求可以被满足为止.
所以设计良好的handler函数应该可以完成以下动作中的一种:
1 分配更多内存或会使分配的内存以满足内存请求
2 安装不同的handler函数来处理该请求。 如果当前的handler函数不能处理当前的请求,但是它知道其它的handler函数可以处理这个请求, 所以它会装载另外一个handler函数。这样,在下次该请求不能被满足的时候,
新安装的handler function就会被调用来处理该请求。
3 卸载handler函数, set_new_handler(null),这样在处理out of memory时将抛出异常bad_alloc.
4 Throw exception: 如果自定义exception,需要继承bad_alloc,使其形成类层次结构。
5 not return: 默认行为是abort 或 exit.

对应于类来说:
class X {
public :
    static new_handler set_new_handler(new_handler p);
    static void * operator new (size_t size);
private:
    static new_handler currentHandler;
}

new_handler X::currentHandler;
new_handler X::set_new_handler(new_handler p){
    new_handler oldHandler = currentHandler;
    currentHandler = p;
    return oldHandler;
    
}

而对于X的operator new 的动作:
1 调用global的set_new_handler,来加载X的currentHandler
2 调用global的new操作, ::new 来进行内存分配。 如果内存分配不能满足,global new operator将会调用X的handler, 也就是刚被装载成为global 的handler, 如果new最终都不能满足内存分配请求, 它将抛出bad_alloc异常, 会被捕获, 同时会重新装载原来的global new-handler.并且重新抛出异常
3 如果分配请求成功 X的operator new会重新装载原来的global new handler.

在考虑一下, X的out of memory处理和X本身无关, 可以使用继承和template来生成可以重用的代码。
template <class T>
class NewHandlerSupport {
public:
    static new_handler set_new_handler (new_handler p);
    static void* operator new (size_t size);
private :
    static new_handler currentHandler;
}

template <class T>
new_handler NewHandlerSupport<T>::set_new_handler(new_handler p)
{
    new_handler oldHandler = currentHandler;
    currentHandler = p;
    return currentHandler;
}

template<class T>
void * NewHandlerSupport<T>::operator new (size_t t )
{
    new_handler globalHandler = std::set_new_handler(currentHandler);
    void * memory;

    try {
        memory = ::operator new (t);
    } catch (bad_alloc&) {
        std::set_new_handler(globalHandler);
        throw;
    }
    std::set_new_handler(globalHandler);
    return memory;
}


为Class X添加自己的set_new_handler就变成

class X: public NewHandlerSupport<X> {
    ....
}

对于new operator, 直到1993 ,对应于new 的out of memory, new在调用handler函数后 返回空指针(0)而不是抛出异常。支持这张形式的定义是Widget* wp = new (nothrow) Widget();

 












原创粉丝点击