一个可以指向任何地方的指针,如何确定"删除"可以安全地召唤它吗?

时间:2017-03-28 16:14:47

标签: c++

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

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




修改 好吧,我看到每个人都指着智能指针,但如果我想编写自己的智能指针(这就是我的问题背后的原因)怎么办?

5 个答案:

答案 0 :(得分:6)

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

  • 无法通过deletedelete[]之间的指针进行分析,
  • 无法区分已释放的指针和尚未释放的指针,
  • 无法告诉指向自动变量的指针,指向静态变量的指针以及指向动态分配块的指针。

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

答案 1 :(得分:5)

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

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

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

不言而喻,你需要遵守我的规则&#34; 做&#39; x&#39;&#34;与一粒盐:偶尔,你将不得不违反这些规则,你可能会遇到需要一套不同规则的情况。但是对于99%的用例,这些规则是正确的,并且最好地传达了防止内存泄漏和正确推理代码行为所需的语义。

答案 2 :(得分:4)

你不能。

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

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

好的,一些可以通过特定于平台,实现定义的方式区分的东西。我不会在这里详细介绍,因为它实际上是疯狂的(并且,再次,取决于平台和实现),但是 要求它。

  1. 区分本地,全局和堆变量。这在许多现代架构中实际上是可能的,因为这三者是地址空间的不同范围。全局变量存在于数据部分(由链接器和运行时加载器定义),堆栈上的局部变量(通常位于地址空间的末尾)和堆变量存在于运行时获得的内存中(通常不在地址空间的末尾,当然不会重叠数据和代码部分,又称&#34;主要是其他所有内容&#34;)。内存分配器知道哪个范围,并且可以告诉您有关其中块的详细信息,请参阅下文。

  2. 检测已释放的变量:您可以通过检查其状态来询问内存分配器。您甚至可以找出指针指向已分配区域的时间,然后找出它所属的块。然而,这可能在计算上很昂贵。

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

答案 3 :(得分:2)

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

这就是我们现在smart pointers的原因。这些指针将生命时间与指针结合在一起,这意味着指针只有在任何地方不再使用时才会被删除。这使得处理指针更容易管理。

cpp core guildlines表明永远不应删除原始指针,因为它只是一个视图。你只是像使用它一样使用它,它的生命周期由其他东西管理。

  

好的,我看到每个人都指着智能指针,但是如果我想编写自己的智能指针(这就是我的问题背后的原因)怎么办?

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

答案 4 :(得分:1)

其实你可以。但是会发生内存开销。

您重载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 deleted\n";
        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 you\n";

}
  

输出

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

重要节点

  • 这只是基础,只是让你入门的代码
  • 供他人开放以进行编辑和进行必要的更改/优化
  • 不能使用STL,他们使用new运算符(如果有人可以实现它们,那将有助于减少和优化代码)