首页 > 编程问答 >free() on result of placement new?

free() on result of placement new?

时间:2024-06-07 14:50:51浏览次数:22  
标签:c++ c++14 language-lawyer

我试图了解在由 malloc() 分配的缓冲区中,对 placement new 的结果调用 free() 是否有效。

请考虑以下几点。这段代码是否表现出任何未定义的行为?

(假设 T 没有重载任何 new delete 操作符)

T* create() {
    void* buf = malloc(sizeof(T) + 10);
    T* obj = new(buf) T;
    返回 obj;
}

int main() {
    T* obj = create();
    obj->~T();
    free(obj); // or free(static_cast<void*>(obj)
}
malloc() 编辑的缓冲区上的 placement new 所返回的指针是否可以传递给 free() 呢?如果我们能保证 static_cast<void*>(obj) == buf ,这应该是很好定义的,但我不确定是否真的能保证这种平等; placement new 操作符 返回其参数不变,但我不确定是否允许我们假设 placement new 表达式 返回一个具有相同值/指向相同地址的指针。

如果没有定义这一点,那么是否有其他方法可以获得一个指针,该指针既可用于访问对象,也可用于释放对象,而不会遇到未定义的行为?


你在代码中观察到的行为是 未定义的 。虽然表面上看起来应该可以,但 C++ 标准并没有保证 placement new 表达式的结果与其第一个参数完全相同。

让我们来分解一下原因:

  1. placement new 的作用: placement new 运算符的作用是在内存的特定位置构造一个对象。它接受一个指向原始内存的指针作为参数,并在该位置构造对象。 关键在于,它返回的指针并不保证与传递给它的指针完全相同。

  2. C++ 标准的内容: C++ 标准 ( §6.7.4.2/2 ) 指出, placement new 表达式的结果是“指向已构造对象的指针”。它没有明确说明该指针必须与传递给 placement new 的指针完全相同。

  3. 编译器优化: 尽管标准没有强制要求,但一些编译器可能会优化 placement new 表达式,使其返回与输入指针相同的地址。但这只是一个实现细节,不能依赖它。

  4. free() 的要求: free() 函数需要传递给它的指针必须是之前由 malloc() calloc() realloc() 返回的指针。传递任何其他指针会导致未定义的行为。

那么,如何安全地释放内存呢?

为了安全地释放内存,你需要跟踪原始指针(即传递给 malloc() 的指针)。你可以这样做:

T* create() {
  void* buf = malloc(sizeof(T) + 10);
  T* obj = new(buf) T;
  return static_cast<T*>(buf); // 返回原始指针
}

int main() {
  T* buf = create(); // 存储原始指针
  T* obj = reinterpret_cast<T*>(buf); // 获取对象的指针
  obj->~T(); // 调用析构函数
  free(buf); // 释放原始指针指向的内存
}

在这个例子中,我们存储了 malloc() 返回的原始指针 buf 。然后,我们将它 reinterpret_cast 为 T* 来访问对象。最后,我们使用原始指针 buf 来释放内存。

其他选择:

  • 使用标准容器: 标准容器,如 std::vector ,可以为你管理内存分配和释放。
  • 使用自定义分配器: 如果你需要更精细的内存管理控制,你可以创建自定义分配器。

总而言之,虽然你的代码看起来合理,但它依赖于 C++ 标准没有保证的行为。为了确保你的代码是可移植和安全的,最好跟踪原始指针并使用它来释放内存。

标签:c++,c++14,language-lawyer
From: 78589716

相关文章

  • 【NOI】C++程序结构入门之循环结构二-for循环
    文章目录前言一、for循环1.导入2.语法3.使用场景4.条件控制5.小结二、例题讲解问题:1264-4位反序数问题:1085-寻找雷劈数问题:1057-能被5整除且至少有一位数字是5的所有整数的个数问题:1392-回文偶数?问题:1090-同因查找问题:1446.人口增长问题三、总结四、感谢......
  • C++ -- 引用
    什么是引用?引用其实就是一个变量的别名。也就是说,你可以通过引用的名称去访问原来的那个变量。其操作符很简单,就是在变量前面加上&。一个很简单的例子://variableinti;//referencevariablesint&r=i;i=5;cout<<"valueofiis:"<<i<<endl;......
  • C++11原子操作
    目录1.什么是原子操作2.为什么需要原子操作?3.C++中的原子操作4.原子操作使用及注意5.应用场景6.使用原子操作的最佳实践7.原子操作与锁机制的比较8.总结1.什么是原子操作        原子操作是一种不可分割的操作,即在多线程环境中,这些操作要么全部执行完成,要么......
  • C++中的priority_queue和deque以及适配器
    C++中的priority_queue和deque一丶priority_queue1.1priority_queue的介绍1.2priority_queue的使用1.3priority_queue的模拟实现二丶deque2.1deque的简单介绍2.2deque的缺陷2.3为什么要选择deque作为stack和queue的迭代器三丶容器适配器3.1什么是适配器3.2S......
  • C++ Template
    一、Template什么是template?重要性如何?下面我就说道说道:无性生殖不只是存在于遗传工程中,对于程序员而言,它也是一个由来已久的动作。过去,我们只不过是以一个简单而基本的工具,也就是一个文字编辑器,重复的复制代码。今天,C++提供给我们一个更好的繁殖方法:template。复......
  • 【C++进阶】深入STL之list:高效双向链表的使用技巧
    ......
  • C++Primer Plus第12章类和动态内存分配--再谈定位new运算符----12.8
    12.5.3再谈定位new运算符本书前面介绍过,定位new运算符让您能够在分配内存时能够指定内存位置。第9章从内置类型的角度讨论了定位new运算符,将这种运算符用于对象时情况有些不同,程序清单12.8使用了定位new运算符和常规new运算符给对象分配内存,其中定义的类的构造函数......
  • C++Primer Plus第12章类和动态内存分配--再谈定位new运算符----12.9
    该程序使用定位new运算符在相邻的内存单元中创建两个对象,并调用了合适的析构函数。#pragmaregion12.9placenew2.cpp//placenew2.cpp--newplacementnew,nodelete#if1#include<iostream>#include<string>#include<new>usingnamespacestd;constintBU......
  • C/C++ 枚举类型的注意事项
    枚举类型(enum)是C/C++的一种常用类型,它允许我们为一组整数值定义有意义的名称。然而,在使用枚举类型时,有几个重要的注意事项需要考虑:1.枚举的基础类型和值基础类型:默认情况下,枚举类型的基础类型是int,但你也可以明确指定其他整数类型(如enumclassColor:char{RED,GREEN,B......
  • c++“二纯” 纯虚函数和纯虚析构
    首先给出这样一段概念:在C++中,当基类包含纯虚函数时,这些纯虚函数在基类中不需要(也不能)有定义。但是,如果基类有一个纯虚析构函数(即析构函数被声明为纯虚函数),那么情况就有些特殊了。纯虚析构函数需要在基类中有声明,但通常也需要在类外提供一个定义(尽管这个定义通常只包含析构函数......