• 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();

  • 相关阅读:
    C语言的AES加密
    curl指定域名的IP
    gdb调试知识
    C++获取寄存器eip的值
    C++嵌入lua
    [置顶] python字典和nametuple互相转换例子
    【python】redis基本命令和基本用法详解
    xshell登录到CentOS7上时出现“The remote SSH server rejected X11 forwarding request.
    selinue引起的ssh连接错误
    SCP和SFTP相同点和区别
  • 原文地址:https://www.cnblogs.com/yuboyue/p/2109879.html
Copyright © 2020-2023  润新知