一个可以指向任何地方的指针,如何确定是否可以安全地调用"删除"
A pointer that can point to anywhere, how to determine if "delete" can be safely called on it?
有没有办法在运行时区分以下两种情况:
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
。直接将它们包装在智能指针中,不要获取任何地址将其包装在智能指针中。这样,所有原始指针将永远不需要释放,并且所有智能指针将在时机成熟时得到正确清理。
好的,你可以以一种非常特定于平台的、实现定义的方式区分一些事情。我不会在这里详细介绍,因为它本质上是疯狂的(并且再次取决于平台和实现),但您要求它。
区分局部变量、全局变量和堆变量。这在许多现代体系结构上实际上是可能的,仅仅是因为这三个是地址空间的不同范围。全局变量位于数据部分(由链接器和运行时加载程序定义),堆栈上的局部变量(通常在地址空间的末尾)和堆变量位于运行时获取的内存中(通常不在地址空间的末尾,当然也不会与数据和代码部分重叠, 又名"主要是其他一切")。内存分配器知道哪个范围,并且可以告诉您有关其中块的详细信息,请参见下文。
检测已经释放的变量:你可以问内存分配器,可能通过检查它的状态。您甚至可以找出指针何时指向分配的区域,然后找出它所属的块。然而,这样做的计算成本可能很高。
区分堆和堆栈有点棘手。如果您的堆栈变得很大,并且您的程序运行时间很长,并且某些堆已返回到操作系统,则以前属于堆的地址现在可能属于堆栈(也可能相反)。所以正如我所提到的,这样做是疯狂的。
你不能可靠地。 这就是为什么拥有原始指针是危险的,它们不会将生命周期与指针耦合,而是让程序员知道可能发生的所有事情并为它们做好准备。
这就是为什么我们现在有智能指针的原因。 这些指针将生存期耦合到指针,这意味着指针只有在任何地方不再使用时才被删除。 这使得处理指针更易于管理。
cpp 核心公会建议永远不要删除原始指针,因为它只是一个视图。 您只是将其用作参考,其生命周期由其他内容管理。
好的,我看到每个人都在向我指出智能指针,但是如果我尝试编写自己的一组智能指针(这就是我问题背后的原因)怎么办?
在这种情况下,请像标准智能指针一样,并采用您默认仅使用删除的删除器。这样,如果类的用户想要传入指向堆栈对象的指针,他们可以指定一个不执行任何操作的删除器,而您的智能指针将使用该删除器,并且什么都不做。 这使使用智能指针的人有责任告诉指针如何删除它指向的内容。 通常,他们永远不需要使用默认以外的其他东西,但如果他们碰巧使用自定义分配器并且需要使用自定义分配器,则可以使用此方法执行此操作。
其实你可以。但会出现内存开销。
你重载new
和delete
运算符,然后跟踪分配并将其存储在某个地方(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,他们使用新的运算符(如果有些人可以实现它们,请这样做,这将有助于减少和优化代码)
- 在提升multi_index容器中,是否定义了"default index"?
- 在C++STL中是否有Polyval(Matlab函数)等价物?
- 检查输入是否不是整数或数字
- 是否可以初始化不可复制类型的成员变量(或基类)
- 在C++中,是否可以基于给定的标识符创建基类的新实例,反之亦然
- 是否可以通过C++扩展强制多个python进程共享同一内存
- 此代码是否违反一个定义规则
- 是否需要删除包含对象的"pair"?
- 是否可以从int转换为enum类类型
- 无论条件是否为true,if总是在c++中执行
- 如何找到大小'x'数组是否完全填充,在C++?
- 检查值是否在集合p1和p2中,但不在p3中
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- 检查 std::shared_ptr<> 的当前底层类型是否为 T
- 在c++中检查长方体是否尽可能快地重叠(无迭代)
- GL_SHADERSTORAGE_BUFFER位置是否与其他着色器位置冲突
- 子目录是否继承属性,例如add_definitions,include_directories和父Cmakelist.t
- 标准是否使用多余的大括号(例如 T{{{10}}})定义列表初始化?
- C/C++预处理器是否可以检测一些编译器选项
- "非静态数据成员之前需要构造函数" - 我是否使用"boost::variant"