全面解析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日

相关文章

  • C语言实现食堂就餐管理系统(带链表)

    C语言实现食堂就餐管理系统(带链表)攻略 1. 系统简介 本系统是基于 C 语言实现的食堂就餐管理系统,主要包含以下功能: 学生信息管理:添加、删除、修改学生信息; 就餐管理:学生进入、离开食堂,统计就餐人数; 就餐情况查询:按照就餐时间查询就餐学生名单。 2. 系统架构 本系统采用链表数据结构实现学生信息和就餐记录的存储和管理,主要包括以下模块: 学生信息…

    C 2023年5月23日
    00
  • c++ 如何在libuv中实现tcp服务器

    C++ 中通常使用 libuv 库来实现 TCP 服务器。下面是使用 libuv 库实现 TCP 服务器的完整攻略。 准备工作 首先需要做的是安装 libuv 库,并配置好 C++ 项目使其能够使用该库。Windows 操作系统可以直接下载预编译的库,然后在项目设置中配置库的路径和头文件路径。Linux 操作系统可以通过包管理器进行安装。 创建 TCP 服务…

    C 2023年5月23日
    00
  • ubuntu系统下C++调用matlab程序的方法详解

    关于在Ubuntu系统下C++调用matlab程序的方法详解,我整理了以下的攻略: 确认安装及配置 在开始调用matlab程序前,必须要确认系统已安装matlab软件及其C++编译器。同时,也需要设置matlab的环境变量来确保matlab程序能够被其他程序调用。 确认安装matlab 首先,可以在终端中输入以下命令,确认matlab是否已正确安装: mat…

    C 2023年5月23日
    00
  • Qt教程之QSqlQueryModel的使用详解

    Qt教程之QSqlQueryModel的使用详解 在Qt开发中,使用数据库是非常常见的需求。QSqlQueryModel是Qt提供的一个方便的数据模型类,可以与数据库进行交互,并提供了方便的方法进行数据的展示和编辑。本文将详细讲解如何使用QSqlQueryModel进行数据库的操作。 初始化QSqlQueryModel 在使用QSqlQueryModel进行…

    C 2023年5月23日
    00
  • C# 中如何使用Thread

    在C#中,我们可以使用Thread类来实现多线程编程。下面是使用Thread类来创建线程的详细攻略: 创建线程 要使用Thread实现线程,首先需要创建一个Thread对象,包含线程要执行的方法。 Thread thread = new Thread(new ThreadStart(ThreadMethod)); 此处ThreadMethod代表线程要执行的…

    C 2023年5月22日
    00
  • C++代码和可执行程序在x86和arm上的区别介绍

    下面是C++代码和可执行程序在x86和ARM上的区别介绍的攻略。 x86和ARM的区别 x86和ARM是两种不同的指令集架构。x86是发达国家使用最多的CPU架构之一,而ARM则是集成电路行业中应用广泛的CPU架构之一。 在x86架构中,CPU使用的指令集是复杂指令集指令集(CISC)。这意味着,CPU可以执行很复杂的操作,比如浮点数运算。C++代码在x86…

    C 2023年5月23日
    00
  • 在Visual Studio中用C++语言创建DLL动态链接库图文教程

    下面是详细的攻略: 1. 创建DLL项目 在Visual Studio中,选择新建项目,选择Visual C++ -> Win32 -> Win32 Console Application,命名为MyDll,勾选空项目,然后点击确定。 在弹出的向导中,在第二个页面,勾选“DLL”选项,然后继续完成后续创建过程,创建完成后,可以看到生成了MyDll…

    C 2023年5月23日
    00
  • Java如何自定义异常打印非堆栈信息详解

    如何自定义异常打印非堆栈信息 在Java程序中,当代码抛出异常时,异常信息中除了常见的堆栈信息(stack trace)外,还可以自定义异常信息和错误代码等非堆栈信息。这种自定义异常信息可以更加清晰地描述异常情况,便于程序员快速定位和排查问题。下面是一种标准的Java自定义异常的方式,结合代码实例进行说明。 自定义异常示例 在Java中,我们可以通过继承Ex…

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