【问题标题】:Problem with move assignment in C++. Illegal instruction: 4C++ 中的移动赋值问题。非法指令:4
【发布时间】:2025-11-29 02:45:01
【问题描述】:

我正在编写一个简单的 Matrix 类,并且我已经定义了一个 operator+ 重载和一个移动赋值等。 当他们两个互动时,似乎发生了一些事情,但我找不到我错在哪里。 这是我的代码(我删除了所有多余的内容,只留下了显示错误所需的内容)。 有问题的行在最后,主要是:

#include <iostream>
#define DEF -1

using namespace std;
//----- Matrix -----//
class Matrix{
private:
    float **matrixpp;
    int dim_r;
    int dim_c;

public:
    Matrix(int d_r = DEF, int d_c = 0);   
    Matrix(const Matrix&);
    Matrix(Matrix&&);
    Matrix& operator=(Matrix&&);
    ~Matrix();

    Matrix operator+(const Matrix&);

    void print();
    void fill();

};

//----- Matrix -----//
Matrix::Matrix(int d_r, int d_c){

    if(d_r == DEF){
        do{
            cout << "number of rows: ";
            cin >> dim_r;
            if(dim_r <= 0){
                cout << "ERROR" << endl;
            }
        }
        while(dim_r <= 0);

        do{
            cout << "Number of columns: ";
            cin >> dim_c;
            if(dim_c <= 0){
                cout << "ERROR" << endl;
            }
        }
        while(dim_c <= 0);


    }
    else{
        dim_r = d_r;
        dim_c = d_c;
    }

    matrixpp = new float*[dim_r];

    for(int i = 0; i < dim_r; i++){
        matrixpp[i] = new float[dim_c];
    }

    for(int r = 0; r < dim_r; r++){
        for(int c = 0; c < dim_c; c++){
            matrixpp[r][c] = 0;
        }
    }
}

Matrix::Matrix(const Matrix& tocopy)
:matrixpp(tocopy.matrixpp), dim_r(tocopy.dim_r), dim_c(tocopy.dim_c)
{

    matrixpp = new float*[dim_r];

    for(int i = 0; i < dim_r; i++){
        matrixpp[i] = new float[dim_c];
    }

    for(int r = 0; r < dim_r; r++){
        for(int c = 0; c < dim_c; c++){
            matrixpp[r][c] = tocopy.matrixpp[r][c];
        }
    }
}

Matrix::Matrix(Matrix&& tomove)
:matrixpp(tomove.matrixpp), dim_r(tomove.dim_r), dim_c(tomove.dim_c)
{
    tomove.matrixpp = nullptr;

}

Matrix& Matrix::operator=(Matrix&& tomove){

    cout << "--- MA ---" << endl;
    matrixpp = tomove.matrixpp;
    dim_r = tomove.dim_r;
    dim_c = tomove.dim_c;

    tomove.matrixpp = nullptr;
}

Matrix::~Matrix(){

    if(matrixpp != nullptr){
        for(int i = 0; i < dim_r; i++){
            delete[] matrixpp[i];
        }
        delete[] matrixpp;
    }
}

Matrix Matrix::operator+(const Matrix& m){

    if(this->dim_r == m.dim_r && this->dim_c == m.dim_c){
        Matrix new_m(m.dim_r, m.dim_c);

        for(int r = 0; r < new_m.dim_r; r++){
            for(int c = 0; c < new_m.dim_c; c++){
                new_m.matrixpp[r][c] = this->matrixpp[r][c] + m.matrixpp[r][c];
            }
        }

        return new_m;
    }
    else{
        cout << "ERROR" << endl;
    }
}

void Matrix::print(){
    int temp;

    for(int r = 0; r < dim_r; r++){
        for(int c = 0; c < dim_c; c++){
            cout << matrixpp[r][c] << " ";

        }
        cout << endl;
    }
    cout << endl;
}

void Matrix::fill(){
    float temp;

    for(int r = 0; r < dim_r; r++){
        for(int c = 0; c < dim_c; c++){
            cout << "new value: " << endl;
            this->print();
            cin >> temp;
            matrixpp[r][c] = temp;
            system("clear");
        }
    }
}

//-------- Main -------//
int main(){

    Matrix m0;
    m0.fill();
    Matrix m1(0);

    m1 = m0+m0; // problematic line

    //m1.print();

}

当调用 move assignment 将 m0+m0 的结果移动到 m1 时,它给了我错误。

如果我在 Mac 上使用 g++ 编译我的代码,给出的错误只是“非法指令:4”,仅此而已。 如果我在 Linux 上这样做,给出的错误是:

在成员函数'Matrix Matrix::operator+(const Matrix&)'中: p.cpp:90:16:错误:使用已删除的函数‘constexpr 矩阵::矩阵(常量矩阵&)' 返回new_m; ^~~~~ p.cpp:9:7: 注意:‘constexpr Matrix::Matrix(const Matrix&)’被隐式声明为已删除,因为‘Matrix’声明 移动构造函数或移动赋值运算符类 Matrix{ ^~~~~~

提前谢谢你!

【问题讨论】:

  • 您在Matrix::Matrix 中的第二个do-while 循环包含一个错误。您输入dim_c 并检查dim_r &lt;= 0(注意cr)。它可能无法解决您的问题,但这仍然是一个错误。这就是复制粘贴代码的风险。另外,如果我这样做Matrix m(2, -2);会发生什么?
  • 请复制并粘贴编译器打印的完整错误。
  • 已编辑,感谢您的错误
  • 具有非void 返回类型的函数必须在每条路径上返回一些内容。你的编译器应该会警告你。

标签: c++ class operator-overloading move-assignment-operator


【解决方案1】:

问题是,当使用 C++17 之前的标准时,没有复制省略保证,因此从函数返回值遵循复制初始化语义,这需要定义移动或复制构造函数。定义您自己的移动赋值运算符会禁用自动构造函数生成 (Class copy CTOR generation rules)。实际上编译器提供的构造函数是定义的,但是被删除了。要解决这个问题,您必须根据五规则 (Rule of three/five/zero) 定义移动或复制构造函数,或两者都定义。

另外,我怀疑最初的问题(非法指令)是由代码中的许多未定义行为引起的(例如,赋值运算符中没有 return 语句,operator+ else 分支中没有返回)。

【讨论】:

  • 正如我所说,这是我整个程序的简化版本。在那里,我定义了移动和复制以及所有构造函数和赋值,但问题仍然存在。
  • 好吧。错误消息说明了其他内容。 ¯\_(ツ)_/¯
  • 我仍然收到“非法指令:4”错误消息。无论如何,关于没有返回的 else,我在编译时会收到警告。
  • 这些警告非常重要,您应该始终在具有非 void 返回类型的函数中返回一些内容。
  • 是的,我知道。但现在我只是在练习移动构造函数和移动赋值。你认为这可能是问题的原因吗?否则,您是否知道导致该错误的原因是什么?
【解决方案2】:

主要问题是您的 operator = 没有任何回报。

像这样重载运算符 +:

Matrix operator+(const Matrix &A ,const Matrix &m)
{
if(A->dim_r == m.dim_r && A->dim_c == m.dim_c){
    Matrix new_m(m.dim_r, m.dim_c);

    for(int r = 0; r < new_m.dim_r; r++){
        for(int c = 0; c < new_m.dim_c; c++){
            new_m.matrixpp[r][c] = A->matrixpp[r][c] + m.matrixpp[r][c];
        }
    }

    return new_m;
}
else{
    cout << "ERROR" << endl;
}

让它成为你班级的朋友。 并在 operator = 的末尾添加这样的 return 语句:

Matrix& Matrix::operator=(Matrix&& tomove){
        if(this != &tomove) { 
        cout << "--- MA ---" << endl;
        matrixpp = tomove.matrixpp;
        dim_r = tomove.dim_r;
        dim_c = tomove.dim_c;

        tomove.matrixpp = nullptr;
    }
    return *this;
}

【讨论】:

  • 它应该像这样工作吗?无论如何,为什么不按我的方式工作呢?
  • 为什么?当两个参数属于同一类型时,成员运算符重载非常好。这不会解决缺少复制构造函数的问题。