全面解析C++中的new,operator new与placement new

全面解析C++中的new、operator new与placement new

在C++中,我们通常使用new来动态分配内存和构造对象。然而,在实际的工程开发中,一个新的问题就会被曝光:new虽然提供了一个比较便利的方法来分配内存和构造对象,但是也很容易引发一些内存方面的问题。例如:

  • new会抛出异常并终止程序,如果内存不足
  • new会调用构造函数来进行初始化
  • new分配的内存是连续的,因此不适用于某些特殊情况,例如一个大型的缓存池等

针对上述问题,我们需要使用到new的两个衍生版本:operator newPlacement new。下面将会对它们进行详细的讲解。

operator new

operator new并不是一个关键字,而是一个可重载的操作符。当我们使用new来动态分配内存时,实际上是调用了全局级别的operator new

void* operator new(size_t size);

operator new的工作方式与C语言中的malloc函数类似,它会尝试分配指定数量的未初始化的内存,并返回一个指向该内存的指针。但是需要注意的是,operator new的实现可能会受到编译器、操作系统等诸多因素的影响,因此可能会存在不同的实现方式。

同时,我们也可以通过new (std::nothrow)来使用operator new,在分配空间失败时不会抛出异常,而是返回一个nullptr。示例代码如下:

class MyClass {
public:
    MyClass(const std::string& str) : m_str(str) { }
    ~MyClass() { }
private:
    std::string m_str;
};

int main() {
    MyClass *p = new (std::nothrow) MyClass("Hello, world!");
    if (p == nullptr) {
        std::cout << "Failed to allocate memory" << std::endl;
    }
    else {
        std::cout << "Successfully allocated memory" << std::endl;
        delete p;
    }
}

placement new

operator new相对应的是placement new,同样也是一个可重载的操作符。placement new可以将对象的构造和内存分配操作进行分离,即先分配一块内存,再在该内存上进行对象的构造。以下是placement new的原型:

void* operator new(size_t size, void* p);

该函数会将一个指向某个内存地址的指针作为参数,并调用该内存地址上的构造函数。示例代码如下:

class MyClass {
public:
    MyClass(const std::string& str) : m_str(str) { }
    ~MyClass() { }
private:
    std::string m_str;
};

int main() {
    char buffer[sizeof(MyClass)];
    MyClass *p = new (buffer) MyClass("Hello, world!");
    p->~MyClass();
}

在示例代码中,我们使用了sizeof计算出MyClass对象的内存大小,并将该大小作为char类型数组buffer的大小分配了相应的内存空间。然后,我们使用placement new来初始化该空间,并手动调用了析构函数。

示例说明

下面我们将通过一个实际的示例来说明placement new的使用场景。

在开发某款游戏时,我们需要对游戏中出现的所有对象进行内存池优化,以提高游戏的性能并避免堆内存分配产生的碎片。在这种情况下,我们需要一种灵活、可扩展的内存分配机制,并且需要能够快速地分配相应大小的内存块。

我们可以通过类似于下面的代码来实现一个对象池的内存分配器:

class MemoryPool {
public:
    explicit MemoryPool(size_t size);
    ~MemoryPool();
    void* Allocate(size_t size);
    void Free(void* p);
private:
    char* m_buffer;
};

MemoryPool::MemoryPool(size_t size)
    : m_buffer(new char[size])
{
}

MemoryPool::~MemoryPool()
{
    delete[] m_buffer;
}

void* MemoryPool::Allocate(size_t size)
{
    // 省略池块分配逻辑
    return memory;
}

void MemoryPool::Free(void* p)
{
    // 省略池块释放逻辑
}

然后,我们需要在该内存池上分配对象时,可以通过以下方式实现:

class MyClass {
public:
    MyClass(const std::string& str) : m_str(str) { }
    ~MyClass() { }
private:
    std::string m_str;
};

int main() {
    MemoryPool pool(1024);
    MyClass *p = new (pool.Allocate(sizeof(MyClass))) MyClass("Hello, world!");
    p->~MyClass();
}

在示例代码中,我们首先创建了一个大小为1024字节的内存池pool,然后通过调用pool.Allocate(sizeof(MyClass))来分配一个大小为MyClass对象的内存块,并使用placement new来构造对象。最后,我们再手动调用对象的析构函数来释放分配的内存。这样就可以实现快速的对象池分配,并且避免了内存分配和析构带来的性能损失。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:全面解析C++中的new,operator new与placement new - Python技术站

(0)
上一篇 2023年5月22日
下一篇 2023年5月22日

相关文章

  • QT判断两个日期时间的大小

    判断两个日期时间的大小是一种常见的需求。在QT中,可以使用QDateTime类来实现这个功能。下面是详细的攻略: 方式一:使用QDateTime的比较运算符 QDateTime类提供了小于、小于等于、大于、大于等于、等于、不等于等比较运算符,可以通过它们来比较两个日期时间的大小。 示例1: QDateTime dt1 = QDateTime::fromStr…

    C 2023年5月22日
    00
  • VScode中C++头文件问题的终极解决方法详析

    下面是详细的攻略: VScode中C++头文件问题的终极解决方法详析 在使用VScode进行C++程序开发时,遇到头文件引用问题是非常常见的。本文将为大家介绍,在VScode中C++头文件问题的终极解决方法,以确保你在开发过程中能够顺畅地引用和编译代码。具体解决方法如下: 第一步:配置includePath 在VScode中,需要配置includePath,…

    C 2023年5月23日
    00
  • C#使用LitJson解析JSON的示例代码

    首先我们需要了解什么是JSON和LitJson,JSON是一种轻量级的数据交换格式,而LitJson则是一款C#的JSON序列化和反序列化库。 接下来,我们将用LitJson来解析JSON数据。以下是示例代码: 引用LitJson 在项目中引入LitJson.dll并添加LitJson命名空间 using LitJson; 创建一个类来接收JSON数据 在此…

    C 2023年5月23日
    00
  • c语言clock函数使用示例

    C语言Clock函数使用示例 Clock函数介绍 clock()是C语言标准库中的一个函数,其函数原型为:clock_t clock(void);。该函数通常用于计算程序运行的时间,单位为时钟周期(clock tick),可以通过除以CLOCKS_PER_SEC转换成秒。 需要注意的是,clock()函数返回的是程序自进程开始运行时起累计的时钟周期数,并不是…

    C 2023年5月23日
    00
  • c语言程序设计文件操作方法示例(CreateFile和fopen)

    “C语言程序设计文件操作方法示例(CreateFile和fopen)”是关于如何在C语言中使用CreateFile和fopen函数进行文件操作的攻略。下面将分别介绍CreateFile和fopen函数的使用方法,并提供两个示例说明。 CreateFile函数的使用方法 CreateFile函数是Windows操作系统中的一个API函数,用于创建或打开文件。其…

    C 2023年5月23日
    00
  • c++ 探讨奶牛生子的问题

    C++ 探讨奶牛生子的问题 问题描述 有 $N$ 只奶牛,每个奶牛的繁殖周期为 $M$ 年,每只奶牛出生后第 $1$ 年不生育,第 $2$ 年起每年生下一只小奶牛,小奶牛出生后第 $1$ 年也不能生育,第 $2$ 年起也可以生下一只小奶牛。假设所有的奶牛都没有死亡,请问 $T$ 年后一共有多少只奶牛? 解题思路 本题可以采用递归或者动态规划的方式进行求解。我…

    C 2023年5月23日
    00
  • C++中四种加密算法之AES源代码

    C++中四种加密算法之AES源代码 什么是AES算法 AES是Advanced Encryption Standard的缩写,它是一种对称加密算法,也是目前最常用的加密算法之一。AES算法可以对数据进行加密和解密,同时还能保证数据的完整性和安全性。 AES算法实现步骤 AES算法实现一般包含以下几个步骤: 密钥扩展:对输入密钥进行处理,扩展成多个轮密钥。 初…

    C 2023年5月23日
    00
  • 一文详解Qt中的对象树机制

    一文详解Qt中的对象树机制 什么是对象树机制? 在 Qt 中,每一个对象都有其父对象,这些对象之间形成了一种树形结构,我们称之为 对象树。当一个对象被创建时,可以设置它的父对象,然后它就会成为父对象的子对象,加入到对象树中。 Qt 中的对象树机制,可以实现对象之间的自动管理,并沿着树形结构进行自动的构建、销毁和内存管理。 对象树的作用 对象树机制的主要作用:…

    C 2023年5月22日
    00
合作推广
合作推广
分享本页
返回顶部