我是C ++的新手,从我到目前为止学到的东西,当你在一个指向堆上创建的东西的指针上调用delete时,该指针所指向的任何内容都会被擦除并释放内存,对吧?
然而,当我在一个简单的课上尝试这个时:
class MyClass
{
int _Id;
public:
MyClass(int id) : _Id(id)
{
std::cout << "$Constructing the damn thing! " << _Id << std::endl;
}
~MyClass()
{
std::cout << "?Destructing the damn thing! " << _Id << std::endl;
}
void Go_XXX_Your_Self()
{
std::cout << "%OooooooooO NOOOOOO! " << _Id << std::endl;
delete this;
}
void Identify_Your_Self()
{
std::cout << "#Object number: " << _Id << " Located at: " << this << std::endl;
}
};
这些只是一些愚蠢的测试,看看删除是如何工作的:
int main()
{
MyClass* MC1 = new MyClass(100);
MyClass* MC2 = new MyClass(200);
MyClass* MC3 = MC2;
std::cout << MC1 << " " << MC2 << " " << MC3 << " " << std::endl;
MC1->Identify_Your_Self();
MC2->Identify_Your_Self();
MC3->Identify_Your_Self();
delete MC1;
MC1->Identify_Your_Self();
MC3->Go_XXX_Your_Self();
MC3->Identify_Your_Self();
delete MC2;
MC2->Identify_Your_Self();
MC2->Go_XXX_Your_Self();
MC2->Identify_Your_Self();
return 0;
}
这是输出:
$Constructing the damn thing! 100
$Constructing the damn thing! 200
0x3e3e90 0x3e3eb0 0x3e3eb0
#Object number: 100 Located at: 0x3e3e90
#Object number: 200 Located at: 0x3e3eb0
#Object number: 200 Located at: 0x3e3eb0
?Destructing the damn thing! 100
#Object number: 0 Located at: 0x3e3e90
%OooooooooO NOOOOOO! 200
?Destructing the damn thing! 200
#Object number: 4079248 Located at: 0x3e3eb0
?Destructing the damn thing! 4079248
#Object number: 4079280 Located at: 0x3e3eb0
%OooooooooO NOOOOOO! 4079280
?Destructing the damn thing! 4079280
#Object number: 4079280 Located at: 0x3e3eb0
所以,我的问题是,为什么即使在删除对象后我仍然可以调用Go_XXX_Your_Self()和Identify_Your_Self()?
这是如何在C ++中运行的? (即使你删除它之后还有吗?)
还可以检查一下它是否不存在? (我知道理论上是不可能的,但我很想知道那里有什么方法)
答案 0 :(得分:8)
所以,我的问题是,为什么即使在删除对象后我仍然可以调用Go_XXX_Your_Self()和Identify_Your_Self()?
这是如何在C ++中运行的? (即使你删除它之后还有吗?)
因为undefined behavior。无法保证它在其他实现中的工作方式相同。再次,undefined behavior。
还可以检查一下它是否不存在? (我知道理论上是不可能的,但我很想知道那里有什么方法)
delete MC1;
MC1 = nullptr;
通过在nullptr
之后将指针设置为delete
,运行时最有可能检测到您正在访问无效的,您没有权利的位置。此外,通过努力为所有适用的指针执行此操作,您可以检查对象是否有效(如果非nullptr
则有效。)
if(my_ptr) {
// my_ptr is most possibly valid (though you can still go wrong)
// use my_ptr
}
同样,当它们尚未初始化为某个有效地址时,您还应将原始指针设置为nullptr
。
MyClass* some_ptr = nullptr;
...
但是,如果您可以访问现代C ++ 11工具,最好不要使用原始指针,只需使用std::unique_ptr
或std::shared_ptr
(取决于您所需的语义) 。在未来的C ++标准修订版中,您可能还想使用proposed std::exempt_ptr
,这是一个非拥有的,仅观察指针包装器。
答案 1 :(得分:2)
当您在某个对象上调用delete
时,该对象使用的内存可供另一个new
使用(或者,实际上,使用该堆的任何内容)。在此之前,已删除的对象可能(或可能不)保留其先前的值。但最终,当你的程序继续运行时,被删除的对象所使用的内存将被覆盖,如果你很幸运会发生坏事:你的程序会崩溃。如果你运气不好,你的程序在现场部署之前不会崩溃。
答案 2 :(得分:0)
有些时候你可能会很幸运,你会很难崩溃,这会突出问题。
访问您delete
d的内存不是您应该做的事情。在C ++中,你不应该这样做,因为编译器不会阻止你。
答案 3 :(得分:0)
C ++编译器通常不会生成代码来代表您检查指针的有效性。如果您在意,您需要自己提供代码。由于访问释放的内存会调用未定义的行为,因此这样做的结果是不确定的。
你不应该操纵原始指针,而是依赖于其中一个智能指针。然后,你不太可能遇到这个错误,因为你将依赖指针超出范围以允许内存被破坏。
然而,即便如此,您仍可能遇到强迫发生不良事件的情况,例如:
std::unique_ptr<Foo> p(new Foo);
p->foo();
p.reset(0);
p->foo();
您可以尝试使用智能指针包装器在运行时捕获此类问题,该包装器会为您执行某种形式的检查:
template <typename T>
class CheckedSharedPtr {
std::shared_ptr<T> ptr_;
public:
//...
T * operator -> () const {
if (ptr_ == 0) {
//...throw something
}
return ptr_.operator->();
}
};
但是,这只会给每个指针访问增加额外的开销。更好的方法是使用一个好的静态代码分析工具,它可以帮助识别代码中的这类问题,以及许多其他问题。
答案 4 :(得分:0)
机制是删除只是告诉系统不再需要内存,可以重新调整用途以便再次使用。没有采取其他行动。这是安全披露中出现的免费使用后错误的基础。程序员可能不再使用内存。
为此,RAII是尝试减少此问题事件的一种方法。