设为首页 加入收藏

TOP

C++:Memory Management(二)
2019-09-30 16:42:17 】 浏览:87
Tags:Memory Management
F(i+1); // placement new cout << lifs[i].lif << endl; }

直观地,placement new并不会分配内存,它只是在已分配的内存上构造对象。对应地,使用array new构造的对象需要使用array delete释放内存。

delete[] lifs;

相较于array new,array delete不需要提供数组长度参数。这是因为,在使用array new构造对象的时候,还有一块额外的空间用于存放cookie,也就是这块内存的一些信息,其中就包括这个内存块的大小和对象的数量等等。

class LiF {
public:
    //...
    ~LiF() { cout << "des" << endl; }
};

delete[] lifs; // array delete

此时我们显式地定义析构函数,并且在析构函数被调用时打印信息。在运行到delete[]的时候,程序就会根据cookie中的信息,准确地释放对应的内存块,本例中,“des”会被打印三次,即3个对象的析构函数都被调用了。此时如果错误地调用delete而非array delete,那么就可能会发生内存泄漏。

delete lifs; // delete

这时只会调用一次析构函数,但本例中并不会发生泄漏,这个简单的类中并没有包含其他对象。再看下面这种情况:

class LiF2 {
public:
    LiF2() : lif(new LiF()) {}
    LiF2(const LiF& _lif) : lif(new LiF(_lif.lif)) {}
    ~LiF2() { delete lif; lif = nullptr; }
private:
    LiF* lif;
};

LiF2* lif2 = new LiF2[3];
delete lif2; // call "delete" by mistake

这时,由于错误地使用了delete,析构函数只会被调用一次,也就是说,还有另外两个对象,虽然对象本身被销毁了,但对象中的lif指针所指的对象却没有被销毁,即:对象本身不会发生泄漏,泄漏的是对象中指针保存的内存

深入placement new

之前提到的new()操作以及new expression拆解的第三步,其实都是placement new。在主动使用placement new时,它的一般格式为:

new(pointer)Constructor(params);
// or
::operator new(size_t, void*);

它的作用是:把对象(object)构造在已分配的内存(allocated memory)中。同样也可以在vcruntime_new.h中找到相关定义:

#ifndef __PLACEMENT_NEW_INLINE
    #define __PLACEMENT_NEW_INLINE
    _Ret_notnull_ _Post_writable_byte_size_(_Size) _Post_satisfies_(return == _Where)
    _NODISCARD inline void* __CRTDECL operator new(size_t _Size, _Writable_bytes_(_Size) void* _Where) noexcept
    {
        (void)_Size;
        return _Where;
    }

    inline void __CRTDECL operator delete(void*, void*) noexcept
    {
        return;
    }
#endif

可以看到,placement new并没有做任何工作,它只是把我们传递的指针又return了回来。结合下面的例子就不难理解这个逻辑。

class LiF {
public:
    LiF(int _lif = 0): lif(_lif) {}
    int lif;
};

LiF* lifs = new LiF[3]; // array new
LiF* lif = new(lifs)LiF(); // placement new

我们在array new得到的LiF对象数组中的第一个对象上使用了placement new,同样拆解这个new操作可以得到类似上面普通new的一个try/catch块:

LiF* lif;
try {
    void* mem = operator new(sizeof(LiF), lifs); // placement new
    lif = static_cast<LiF*>(mem); // static type conversion
    lif->LiF::LiF(); // constructor
} catch(std::bad_alloc) {
    // exception handling
}

此外,在__PLACEMENT_NEW_INLINE宏还包含了一个placement delete的定义:

inline void __CRTDECL operator delete(void*, void*) noexcept
{
    return;
}

可以看到,它也是不做任何工作的,所谓的placement delete只是为了形式上的统一。

总结

  • 内存的申请释放可以在不同层面上进行,但只要是在操作系统之上,都是基于malloc/free。
  • C++ primitive层,通常使用new和delete系列,new是对malloc的封装,delete是对free的封装。
  • 通常new是指new expression。严格来说,new的含义有三种:new expression、operator new和placement new。new expression是operator new和placement new的复合,operator new负责内存的申请,placement new负责对象的构造;此外还有new[]。
  • 所有的内存申请/释放操作都必须配套使用。
首页 上一页 1 2 下一页 尾页 2/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇符号表-定长数组实现符号表 下一篇多态

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目