Effective C++学习笔记(第八章)

2022-05-15  本文已影响0人  crazyhank
条款49:了解new-handler的行为
#include <iostream>
void OutOfMem()
{
    std::cout << "Entering OutOfMem Function" << std::endl;
}
int main()
{
    std::set_new_handler(OutOfMem);

    int *p = new int[10000000000000000L];
    delete [] p;

    return 0;
}

上面这段代码中,OutOfMem函数会被一直调用,所以系统提供这个接口的本意是希望用户在实现这个函数时回收内存。除非修改为以下代码,则会抛出异常:

#include <iostream>
void OutOfMem()
{
    std::cout << "Entering OutOfMem Function" << std::endl;
    std::set_new_handler(nullptr); //重新将new_handler设置为空!
}
int main()
{
    std::set_new_handler(OutOfMem);

    int *p = new int[10000000000000000L];
    delete [] p;

    return 0;
}
#include <iostream>
void OutOfMem_For_MyClass()
{
    std::cout << "Entering OutOfMem_For_MyClass" << std::endl;
};
class MyClass {
public:
    void* operator new(std::size_t size)
    {
        std::cout << "Entering MyClass::operator new" << std::endl;
        std::set_new_handler(OutOfMem_For_MyClass);
        auto res = ::operator new(size);
        std::set_new_handler(nullptr);
        return res;
    }
};
int main()
{
    MyClass* p = new MyClass();
    delete p;
    return 0;
}
条款50:了解New和Delete的合理替换时机
#include <iostream>
class A {
public:
    void* operator new(std::size_t size) {
        std::cout << "Entering A::operator new" << std::endl;
        return ::operator new(size);
    }
};
int main()
{
    A* p = new A();
    delete p;

    return 0;
}

值得注意的是,系统的默认的全局域operator new的定义形式为:

void* operator new(std::size_t size);

但是你也可以定义自己的operator new函数形式,但是第一个参数必须是std::size_t,如下:

#include <iostream>
class A {
public:
    void* operator new(std::size_t size, std::string str) {
        std::cout << "Entering A::operator new, str = " << str <<std::endl;
        return ::operator new(size);
    }
};
int main()
{
    A* p = new("TEST") A(); // 调用的是自己定义的operator new函数
    A* q = new A();  // 编译不通过,因为在子类域中定义了operator new(std::size_t, std::string)函数,它屏蔽了全局域中的operator new(std::size_t)函数。
    delete p;

    return 0;
}

替换系统默认的operator new和delete有以下应用场景:

条款51:编写New和Delete时需固守常规
class MyClass {
public:
    void* operator new(std::size_t size) {
        // 处理size为0的情况
        if (size == 0) {
            size = 1;
        }
        // 处理不是MyClass类对象的情况,因为有可能它被继承
        if (size != sizeof(MyClass)) {
            return ::operator new(size);
        }
        // 包含一个无限循环
        while (true) {
            尝试分配size个字节
            if (分配成功) {
                return 指针
            }
            // 尝试调用用户设置的new_handler,希望它能帮忙解决内存不足的问题
            std::new_handler globalHandler = std::set_new_handler(nullptr);
            std::set_new_handler(globalHandler);
            if (*globalHandler) (*globalHandler)();
            else std::throw std::bad_alloc();
        }
    }
};
class MyClass {
public:
    void operator delete(void* p, std::size_t size) {
        if (p == nullptr) return;
        if (size != sizeof(MyClass)) {
            ::operator delete(p, size);
            return;
        }
        归还p指向的内存
        return;
    }
};
条款52:写了Placement New也要写Placement Delete
#include <iostream>

class A {
public:
    // 定义一个定制的placement operator new
    void* operator new(std::size_t size, void *p) {
        return p;
    }
};

int main()
{
    char* buf = new char[sizeof(A)];

    A* p1 = new(buf) A();
    A* p2 = new A();  //编译不通过,由于定义了一个定制的placement operator new,所以看不到全局域中的operator new了
    delete [] buf;
    
    return 0;
}

所以这时候要在类中定义对应的operator new,如下所示:

#include <iostream>

class A {
public:
    // 定义一个定制的placement operator new
    void* operator new(std::size_t size, void *p) {
        return p;
    }
};

int main()
{
    char* buf = new char[sizeof(A)];

    A* p1 = new(buf) A();
    A* p2 = new A();  //编译不通过,由于定义了一个定制的placement operator new,所以看不到全局域中的operator new了
    delete [] buf;
    
    return 0;
}

注意:当你定义了placement operator delete时,它只会在使用placement operator new一个对象,并且对象构造函数出现异常时才会被调用,而一般写delete该对象指针时,它只会调用默认的operator delete。如下所示:

#include <iostream>

class A {
public:
    // 定义一个定制的placement operator new
    void* operator new(std::size_t size, void *p) {
        return p;
    }
    // 定义对应的placement operator delete
    void operator delete(void* p, void* pMem) {
        return;
    }

    // 这里假设用户只能使用placement operator new进行分配对象
    void operator delete(void *p, std::size_t size) {
        // 本示例中啥也不用做
        std::cout << "Entering operator delete(void*, std::size_t)" << std::endl;
        return;
    }
};

int main()
{
    char* buf = new char[sizeof(A)];

    A* p = new(buf) A();

    delete p;  // 它只会调用默认的operator delete,所以要实现它,并且要特殊处理

    // 在这里释放placement operator new用到的内存
    delete []buf;
    
    return 0;
}
上一篇 下一篇

猜你喜欢

热点阅读