C++ 模板类的运算符重载

新手上路,请多包涵

自己写了一个matrix的模板类, 但是在重载运算符时,出现问题了。
下面是matrix类:

// matrix.h -- A simple matrix class;
#ifndef __MATRIX_H_
#define __MATRIX_H_

#include <iostream>
#include <ostream>
#include <fstream>
#include <string>
#include <cstring>
#include <cmath>
#include <vector>
#include <iomanip>

using namespace std;

template <typename T>
class matrix {
    private:
        vector<vector<T>> data;

    public:
        matrix(int nRow = 2, int nCol = 2){
            data.resize(nRow);
            for (int i = 0; i < nRow; i++) {
                data[i].resize(nCol);
            }
            cout << "Default Constructor" << endl;
        }
        matrix(matrix<T> & c):data(c.data){
            data = c.data;
            cout << "Copy Constructor" << endl;
        }
        matrix(matrix<T> &&c):data(std::move(c.data)){ 
            cout << "Move Constructor" << endl; }
        matrix<T> operator = (matrix<T> &c);
        matrix<T> operator = (matrix<T> &&c){ 
            data = move(c.data); 
            cout << "Move Assignment" << endl;
            return *this;
        }

        int row(){ return data.size();} // Get row of the matrix.
        int col(){ return (row() ? data[0].size() : 0);} // Get col of the matrix.
        bool is_empty(){return (row() == 0);} // check if the matrix is empty.
        bool is_square(){return (!is_empty()) && (row() == col());} // check if the matrix is a square matrix.

        void resize(int nrow, int ncol); // Resize matrix
        void display(); // print matrix.

        T &operator()(int i, int j){
            if ((i < 0) || (i >= row())||(j < 0)||(j >=col())) {
                cerr << "(" << i << "," << j << ") Out of Range" << endl;
                exit(1);
            }
            return data[i][j];
        }
        matrix<T> operator + (matrix<T> &c);
        matrix<T> operator - (matrix<T> &c);
        matrix<T> operator * (matrix<T> &c);
        matrix<T> operator / (matrix<T> &c);
        matrix<T> operator += (matrix<T> &c);
        matrix<T> operator -= (matrix<T> &c);
        matrix<T> operator *= (matrix<T> &c);
        matrix<T> operator /= (matrix<T> &c);

        matrix<T> operator + (matrix<T> &&c);
        matrix<T> operator - (matrix<T> &&c);
        matrix<T> operator * (matrix<T> &&c);
        matrix<T> operator / (matrix<T> &&c);
        matrix<T> operator += (matrix<T> &&c);
        matrix<T> operator -= (matrix<T> &&c);
        matrix<T> operator *= (matrix<T> &&c);
        matrix<T> operator /= (matrix<T> &&c);
        template<typename U> friend std::ostream &operator<< (std::ostream &out,  matrix<T> &c);

        ~matrix(){
            cout << "Free done" << endl;
        }
};

template <typename T>
matrix<T> matrix<T>:: operator = (matrix<T> &c){
    if (this == &c) {
        return *this;
    }
    if( row() != c.row()){
        int nrow = row();data.resize(c.row());
        if ((row() < c.row()) && (col() != c.col())) {
            for (int i = 0; i < c.row(); i++) {
                data[i].resize(c.col());
            }
        }
        if ((row() < c.row()) && (col() == c.col())) {
            for (int i = row(); i < c.row(); i++) {
                data[i].resize(c.col());
            }
        }
    }
    for (int i = 0; i < c.row(); i++) {
        for (int j = 0; j < c.col(); j++) {
            data[i][j] = c.data[i][j];
        }
    }
    cout << "Copy Assignment" << endl;
    return *this;
}

template <typename T>
void matrix<T>:: resize(int nrow, int ncol){
    if ((row() == nrow) && (col() == ncol)) {
        return;
    }
    if ((row() == nrow) && (col() != ncol)) {
        for (int i = 0; i < nrow; i++) {
            data[i].resize(ncol);
        }
    }
    if ((row() != nrow) && (col() == ncol)) {
        data.resize(nrow);
        if (row() < nrow) {
            for (int i = row(); i < nrow; i++) {
                data[i].resize(ncol);
            }
        }
    }
    if ((row() != nrow) && (col() != ncol)) {
        data.resize(nrow);
        for (int i = 0; i < nrow; i++) {
            data[i].resize(ncol);
        }
    }
}

template <typename T>
void matrix<T>:: display(){
    cout << "Row = " << row() << "\tCol = " << col() << endl;
    cout.precision(10);
    for (int i = 0; i < row(); i++) {
        for (int j = 0; j < col(); j++) {
            cout << setw(8) << data[i][j] << " ";
        }
        cout << endl;
    }
}

template <typename T>
matrix<T> matrix<T>:: operator + (matrix<T> &c){
    if ((row() != c.row()) || (col() != c.col())) {
        cerr << "Dimensions of matrices besides + don't match! Exit!!!" << endl;
        exit(-1);
    }
    matrix<T> temp(c.row(),c.col());
    for (int i = 0; i < c.row(); i++) {
        for (int j = 0; j < c.col(); j++) {
            temp(i,j) = data[i][j] + c(i,j);
        }
    }
    cout << "Copy Add" << endl;
    return temp;
}

template <typename T>
matrix<T> matrix<T>:: operator + (matrix<T> &&c){
    if ((row() != c.row()) || (col() != c.col())) {
        cerr << "Dimensions of matrices besides + don't match! Exit!!!" << endl;
        exit(-1);
    }
//  matrix<T> temp(c.row(),c.col());
    for (int i = 0; i < c.row(); i++) {
        for (int j = 0; j < c.col(); j++) {
            c(i,j) += data[i][j];
        }
    }
    cout << "Move Add" << endl;
    return c;
}

template <typename T>
matrix<T> matrix<T>:: operator - (matrix<T> &c){
    if ((row() != c.row()) || (col() != c.col())) {
        cerr << "Dimensions of matrices besides + don't match! Exit!!!" << endl;
        exit(-1);
    }
    matrix<T> temp(row(),col());
    for (int i = 0; i < row(); i++) {
        for (int j = 0; j < col(); j++) {
            temp(i,j) = data[i][j] - c(i,j);
        }
    }
    return temp;
}

template <typename T>
matrix<T> matrix<T>:: operator - (matrix<T> &&c){
    if ((row() != c.row()) || (col() != c.col())) {
        cerr << "Dimensions of matrices besides + don't match! Exit!!!" << endl;
        exit(-1);
    }
    for (int i = 0; i < row(); i++) {
        for (int j = 0; j < col(); j++) {
            c(i,j) = data[i][j] - c(i,j);
        }
    }
    return c;
}

template <typename T>
std::ostream &operator << (std::ostream &out,  matrix<T> &c){
    if(c.is_empty()){
        return out;
    }
    for (int i = 0; i < c.row(); i++) {
        for (int j = 0; j < c.col(); j++) {
            out << setw(8) << c(i,j) << " ";
        }
        out << endl;
    }
    return out;
}

#endif /* __MATRIX_H_ */

但在调用时,

#include <iostream>
#include "matrix.h"

using namespace std;

int main(int argc, const char *argv[]) {

    matrix<double> my(3,3);
    my(2,2) = 2.12;

    cout << my;
    cout << endl << endl;

    matrix<double> aaa(my);
    cout << endl << endl;
    matrix<double> bbb(my);
    cout << endl << endl;
    bbb = aaa + my + bbb;
    cout << endl << endl;

    matrix<double> ccc = aaa + my + bbb;
    cout << endl << endl << endl;
    matrix<double> ddd=aaa;
    ddd = bbb;
    cout << endl << endl;
    matrix<double> eee(aaa + bbb);
    cout << endl << endl;
    return 0;
}

结果却是这样:

Default Constructor
       0        0        0 
       0        0        0 
       0        0     2.12 


Copy Constructor


Copy Constructor


Default Constructor
Copy Add
Default Constructor
Copy Add
Move Assignment
Copy Constructor
Free done
Free done
Free done


Default Constructor
Copy Add
Default Constructor
Copy Add
Free done



Copy Constructor
Copy Assignment
Copy Constructor
Free done


Default Constructor
Copy Add


Free done
Free done
Free done
Free done
Free done
Free done

移动构造函数并没有执行,

    matrix<double> eee(aaa + bbb);

这里有两次复制构造,一次复制赋值, 本应该是移动构造的,太奇怪了。还有很多问题,大都与这有关。这是怎么回事呢?

阅读 8.4k
1 个回答
✓ 已被采纳新手上路,请多包涵

我跑了一下(加了两行cout),matrix<double> eee(aaa + bbb);对应的输出是,

Default Constructor
Copy Add

不是

Copy Constructor
Copy Assignment
Copy Constructor
Free done

所以,没有“两次复制构造,一次复制赋值”,而是“一次默认构造,一次矩阵加法”,也即以下代码:

template <typename T>
matrix<T> matrix<T>:: operator + (matrix<T> &c){
    // ...
    matrix<T> temp(c.row(),c.col());
    // ...
    cout << "Copy Add" << endl;
    return temp;
}

所以问题是,为什么矩阵加法返回了临时变量(右值引用),但eee却没有调用移动构造函数?
我认为,这里是编译器进行了更古老的一个处理——返回值优化,把矩阵加法的返回值直接构造在了eee上,省去了temp
“典型地,当一个函数返回一个对象实例,一个临时对象将被创建并通过复制构造函数把目标对象复制给这个临时对象。C++标准允许省略这些复制构造函数,即使这导致程序的不同行为,即使编译器把两个对象视作同一个具有副作用。”——引自前述返回值优化维基百科
至于为什么返回值优化会优先于移动构造函数执行,我不知道原因。我能提供的信息是,返回值优化在C++98(1998年)里已经有了,移动构造(右值引用)在C++11(2011年)里才有。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
logo
101 新手上路
子站问答
访问
宣传栏