简体   繁体   English

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

[英]A pointer that can point to anywhere, how to determine if “delete” can be safely called on it?

Is there any way to distinguish two following situations at run time: 有没有办法在运行时区分以下两种情况:

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 !!!

I have find myself in situation in with I need to call delete operator for some unknown pointer. 我发现自己陷入困境,我需要为一些未知指针调用delete运算符。 The pointer can point to anywhere (to a "local" variable or to dynamically allocated variable). 指针可以指向任何位置(指向“本地”变量或动态分配的变量)。

How can I find out where my "unknown" pointer points, and therefore choose when to and when not to call operator delete on it 如何找出“未知”指针指向的位置,从而选择何时何时不调用operator delete




EDIT: Ok I see that everyone is pointing me to the smart pointers, but what if I am trying to write my own set of smart pointers (that is The reason behind my question)? 编辑:好的我看到每个人都指向我的智能指针,但如果我正在尝试编写我自己的一组智能指针(这是我的问题背后的原因)怎么办?

There is no way to test if a pointer is pointing to a memory area that would be valid to delete. 无法测试指针是否指向可以删除的内存区域。 Moreover, 此外,

  • There is no way to tell between pointers that must be freed with delete vs. delete[] , 没有办法告诉必须使用deletedelete[]释放的指针之间,
  • There is no way to tell between the pointers that have been freed and pointers that have not been freed, 在释放的指针和尚未释放的指针之间无法区分,
  • There is no way to tell among pointers to an automatic variable, pointers to static variable, and pointers to dynamically allocated blocks. 没有办法告诉指向自动变量,指向静态变量的指针和指向动态分配块的指针。

The approach that you should take is tracking allocations/deallocations by some other means, such as storing flags along with your pointers. 您应采取的方法是通过其他方式跟踪分配/解除分配,例如将标记与指针一起存储。 However, this is rather tedious: a much better practice is to switch to smart pointers, which would track resources for you. 然而,这是相当繁琐的:更好的做法是切换到智能指针,这将为您跟踪资源。

You need to set some better coding practices for yourself (or for your project). 您需要为自己(或您的项目)设置一些更好的编码实践。

Especially since most platforms have, at the very least, a C++11-compliant compiler, there's no reason not to be using the following paradigm: 特别是因为大多数平台至少都有符合C ++ 11标准的编译器,所以没有理由不使用以下范例:

  • Raw Pointers ( T* ) should ONLY be used as non-owning pointers. 原始指针( T* )应仅用作非拥有指针。 If you receive a T* as the input for a function or constructor, you should assume you have no responsibility for deleting it. 如果您收到T*作为函数或构造函数的输入,则应该假定您不负责删除它。 If you have an instance or local variable that is a T* , you should assume you have no responsibility for deleting it. 如果您有一个T*的实例或局部变量,您应该假定您没有责任删除它。
  • Unique Pointers ( std::unique_ptr<T> ) should be used as single-ownership pointers, and in general, these should be your default go-to choice for any situation where you need to dynamically allocate memory . 唯一指针( std::unique_ptr<T> )应该用作单一所有权指针,一般来说,这些应该是您需要动态分配内存的任何情况的默认选择。 std::make_unique<T>() should be preferred for creating any kind of Unique Pointer, as this prevents you from ever seeing the raw pointer in use, and it prevents issues like you described in your original post. std::make_unique<T>()应该是创建任何类型的唯一指针的首选,因为这可以防止您看到正在使用的原始指针,并且它可以防止您在原始帖子中描述的问题。
  • Shared Pointers ( std::shared_ptr<T> and std::weak_ptr<T> ) should ONLY be used in situations where it is logically correct to have multiple owners of an object. 共享指针( std::shared_ptr<T>std::weak_ptr<T> )应用于逻辑上正确的对象的多个所有者。 These situations occur less often than you think, by the way! 顺便说一句,这些情况的发生频率低于您的想象! std::make_shared<T>() is the preferred method of creating Shared Pointers, for the same reasons as std::make_unique , and also because std::make_shared can perform some optimizations on the allocations, improving performance. std::make_shared<T>()是创建共享指针的首选方法,原因与std::make_unique相同,也是因为std::make_shared可以对分配执行一些优化,从而提高性能。
  • Vectors ( std::vector<T> ) should be used in situations where you need to allocate multiple objects into heap space, the same as if you called new T[size] . Vector( std::vector<T> )应该用于需要将多个对象分配到堆空间的情况,就像调用new T[size] There's no reason to use pointers at all except in very exotic situations. 除非在非常奇特的情况下,否则没有理由使用指针。

It should go without saying that you need to take my rules of " ONLY do 'x'" with a grain of salt: Occasionally, you will have to break those rules, and you might be in a situation where you need a different set of rules. 它应该不用说,你需要我“ 只做 ‘X’”的规则,一粒盐:有时候,你必须打破这些规则,你可能会在你需要一组不同的情况规则。 But for 99% of use-cases, those rules are correct and will best convey the semantics you need to prevent memory leaks and properly reason about the behavior of your code. 但是对于99%的用例,这些规则是正确的,并且最好地传达了防止内存泄漏和正确推理代码行为所需的语义。

You cannot. 你不能。

Avoid raw pointers and use smart pointers, particularly std::unique_ptr . 避免使用原始指针并使用智能指针,尤其是std::unique_ptr It conveys clearly who is responsible for deleting the object, and the object will be deleted when the std::unique_ptr goes out of scope. 它清楚地传达了谁负责删除对象,当std::unique_ptr超出范围时,该对象将被删除。

When creating objects, avoid using new . 创建对象时,请避免使用new Wrap them in a smart pointer directly and do not take addresses of anything to wrap it in a smart pointer. 将它们直接包装在智能指针中,不要将任何地址包装在智能指针中。 This way, all raw pointers will never need freeing and all smart pointers will get cleaned up properly when their time has come. 这样,所有原始指针永远不需要释放,并且所有智能指针将在它们的时间到来时得到正确清理。


Okay, some things you can distinguish in a very platform-specific, implementation-defined manner. 好的,您可以通过特定于平台的实现定义方式区分某些内容。 I won't go into details here, because it's essentially insane to do (and, again, depends on the platform and implementation), but you are asking for it. 我不赘述了,因为它本质上是疯狂的做(和,再次,取决于平台和实现),但问它。

  1. Distinguish local, global and heap variables. 区分本地,全局和堆变量。 This is actually possible on many modern architectures, simply because those three are different ranges of the address space. 这在许多现代架构中实际上是可能的,因为这三者是地址空间的不同范围。 Global variables live in the data section (as defined by the linker and run-time loader), local variables on the stack (usually at the end of the address space) and heap variables live in memory obtained during run-time (usually not at the end of the address space and of course not overlapping the data and code sections, aka "mostly everything else"). 全局变量存在于数据部分(由链接器和运行时加载器定义),堆栈上的局部变量(通常位于地址空间的末尾)和堆变量存在于运行时获得的内存中(通常不在地址空间的末尾,当然不会重叠数据和代码部分,也就是“几乎所有其他内容”)。 The memory allocator knows which range that is and can tell you details about the blocks in there, see below. 内存分配器知道哪个范围,并且可以告诉您有关其中块的详细信息,请参见下文。

  2. Detect already-freed variables: you can ask the memory allocator that, possibly by inspecting its state. 检测已释放的变量:您可以通过检查其状态来询问内存分配器。 You can even find out when a pointer points into a allocated region and then find out the block to which it belongs. 您甚至可以找出指针指向已分配区域的时间,然后找出它所属的块。 This is however probably computationally expensive to do. 然而,这可能是计算上昂贵的。

Distinguishing heap and stack is a bit tricky. 区分堆和堆栈有点棘手。 If your stack grows large and your program is running long and some piece of heap has been returned to the OS, it is possible that an address which formerly belonged to the heap now belongs to the stack (and the opposite may be possible too). 如果您的堆栈变大并且您的程序运行时间很长并且某些堆已经返回到操作系统,则以前属于堆的地址现在可能属于堆栈(反之亦然)。 So as I mentioned, it is insane to do this. 正如我所提到的,这样做是疯狂的。

You can't reliably. 你不能可靠。 This is why owning raw pointers are dangerous, they do not couple the lifetime to the pointer but instead leave it up to you the programmers to know all the things that could happen and prepare for them all. 这就是为什么拥有原始指针是危险的,它们不会将生命周期与指针结合在一起,而是让程序员知道所有可能发生的事情并为所有事情做好准备。

This is why we have smart pointers now. 这就是为什么我们现在有智能指针 These pointers couple the life time to the pointer which means the pointer is only deleted once it is no longer in use anywhere. 这些指针将生命时间与指针结合在一起,这意味着指针只有在任何地方不再使用时才会被删除。 This makes dealing with pointer much more manageable. 这使得处理指针更易于管理。

The cpp core guildlines suggests that a raw pointer should never be deleted as it is just a view. cpp核心guildlines建议永远不要删除原始指针,因为它只是一个视图。 You are just using it like a reference and it's lifetime is managed by something else. 你只是像引用一样使用它,它的生命周期由其他东西管理。


Ok I see that everyone is pointing me to the smart pointers, but what if I am trying to write my own set of smart pointers (that is The reason behind my question)? 好吧,我看到每个人都指着智能指针,但如果我想编写自己的智能指针(这就是我的问题背后的原因)怎么办?

In that case do like the standard smart pointers do and take a deleter which you default to just using delete. 在这种情况下,像标准智能指针那样做,并采取默认只使用删除的删除。 That way if the user of the class wants to pass in a pointer to a stack object they can specify a do nothing deleter and you smart pointer will use that and, well, do nothing. 这样,如果类的用户想要传递指向堆栈对象的指针,他们可以指定一个什么都不做删除器,而智能指针将使用它,并且,什么都不做。 This puts the onus on the person using the smart pointer to tell the pointer how to delete what it points to. 这使得使用智能指针的人有责任告诉指针如何删除它指向的内容。 Normally they will never need to use something other than the default but if they happen to use a custom allocator and need to use a custom deallocator they can do so using this method. 通常,他们永远不需要使用默认值之外的东西,但如果他们碰巧使用自定义分配器并且需要使用自定义解除分配器,他们可以使用此方法。

Actually you can. 其实你可以。 But memory overhead occurs. 但是会发生内存开销。

You overload new and delete operator and then keep track of allocations and store it somewhere( void * ) 你重载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;
}

Now the most important function(You will have to work on this because it is not complete) 现在最重要的功能(你必须处理这个因为它不完整)

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";

}

Output 产量

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

Important Node 重要节点

  • This is just basics and just code to get you started 这只是基础知识,只是让您入门的代码
  • Open for others to edit and make necessary changes/optimization 允许其他人编辑并进行必要的更改/优化
  • Cannot use STL, they use new operator(if some can implement them please do,it would help to reduce and optimize the code) 不能使用STL,他们使用new运算符(如果有人可以实现它们,那将有助于减少和优化代码)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM