析构函数在c++中的工作方式
how the destructor works in c++
这是我的c++代码:
class Sample
{
public:
int *ptr;
Sample(int i)
{
ptr = new int(i);
}
~Sample()
{
delete ptr;
}
void PrintVal()
{
cout << "The value is " << *ptr;
}
};
void SomeFunc(Sample x)
{
cout << "Say i am in someFunc " << endl;
}
int main()
{
Sample s1= 10;
SomeFunc(s1);
s1.PrintVal();
}
它返回给我的输出如下:
Say i am in someFunc
Null pointer assignment(Run-time error)
当对象按值传递给SomeFunc时,当控件从函数返回时,将调用对象的析构函数
我应该对吗?如果是,那么为什么会发生这种情况?解决这个问题的办法是什么???
Sample
按值传递给SomeFunc
,这意味着要进行复制。该副本具有相同的ptr
,因此当该副本在SomeFunc
返回时被销毁时,两个对象的ptr
都将被删除。然后,当您在main中调用PrintVal()
时,您将取消引用该无效指针。这是未定义的行为。即使这起作用,那么当s1
被破坏时,ptr
被再次删除,这也是UB。
此外,如果编译器未能消除Sample s1= 10;
中的副本,则s1
一开始甚至无效,因为当临时文件被销毁时,指针将被删除。不过,大多数编译器都避免使用此副本。
您需要正确实现复制或禁止复制。默认的复制ctor不适用于此类型。我建议将此类型设置为值类型(直接保存其成员,而不是通过指针保存),以便默认的复制ctor工作,或者使用智能指针保存引用,以便它可以为您管理按引用的资源,并且默认的复制tor仍将工作。
我真正喜欢C++的一点是,它对在任何地方使用值类型都非常友好,如果你需要引用类型,你可以将任何值类型封装在智能指针中。我认为这比其他具有带值语义的基元类型但用户定义的类型默认具有引用语义的语言要好得多。
您通常需要遵守三规则,因为您有一个指针成员
在您的代码示例中,为了避免您看到的未定义行为:
将第一个语句中的需要替换为必须。
由于SomeFunc()
按值获取参数,因此会复制传递给它的Sample
对象。当SomeFunc()
返回时,临时副本将被销毁。
由于Sample
没有定义复制构造函数,其编译器生成的复制构造函数只是复制指针值,因此两个Sample
实例都指向同一个int
。当一个Sample
(临时副本)被销毁时,该int
被删除,然后当第二个Sample
(原始副本)被摧毁时,它试图再次删除相同的int
。这就是你的程序崩溃的原因。
您可以更改SomeFunc()
以获取引用,从而避免临时副本:
void someFunc(Sample const &x)
和/或您可以为CCD_ 22定义一个复制构造函数,它分配一个新的CCD_。
当您传递函数的参数时,它被称为复制构造函数,但您没有,因此指针不会初始化。当它退出函数时,对象调用析构函数来删除统一指针,因此它出现了一个错误。
而不是
int main()
{
Sample s1= 10;
SomeFunc(s1);
s1.PrintVal();
}
尝试使用
int main()
{
Sample* s1= new Sample(10);
SomeFunc(*s1);
s1->PrintVal();
}
- 在 Eclipse 中添加库的工作方式是否与在 Visual Studio 中相同?
- OpenGL应用程序在不同的计算机上的工作方式不同
- 为什么我的代码在添加不相关的代码行后工作方式不同?
- wait_until在主线程而不是主线程中的工作方式不同吗? c ++
- 用于了解输入和输出流缓冲区实际工作方式的程序
- 查找函数在unordered_map中的工作方式是搜索键值
- 尝试使用 Qt 库中的 QPixmap 将图像拆分为多个块。关于他的复制方法的工作方式,我有什么不明白的吗?
- 容器类别在STL中的工作方式
- Unity 的 HLSL/Cg 预处理器工作方式错误?
- 无法让我了解 cin.get 和 cout 在这里的工作方式
- 不了解C 集合的工作方式
- 有人可以解释C 操作员=此处的工作方式
- 了解 declval 在copy_assignment情况下的工作方式
- 打开文件的正确模式是什么,以便 seekp() 的工作方式与在默认模式下打开的文件相同
- 了解文件页在技术级别的工作方式
- 战俘的工作方式不同,详细解释
- 切换到新编译器后,SSCANF 的工作方式有所不同
- 为什么三元运算符在编译时的工作方式与运行时不同?
- 对运算符删除覆盖的工作方式感到困惑
- 在两种情况下,铸造的工作方式不同