【问题标题】:Move semantics: why is the destructor called on the moved instance and is that a problem?移动语义:为什么要在移动的实例上调用析构函数,这是一个问题吗?
【发布时间】:2019-08-20 06:25:00
【问题描述】:

我正在追赶现代 C++,练习移动语义。

我做了一个很简单的测试用例:

  • 创建实例
  • 移动-构造一个新实例

我注意到当我的实例被销毁时,两个析构函数都会被调用:

  • 移动构造实例之一,其中数据是有效的 指针
  • 原始实例之一,其中数据指针被删除 并在移动时设置为 nullptr

我的代码删除了一个 nullptr 让我很不舒服,这里有一些问题:

  • 是(删除 nullptr)甚至是一个有效的操作(即是否会导致 UB;它会 最终使我的应用程序崩溃)?
  • 或者是我的移动构造函数/移动赋值运算符定义 错了吗?
  • 我找到了这个similar question,但仍然不清楚,尤其是删除 nullptr 是否有问题。我应该通过检查析构函数中的指针来避免这种情况吗?如果是这样,感觉就像使用移动语义会导致某种系统性错误行为。

我的测试输出(下面的代码)是:

Test 5
        new 0x7512b0
        move_new 0x7512b0
        delete[] 0x7512b0
        delete[] 0

delete[] 0 的输出让我大吃一惊。

这里是主要的:

#include <iostream>
#include "test5.h"
int main()
{
    std::cout << "Test 5" << std::endl;

    test5 rule5;
    test5 rule5move = std::move(rule5);
    // rule5 = std::move(rule5move);

    return 0;
}

这里是 test5.h:

#ifndef TEST5_H
#define TEST5_H

class test5
{
public:
    test5(): data(new float[10]){
        std::cout << "\tnew " << data << std::endl;
        for (int i = 0; i < 10; i++)
            data[i] = float(i);
    }

    ~test5(){
        std::cout << "\tdelete[] " << data << std::endl;
        delete[] data;
    }

    // copy constructor
    test5(const test5& t) : data(new float[10]){
        std::cout << "\tcopy " << data << std::endl;
        std::copy(t.data, t.data + 10, data);
    }

    // copy operator
    test5& operator=(const test5& t){
        std::cout << "\tassign " << data << std::endl;
        std::copy(t.data, t.data + 10, data);
        return *this;
    }

    // move constructor
    test5(test5&& t): data(new float[10]){
        delete[] data;
        data = t.data;
        std::cout << "\tmove_new " << data << std::endl;
        t.data = nullptr;
    }
    // move operator
    test5& operator=(test5&& t){
        delete[] data;
        data = t.data;
        std::cout << "\tmove_assign " << data << std::endl;
        t.data = nullptr;
        return *this;
    }

private:
    float* data;
};

#endif // TEST5_H

【问题讨论】:

    标签: c++11 destructor move-semantics


    【解决方案1】:

    那(删除 nullptr)甚至是一个有效的操作(即,这会导致 UB;它最终会使我的应用程序崩溃吗)?

    删除nullptr 是无操作的。这是有效的。根据网上CPP reference

    如果表达式的计算结果为空指针值,则不调用析构函数,也不调用释放函数。

    我相信您的移动构造函数和移动赋值运算符不正确。为什么还要使用原始指针?

    如果您正在追赶现代 C++(正如您所提到的),您应该使用 smart pointers

    【讨论】:

    • 我还从 C++14 中读到,甚至没有调用析构函数,所以我的问题是......迟到了。是时候学习 C++17 了。
    猜你喜欢
    • 1970-01-01
    • 2015-11-04
    • 1970-01-01
    • 1970-01-01
    • 2012-04-29
    • 2014-08-26
    • 2023-03-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多