析构函数在c++中的工作方式

how the destructor works in c++

本文关键字:工作 方式 c++ 析构函数      更新时间:2023-10-16

这是我的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();
}