为什么即使在对象被删除后我也可以访问成员函数
Why I can access member functions even after the object was deleted?
我是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_自私()?
这就是它在C++中的工作方式吗?(删除后还有吗?)
你能检查一下它是否不在那里吗?(我知道理论上是不可能的,但我很好奇有什么方法)
所以,我的问题是,为什么即使在对象被删除后,我仍然可以调用Go_XXX_Your_Self()和Identify_Your_自私()?
因为未定义的行为。
这就是它在C++中的工作方式吗?(删除后还有吗?)
因为未定义的行为。不能保证它在其他实现中也能同样工作。同样,未定义的行为。
你能检查一下它是否不在那里吗?(我知道理论上是不可能的,但我很好奇有什么方法)
delete MC1;
MC1 = nullptr;
通过在delete
调用后将指针设置为nullptr
,运行时最有可能检测到您正在访问一个无效的、您无权使用的位置。此外,通过对所有适用的指针认真执行此操作,您可以检查对象是否有效(如果不是-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++标准修订版中,您可能还希望使用所提出的std::exempt_ptr
,它是一个非拥有的、仅观察的指针包装器。
在对象上调用delete
时,该对象使用的内存可供new
的另一个使用(或者,实际上,任何使用堆的东西)使用。在此之前,被删除的对象可能(或可能不)保留其以前的值。但最终,当你的程序继续运行时,被删除的对象使用的内存将被覆盖,如果幸运的话,坏事就会发生:你的程序会崩溃。如果运气不好,程序在部署到现场之前不会崩溃。
你运气不好。记忆还没有被改写,一切仍然正常。
其他时候你可能运气好,会摔得很厉害,这会突出问题。
访问delete
d的内存不是你应该做的事情。在C++中,你不应该这样做,因为编译器不会阻止你。
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->();
}
};
但是,这只会给每个指针访问增加额外的开销。更好的方法是利用一个好的静态代码分析工具,它可以帮助识别代码中的这些类型的问题以及其他许多问题。
机制是删除只是"告诉"系统不再需要内存,可以重新调整用途以再次使用。没有采取其他行动。这是在安全披露中出现免费错误后使用的基础。这取决于程序员是否不再使用内存。
为此,RAII是一种尝试减少此类问题事件的方法。
- 我们可以访问一个不存在的联盟的成员吗
- static_assert在宏中,但也可以扩展到可以用作函数参数的东西
- 有没有什么方法可以使用一个函数中定义的常量变量,也可以由c++中同一程序中的其他函数使用
- 函数是否可以访问传递给main()的参数
- 用于解析 win64 堆栈跟踪的命令行客户端(可以访问符号服务器)
- 为什么即使调用了析构函数,C++11 中的分离线程也可以执行
- 映射唯一值和重复值的有效方法.可以访问键或值的位置
- 访问我以前可以访问的功能时出现分段错误
- 为什么我可以访问其他班级的私人成员?
- C ++基础私有方法在将自身转换为派生类后可以访问吗?
- 为什么即使直到最后才定义实际类型,也可以将依赖名称视为完整
- 好友库函数可以访问子数据
- 如果类"A"具有指向另一个类的指针,则"B" A 可以访问 B 类公共方法
- 斯塔克,堆栈,也可以在底部和顶部添加整数
- Unity3d 中还有其他方法可以访问设备相机吗?
- 是否可以访问代码中的调用堆栈?
- 重塑Microsoft的并发::d iagnostic::span,也可以检测外部跨度
- 在不工作的情况下为数组分配指针,但反过来也可以
- 可以访问一个类中的播放器结构,但不能访问另一个类中的播放器结构
- 为什么即使在对象被删除后我也可以访问成员函数