一个可以指向任何地方的指针,如何确定是否可以安全地调用"删除"

A pointer that can point to anywhere, how to determine if "delete" can be safely called on it?

本文关键字:quot 是否 何确定 安全 删除 调用 一个 任何地 指针      更新时间:2023-10-16

有没有办法在运行时区分以下两种情况:

double ptr * = new double(3.14159);
double variable = 3.14159
double * testPtr_1 = ptr;
double * testPtr_2 = &variable;
delete testPtr_1  // fine... 
delete testPtr_2  // BIG RUN TIME ERROR !!!

我发现自己的情况是我需要打电话给delete接线员以获取一些未知的指针。指针可以指向任何位置(指向"局部"变量或动态分配的变量)。

我怎样才能找出我的"未知"指针指向的位置,从而选择何时调用和何时不调用operator delete




编辑:好的,我看到每个人都在向我指出智能指针,但是如果我尝试编写自己的一组智能指针(这就是我问题背后的原因)怎么办?

无法测试指针是否指向可有效删除的内存区域。此外

  • 无法区分必须释放delete和指针之间的指针。delete[]
  • 无法区分已释放的指针和未释放的指针,
  • 无法区分指向自动变量的指针、指向静态变量的指针以及指向动态分配块的指针。

您应该采取的方法是通过其他方式跟踪分配/解除分配,例如将标志与指针一起存储。但是,这相当乏味:更好的做法是切换到智能指针,这将为您跟踪资源。

你需要为自己(或你的项目)设置一些更好的编码实践。

特别是由于大多数平台至少具有符合 C++11 的编译器,因此没有理由不使用以下范例:

  • 原始指针(T*)只能用作非拥有指针。如果您收到作为函数或构造函数输入的T*,则应假定您没有删除它的责任。如果你有一个实例或局部变量是T*,你应该假设你没有责任删除它。
  • 唯一指针 (std::unique_ptr<T>) 应用作单所有权指针,通常,这些应该是您需要动态分配内存的任何情况的默认首选。std::make_unique<T>()应该是创建任何类型的唯一指针的首选,因为这可以防止您看到正在使用的原始指针,并且可以防止像您在原始帖子中描述的那样出现问题。
  • 共享指针(std::shared_ptr<T>std::weak_ptr<T>)应该在逻辑上正确的情况下使用,即一个对象的多个所有者。顺便说一下,这些情况发生的频率比您想象的要少!std::make_shared<T>()是创建共享指针的首选方法,原因与std::make_unique相同,还因为std::make_shared可以对分配执行一些优化,从而提高性能。
  • 向量 (std::vector<T>) 应该在需要将多个对象分配到堆空间的情况下使用,就像调用new T[size]一样。除非在非常奇特的情况下,否则根本没有理由使用指针。

不言而喻,你需要对我的">做'x'"的规则持保留态度:偶尔,你将不得不打破这些规则,你可能会处于需要一套不同规则的情况。但对于 99% 的用例,这些规则是正确的,并且将最好地传达防止内存泄漏和正确推理代码行为所需的语义。

你不能。

避免使用原始指针并使用智能指针,尤其是std::unique_ptr。它清楚地传达了谁负责删除对象,当std::unique_ptr超出范围时,对象将被删除。

创建对象时,请避免使用new。直接将它们包装在智能指针中,不要获取任何地址将其包装在智能指针中。这样,所有原始指针将永远不需要释放,并且所有智能指针将在时机成熟时得到正确清理。


好的,你可以以一种非常特定于平台的、实现定义的方式区分一些事情。我不会在这里详细介绍,因为它本质上是疯狂的(并且再次取决于平台和实现),但您要求它。

  1. 区分局部变量、全局变量和堆变量。这在许多现代体系结构上实际上是可能的,仅仅是因为这三个是地址空间的不同范围。全局变量位于数据部分(由链接器和运行时加载程序定义),堆栈上的局部变量(通常在地址空间的末尾)和堆变量位于运行时获取的内存中(通常不在地址空间的末尾,当然也不会与数据和代码部分重叠, 又名"主要是其他一切")。内存分配器知道哪个范围,并且可以告诉您有关其中块的详细信息,请参见下文。

  2. 检测已经释放的变量:你可以问内存分配器,可能通过检查它的状态。您甚至可以找出指针何时指向分配的区域,然后找出它所属的块。然而,这样做的计算成本可能很高。

区分堆和堆栈有点棘手。如果您的堆栈变得很大,并且您的程序运行时间很长,并且某些堆已返回到操作系统,则以前属于堆的地址现在可能属于堆栈(也可能相反)。所以正如我所提到的,这样做是疯狂的。

你不能可靠地。 这就是为什么拥有原始指针是危险的,它们不会将生命周期与指针耦合,而是让程序员知道可能发生的所有事情并为它们做好准备。

这就是为什么我们现在有智能指针的原因。 这些指针将生存期耦合到指针,这意味着指针只有在任何地方不再使用时才被删除。 这使得处理指针更易于管理。

cpp 核心公会建议永远不要删除原始指针,因为它只是一个视图。 您只是将其用作参考,其生命周期由其他内容管理。

好的,我

看到每个人都在向我指出智能指针,但是如果我尝试编写自己的一组智能指针(这就是我问题背后的原因)怎么办?

在这种情况下,请像标准智能指针一样,并采用您默认仅使用删除的删除器。这样,如果类的用户想要传入指向堆栈对象的指针,他们可以指定一个不执行任何操作的删除器,而您的智能指针将使用该删除器,并且什么都不做。 这使使用智能指针的人有责任告诉指针如何删除它指向的内容。 通常,他们永远不需要使用默认以外的其他东西,但如果他们碰巧使用自定义分配器并且需要使用自定义分配器,则可以使用此方法执行此操作。

其实你可以。但会出现内存开销。

你重载newdelete运算符,然后跟踪分配并将其存储在某个地方(void *)

#include<iostream>
#include<algorithm>
using namespace std;
void** memoryTrack=(void **)malloc(sizeof(void *)*100); //This will store address of newly allocated memory by new operator(really malloc)
int cnt=0;//just to count
//New operator overloaded
void *operator new( size_t stAllocateBlock ) {  
cout<<"in new";
void *ptr = malloc(stAllocateBlock); //Allocate memory using malloc
memoryTrack[cnt] = ptr;//Store it in our memoryTrack
cnt++;          //Increment counter
return ptr;     //return address generated by malloc
}  
void display()
{
for(int i=0;i<cnt;i++)
cout<<memoryTrack[i]<<endl;
}
int main()
{
double *ptr = new double(3.14159);
double variable = 3.14159;
double * testPtr_1 = ptr;
double * testPtr_2 = &variable;
delete testPtr_1; // fine... 
delete testPtr_2;
return 0;
}

现在最重要的功能(你将不得不处理这个,因为它不完整)

void operator delete( void *pvMem )
{
//Just printing the address to be searched in our memoryTrack
cout<<pvMem<<endl;
//If found free the memory
if(find(memoryTrack,memoryTrack+cnt,pvMem)!=memoryTrack+cnt)
{
//cout<<*(find(memoryTrack,memoryTrack+cnt,pvMem));
cout<<"Can be deletedn";
free (pvMem);
//After this make that location of memoryTrack as NULL
//Also keep track of indices that are NULL
//So that you can insert next address there 
//Or better yet implement linked list(Sorry was too lazy to do)
}
else
cout<<"Don't delete memory that was not allocated by youn";
}

输出

in new
0xde1360
0xde1360
Can be deleted
0xde1360
0x7ffe4fa33f08
Dont delete memory that was not allocated by you
0xde1360

重要节点

  • 这只是基础知识,只是帮助您入门的代码
  • 开放供他人编辑并进行必要的更改/优化
  • 不能使用STL,他们使用新的运算符(如果有些人可以实现它们,请这样做,这将有助于减少和优化代码)