移动构造函数 - C++ 模板类的运算符重载
ringa_lee
ringa_lee 2017-04-17 11:30:48
0
1
698

自己写了一个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_lee
ringa_lee

ringa_lee

reply all(1)
阿神

I ran it (added two lines of cout), the corresponding output of matrix<double> eee(aaa + bbb); is,

Default Constructor
Copy Add

No

Copy Constructor
Copy Assignment
Copy Constructor
Free done

So, there is no "two copy constructions, one copy assignment", but "one default construction, one matrix addition", that is, the following code:

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

So the question is, why does matrix addition return a temporary variable (rvalue reference), but eee doesn't call the move constructor?
I think this is an older process performed by the compiler - return value optimization, which constructs the return value of matrix addition directly on eee, omitting temp.
"Typically, when a function returns an object instance, a temporary object is created and the target object is copied to the temporary object via a copy constructor. The C++ standard allows these copy constructors to be omitted, even if this results in different behavior of the program. It has side effects that the compiler treats two objects as the same.”——Quoted from the aforementioned return value optimization Wikipedia
. As for why return value optimization takes precedence over move constructors, I don't know the reason. The information I can provide is that return value optimization is already available in C++98 (1998), and move construction (rvalue reference) is only available in C++11 (2011).

Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template