当前位置: 首页 > 知识库问答 >
问题:

C++析构函数调用了错误的对象?

闻人栋
2023-03-14

我是C++的新手,我写了一个小程序来了解赋值如何处理对象。这个页面(http://www.cplusplus.com/doc/tutorial/classes2/)的cpp文档提示我这样做。在这一页上,它指出:

隐式版本[复制赋值操作符]执行浅层复制,这适用于许多类,但不适用于具有指向对象的指针的类,这些对象处理其存储。在这种情况下,不仅类会冒两次删除指向对象的风险,而且赋值会通过在赋值之前不删除对象指向的对象而造成内存泄漏。

class Test{
public:
    int id;
    explicit Test(int id) : id(id) {
        cout << "Created " << id << endl;
    }
    ~Test() {
        cout << "Destroyed " << id << endl;
    }
};

int main() {
    Test x = Test(1);
    x = Test(2);

    cout << x.id << endl;
    return 0;
}

我所期望的输出是:

1:创建1
2:销毁1?(这是一个我不确定的问题,因为网站暗示如果对象被另一个对象“替换”而不是超出作用域,将不会调用此析构函数)
3:创建2对象2'替换‘对象1,因为它被分配给X
4:2对象2的id值打印出来
5:销毁2对象2超出作用域时销毁

相反,我得到了以下输出:

Created 1
Created 2
Destroyed 2
2
Destroyed 2

其次,由于对象2的析构函数已经被调用,我们可以假定它已经被销毁。2的下一个输出似乎与此矛盾,因为它表明X仍然保存着对象2(预期的,但与调用其析构函数相矛盾)。

我不太清楚为什么会这样。

最后,输出destroyed 2。如果我们不早点看到这一点,这是有道理的。对象2存储在X中,因此当它超出作用域时,将调用析构函数。

共有1个答案

贺景山
2023-03-14

使用调试器,当行x=Test(2)时,Created2和Destroy2都显示;叫做。如果我们刚刚给对象2分配了x,为什么它的析构函数会立即被调用呢?接下来是下一部分。

x=Test(2);首先使用构造函数参数2创建Test。这就是生成Created2的原因。然后,这个无名的测试被分配给x,它给x.id提供值2。这个无名的测试在表达式末尾被销毁,生成“destroyed 2”。

其次,由于对象2的析构函数已经被调用,我们可以假定它已经被销毁。下一个输出2似乎与此矛盾,因为它表明x仍然持有对象2(预期的,但与它的析构函数的调用相矛盾)。

正如本答案的第一部分所表示的,销毁的不是x,而是临时的tempx.id仍然有效,并将产生它的新值2。

最后,输出销毁2。如果我们不早点看到这一点,这是有道理的。对象2存储在x中,因此当它超出作用域时,将调用析构函数。

x在函数末尾被销毁时,就会发生这种情况。它的ID值被前面的赋值更改为2,因此生成“Destroyed 2”。

编辑:看来您可能担心资源泄漏。由于test没有管理任何资源,因此不会发生泄漏,编译器生成的成员行为良好。如果您的类确实管理资源(通常以动态分配内存的形式),那么您将需要应用3/5/0规则。值得注意的是,您需要自己实现赋值操作符,以便它清理以前保存的任何资源。仅仅实现析构函数是不够的,因为它不涉及赋值。

 类似资料:
  • 更新1:根据建议添加了打印“this”。 更新2:拆分成几个文件,尝试阻止gcc优化。 更新3:记录复制构造函数并输入添加函数。 更新4:在main中添加了Clang和第二个cout的输出。 我希望参数析构函数作为函数中的最后一条语句被调用。从今以后,我希望下面的代码能够提供以下输出。 使用MSVC(Visual Studio)时,输出与预期的一样。但GCC(4.8.2-19ubuntu1)输出以

  • 类 类是对某一事物的抽象描述,具体地讲,类是C++中的一种构造的数据类型。它即可包含描述事物的数据,又可包含处理这些数据的函数,类在程序运行时是被用作样板来建立对象的。所以要建立对象,首先必须定义类。 定义类 定义一个类的一般格式为: class 类名{     private:         成员表1;     public:         成员表2;     protected:     

  • 我正在尝试编译以下C代码(另存为example4.cpp) 此代码给出以下错误: example4.cpp:在函数'Conconexpr double nth(double, int)':example4.cpp:24: 9: error:调用非Conconexpr函数'std::basic_ostream 谁能推荐一下吗? 谢谢你。

  • 基本问题:程序何时在C中调用类的析构函数方法?有人告诉我,每当对象超出范围或受到时都会调用它 更具体的问题: 1)如果对象是通过指针创建的,并且该指针后来被删除或给定一个新的地址来指向,它所指向的对象是否调用其析构函数(假设没有其他东西指向它)? 2) 接下来是问题1,什么定义了对象何时超出范围(与对象何时离开给定的{block}无关)。换句话说,什么时候对链表中的对象调用析构函数? 3) 你想手

  • 与《 构造函数》类似,C# 中的析构函数(也被称作“终结器”)同样是类中的一个特殊成员函数,主要用于在垃圾回收器回收类实例时执行一些必要的清理操作。 C# 中的析构函数具有以下特点: 析构函数只能在类中定义,不能用于结构体; 一个类中只能定义一个析构函数; 析构函数不能继承或重载; 析构函数没有返回值; 析构函数是自动调用的,不能手动调用; 析构函数不能使用访问权限修饰符修饰,也不能包含参数。 析

  • 主要内容:析构函数的执行时机创建对象时系统会自动调用构造函数进行初始化工作,同样,销毁对象时系统也会自动调用一个函数来进行清理工作,例如释放分配的内存、关闭打开的文件等,这个函数就是析构函数。 析构函数(Destructor)也是一种特殊的成员函数,没有返回值,不需要程序员显式调用(程序员也没法显式调用),而是在销毁对象时自动执行。构造函数的名字和类名相同,而析构函数的名字是在类名前面加一个 符号。 注意:析构函数没有参数,