搜尋

首頁  >  問答  >  主體

移动构造函数 - 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);

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

ringa_leeringa_lee2807 天前700

全部回覆(1)我來回復

  • 阿神

    阿神2017-04-17 11:32:48

    我跑了一下(加了兩行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年)裡才有。

    回覆
    0
  • 取消回覆