在阅读了很多关于这方面的文章之后,我想澄清下一点:
1 2 3 4 5 6 7
| A* a = new A();
A* b = a;
delete a;
A* c = a; //illegal - I know it (in c++ 11)
A* d = b; //I suppose it's legal, is it true? |
所以问题是如何使用已删除指针副本的值。
我已经读到,在C++ 11中,读取EDOCX1的值(0)会导致未定义的行为,但是读取EDOCX1的值1呢?
Trying to read the value of the pointer (note: this is different to
dereferencing it) causes implementation-defined behaviour since C++14,
which may include generating a runtime fault. (In C++11 it was
undefined behaviour)
What happens to the pointer itself after delete?
- 取消对无效内存的指针的引用总是未定义的行为-但是您的代码不会取消对这些指针的引用。
- 您可以将"a"或"b"分配给其他指针,但在每种情况下,它都将指向已释放的同一内存位置,因此如果您取消引用任何指针,则很可能崩溃。实际上,这是未定义的行为
- 为什么d=b是合法的,b与a的价值相同,这是非法的?
- "a*c=a;//非法-我知道"复制删除的指针不是非法的。在代码段的末尾,所有指针都指向一个已删除的实例,不能使用,但这里没有未定义的行为。
- 一点也不违法。只是有很多麻烦。删除后的两个分配都只会使您以后头疼。
- A* c = a;是合法的-只是有点愚蠢。
- @很多人认为delete修改指针本身,以某种方式掩盖了这种想法。
- 隐式转换为指向虚拟基类的指针可能会使程序崩溃。
两者:
在C++ 11中未定义,并且在C++ 14中定义了实现。这是因为EDCOX1 1和EDCOX1×2 }都是"无效指针值"(因为它们指向释放的存储空间),并且"使用无效指针值"是未定义的或实现定义的,这取决于C++版本。("使用"包括"复制的价值")。
C++ 11中的相关部分(EDOCX1×10)读取(强调添加):
If the argument given to a deallocation function in the standard library is a pointer that is not the null pointer value (4.10), the deallocation function shall deallocate the storage referenced by the pointer, rendering invalid all pointers referring to any part of the deallocated storage. The effect of using an invalid pointer value (including passing it to a deallocation function) is undefined.
非规范性说明:
On some implementations, it causes a system-generated runtime
在C++ 14中,同一部分读取:
If the argument given to a deallocation function in the standard library is a pointer that is not the null pointer value (4.10), the deallocation function shall deallocate the storage referenced by the pointer, rendering invalid all pointers referring to any part of the deallocated storage. Indirection through an invalid pointer value and passing an invalid pointer value to a deallocation function have unde?ned behavior. Any other use of an invalid pointer value has implementation-de?ned behavior.
非规范性说明:
Some implementations might de?ne that copying an invalid pointer value causes a system-generated runtime fault
- 型有趣的是,即使C++ 14(当前草案)也有:EDOCX1×13。我不清楚"未定义的效果"是指未定义的行为还是实现定义的行为。虽然它只是一个注释,因此不规范。
- 型@程序1234我把你链接的那个标记为这个的副本,因为解释清楚,所有相关的东西都被正确引用了。
- @用户2079303:新草案似乎将细节移动到[basic.stc]/4;但效果仍然是实现定义的:"当达到存储区域的持续时间结束时,表示该区域任何部分地址的所有指针的值都变成无效的指针值。通过无效的指针值进行间接寻址并将无效的指针值传递给释放函数具有未定义的行为。对无效指针的任何其他使用都具有实现定义的行为。"
- …[basic.stc.dynamic.safety]/4中的音符缺少这种细微差别,大概是为了简洁。
不应在delete后面使用指针。下面这个使用acessing a的例子是基于实现定义的行为。(感谢M.M.和Mankarse的指点)
我觉得在这里重要的不是变量a(或b、c、d,而是值(=释放块的内存地址),在某些实现中,当在某些"指针上下文"中使用时,它可以触发运行时错误。
这个值可能是一个右值/表达式,不一定是存储在变量中的值-所以我不相信EDOCX1的值(我使用松散的"指针上下文"来区别于在非指针相关表达式中使用相同的值,即相同的位集-这不会导致运行时错误)。
------------我的原始帖子如下。-
你的实验就快到了。只需添加一些cout,如下所示:
1 2 3 4 5 6 7 8
| class A {};
A* a = new A();
A* b = a;
std::cout << a << std::endl; // <--- added here
delete a;
std::cout << a << std::endl; // <--- added here. Note 'a' can still be used!
A* c = a;
A* d = b; |
调用delete a对变量a没有任何作用。这只是图书馆的电话。管理动态内存分配的库保留已分配内存块的列表,并使用变量a传递的值将以前分配的一个块标记为释放。
虽然Mankarse引用C++文档是真实的,但是:"呈现无效的所有指针指向释放的存储的任何部分"-注意变量EDCOX1(1)的值仍然未被触动(您没有通过引用传递,而是通过值传递)。.
因此,总结并尝试回答您的问题:
在delete之后,变量a仍存在于范围内。变量a仍然包含相同的值,这是为class A的对象分配(现在已经释放)的内存块的起始地址。技术上可以使用a的该值-例如,可以像在上面的示例中一样打印它-但是很难找到比打印/记录过去更合理的使用方法…您不应该做的是尝试取消引用这个值(它也保存在变量b、c和d中),因为这个值不再是有效的内存指针。
您永远不应该依赖于被释放的存储对象(尽管它很可能会在那里停留一些时间,因为C++不需要清除在使用后释放的存储)-您没有保证,也没有安全的方式来检查这一点。
- 正如曼卡斯的回答所解释的那样,delete a;可能改变a和b的价值(即使它们是通过价值传递的),技术上它们不能使用。另外,delete是一种语言特性,而不是库调用。作为delete操作的一部分,可能存在对存储释放函数的库调用。
- @M.M.delete a不能改变a和b的价值,这在技术上是不可能的,而且会违反许多事情的方法。
- @斯拉夫语在技术上是可能的,并且标准允许它(参见曼卡西的标准参考答案)。一种用法是,在调试构建中,编译器可以将a和b设置为已知的垃圾值,以帮助检测以后对它们的无效使用。有些编译器也会在调试模式下对未初始化的变量执行此操作。
- @M.M我不认为标准允许更改指针值,也不认为虚拟机实现这一点的方法,包括传递参数、从函数返回值、将它们存储在全局变量中等等。
- @M.M.-您能否参考一些文件来支持您的声明"delete a可能改变a和b的价值(即使它们是按价值传递的)"?@曼卡斯引用的段落没有规定;提到非规范C++ 14标准部分:"(…)复制无效指针值会导致系统生成的运行时故障"也不需要违反PASS逐值规则。
- @M.M:引用调试器内部功能与引用语言不同
- 请仔细阅读Mankarse答案中的标准引号,尤其是"呈现无效的所有指针"文本。指针之前有一个有效值,之后有一个无效值。因此值发生了变化。您是否认为有效值与无效值相同?你如何看待C++ 11下一个句子"使用无效指针值的效果是未定义的"?
- 我提到调试器是为了提供语言规则为何如此的基本原理。b没有传递给上面代码中的任何函数,所以我不知道您为什么要说b是按值传递的。
- @M.M:关于您的"指针之前有一个有效值,之后有一个无效值"的可能解释,请参阅我在上面更新的帖子。因此,我想传达的是,C++运行时不必改变一个值以使它无效,它可以把它列在某个无效的地方。修改某个变量并不能解决rvalues的问题。
- 当我说"更改值"时,我包括"将其列为无效"。(MMU确实保留了这样的清单)。也许你想知道的是"改变表现形式"。编译器也可以这样做,因为标准没有指定值和指针表示之间的特定关系。
- @M.M:你之前在这条线上的发言比上一条评论更具体。:-)希望在另一次讨论中再次见面。-)
这2行没有任何区别(意思是C++的合法性):
1 2
| A* c = a; //illegal - I know it (in c++ 11)
A* d = b; //I suppose it's legal, is it true? |
你的错误(这是很常见的)是,如果你在a上称delete,它会使它与b有任何不同。你应该记住,当你在指针上调用delete时,你通过值传递参数,所以内存,其中a指向delete之后,就不再可用了,但是这个调用不会使a与你例子中的b有任何不同。
- 型在末尾添加了一个链接-这就是为什么我认为a*c=a;是非法的
- 型这并没有改变我的回答,它们都是合法的,或者都是非法的,你在a上称delete并没有使它不同。
- 型我喜欢匿名的投反对票的人
- 型的确,这两行的合法性没有区别,但有人可能会读到你的答案,认为两者都是合法的(实际上两者都可能导致运行时错误)。delete不是一个函数,所以说"按值传递参数"是没有意义的。
- 型@m.m语义调用delete与调用函数没有区别。因此成为真正的用户不友好的网站-拒绝投票不是因为答案是错误的,而是因为"有人可能读你的答案并思考"
- 型调用delete与调用函数不同。
- 型我可以调用一个函数来调用delete,它有什么区别?我的指针会松开它的值吗?
- 型是的,指针也将在那里无效。
- 型@m.m void mydelete( T *p ) { delete p; }调用这个函数和直接调用delete在语义上有什么不同?
- 型没有语义差异
- 型@m.m它的值将变为无效,但我怀疑值会改变。尤其是对于指向内存块内部的指针。
- 型如果它以前是有效的,以后是无效的,那就意味着它变了
- 型@m.m那么mydelete可以改变参数,通过值传递吗?
- 型是的,没错。"传递值"是指被叫方收到值的副本,而不是其他任何内容。
- 型@M.M.恐怕他们需要更多的思考,语言会变成不可预测的野兽,我们不能依赖任何东西。你通过值传递一些东西,函数改变它。它不再是不变的。
- 型@slava,该行为已经存在而不涉及delete,参见示例
- 型@M.M.这不是正确的例子,对不起。我的意思是通过参数改变值。更有趣的是,持续的指针可以神奇地改变它的价值,哈哈。我认为这是真的——想要毁掉某件事,把它交给官僚们。
- 型@M.M.这个怎么样:Foo const *ptr = new Foo; delete ptr;会改变它的价值吗?
- 型另一个用例是,如果您在一个有专门用于内存地址的CPU寄存器的系统上,并且硬件在加载一个寄存器时执行检查,该寄存器的值对应于分配给您的进程的内存。在delete p;之后,内存可能会释放到OS,然后您将p加载到寄存器中,硬件会产生故障。
- 型@M.M我是说Foo *const ptr = new Foo; delete ptr;。
- 型是的,在这种情况下,ptr也会失效。
- 型@M.M.它的价值会改变吗?
- 型是的,它从有效更改为无效,并且您的断言可能失败或导致C++ 14中的硬件故障(或者恶魔在C++ 11中飞出您的鼻子)
- 型@m.m另一个问题出现在stackoverflow.com/questions/44191920/&hellip;