Оптимизация выражений C ++ Long Arithmeti c для пользовательских объектов - PullRequest
0 голосов
/ 11 марта 2020

Я пытаюсь перегрузить операторов. Я заметил, что при выполнении длинных выражений компилятор делает временные и уничтожает их в конце оператора (после ;). Однако, когда выражение становится длиннее или объект больше, память занята без причины (не то, что я знаю). Я хочу уничтожить их после использования следующим образом:

    Test m3 = m1 - m2*2 + 3*m1;
    /* Temporaries predicted from result, temporaries might 
    have different declarations like 'const Test temp = ...;'
    Test temp_m1 = m1*3;
    Test temp_m2 = m2*2;
    Test temp_m3 = m1 - temp_m2;
    Test m3 = temp_m3 + temp_m1;
    temp_m3.~Test();  //destroyed at the end of statement
    temp_m2.~Test();  //destroyed at the end of statement
    temp_m1.~Test();  //destroyed at the end of statement
           ||
           \/
    Test temp_m1 = m1*3;
    Test temp_m2 = m2*2;
    Test temp_m3 = m1 - temp_m2;
    temp_m2.~Test();  //destroyed after temp_m2 is used
    Test m3 = temp_m3 + temp_m1;
    temp_m3.~Test();  //destroyed after temp_m3 is used
    temp_m1.~Test();  //destroyed after temp_m1 is used
    */

Я попытался ввести rvalue в функции перегрузки операторов, но он не работает, так как временные значения являются lvalue. Я заметил, что временные фильтры создаются с помощью конструктора перемещения и могут этим воспользоваться, но это не идеально.

Вопрос в следующем:

  1. Есть ли способ отличить временные от обычных переменных?
  2. Если нет, есть ли другой способ уничтожить их после использования?
  3. Есть ли цель уничтожить их в конце оператора?

Main:

#include <iostream>
#include <iomanip>
class Test {
public:
    Test(const Test& m);
    Test(unsigned int limit = 0, int value = 0);
    Test(Test&& m);
    ~Test();

    Test& operator=(const Test& m);
    Test& operator=(Test&& m);
    Test operator+(const Test& m) const;
    Test operator-(const Test& m) const;
    Test operator*(const int& scalar) const;
    friend std::ostream& operator<<(std::ostream& os, const Test& m);
    void reset();
    unsigned int limit_ = 0;
    std::unique_ptr<int[]> data_;
};
int main(){
    Test m2(2,2);
    Test m1(2,1);
    Test m3 = m1 - m2*2 + 3*m1;
    /* Temporaries predicted from result, temporaries might 
    have different declarations like 'const Test temp = ...;'
    Test temp_m1 = m1*3;
    Test temp_m2 = m2*2;
    Test temp_m3 = m1 - temp_m2;
    Test m3 = temp_m3 + temp_m1;
    temp_m3.~Test();
    temp_m2.~Test();
    temp_m1.~Test();
    */
    std::cout << "RESULT" << std::endl;
    std::cout << m1;
    std::cout << m2;
    std::cout << m3;
}

Результат:

Constructor: [ 2     2    ]
Constructor: [ 1     1    ]

Multiplication operation:
Constructor: [ 0     0    ]
[ 1     1    ]
 * 3 = [ 3     3    ]
Move constructor: [ 3     3    ]   //temp_m1
Destructor: Empty


Multiplication operation:
Constructor: [ 0     0    ]
[ 2     2    ]
 * 2 = [ 4     4    ]
Move constructor: [ 4     4    ]   //temp_m2
Destructor: Empty


Subtraction operation:
Constructor: [ 0     0    ]
[ 1     1    ]
 - [ 4     4    ]
 = [ -3    -3   ]
Move constructor: [ -3    -3   ]   //temp_m3
Destructor: Empty


Addition operation:
Constructor: [ 0     0    ]
[ -3    -3   ]
 + [ 3     3    ]
 = [ 0     0    ]
Move constructor: [ 0     0    ]   //m3
Destructor: Empty

Destructor: [ -3    -3   ]   //temp_m3 destroyed at the end of statement

Destructor: [ 4     4    ]   //temp_m2 destroyed at the end of statement

Destructor: [ 3     3    ]   //temp_m1 destroyed at the end of statement

RESULT
[ 1     1    ]
[ 2     2    ]
[ 0     0    ]
Destructor: [ 0     0    ]

Destructor: [ 1     1    ]

Destructor: [ 2     2    ]


Process returned 0 (0x0)   execution time : 0.125 s

Код реализации:

Test::~Test(){
        std::cout << "Destructor: " << *this << std::endl;
        data_.reset();
}
Test::Test(Test&& m){  //Move constructor
    std::cout << "Move constructor: ";
    data_ = std::move(m.data_);
    limit_ = m.limit_;
    m.reset();
    std::cout << *this;
}
Test::Test(const Test& m){  //Copy constructor
    std::cout << "Copy constructor: ";
    data_ = std::make_unique<int[]>(m.limit_);
    limit_ = m.limit_;
    for(unsigned int i=0; i<limit_ ; ++i) data_[i] = m.data_[i];
    std::cout << *this;
}
Test::Test(unsigned int limit, int value){    //Constructor
    std::cout << "Constructor: ";
    data_ = std::make_unique<int[]>(limit);
    limit_ = limit;
    if(limit == 0) return;
    for(unsigned int i=0; i<limit ; ++i) data_[i] = value;
    std::cout << *this;
}
Test& Test::operator=(const Test& m){ //Copy assignment
    std::cout << "Copy assignment: ";
    Test local_m = m;

    limit_ = local_m.limit_;
    data_ = std::move(local_m.data_);
    std::cout << *this << std::endl;
    return *this;
}
Test& Test::operator=(Test&& m){  //Move assignment
    std::cout << "Move assignment: ";
    Test local_m = std::move(m);

    limit_ = local_m.limit_;
    data_ = std::move(local_m.data_);
    std::cout << *this << std::endl;
    return *this;
}
Test Test::operator+(const Test& m) const{ //Addition
    std::cout << std::endl<< "Addition operation: " <<std::endl;
    if( limit_ != m.limit_){
        std::cout << "Addition error: not same dimensions, return Empty" <<std::endl;
        return Test();
    }
    if( limit_ == 0){
        std::cout << "Addition warning: Empty, return Empty" <<std::endl;
        return Test();
    }
    Test result(limit_);
    for ( unsigned int i = 0; i < limit_; ++i ) result.data_[i] = data_[i] + m.data_[i];
    std::cout << *this << " + " << m << " = " << result;
    return result;
}
Test Test::operator-(const Test& m) const{ //Subtraction
    std::cout << std::endl<< "Subtraction operation: " <<std::endl;
    if( limit_ != m.limit_){
        std::cout << "Subtraction error: not same dimensions, return Empty" <<std::endl;
        return Test();
    }
    if( limit_ == 0){
        std::cout << "Subtraction warning: Empty, return Empty" <<std::endl;
        return Test();
    }
    Test result(limit_);
    for ( unsigned int i = 0; i < limit_; ++i ) result.data_[i] = data_[i] - m.data_[i];
    std::cout << *this << " - " << m << " = " << result;
    return result;
}
Test Test::operator*(const int& scalar) const{ //Multiplication
    std::cout << std::endl<< "Multiplication operation: " <<std::endl;
    if( limit_ == 0){
        std::cout << "Multiplication warning: Empty, return Empty" <<std::endl;
        return Test();
    }
    Test result(limit_);
    for ( unsigned int i = 0; i < limit_; ++i ) result.data_[i] = data_[i] * scalar;
    std::cout << *this << " * " << scalar << " = " << result;
    return result;
}
Test operator*(const int& scalar, const Test& m){ //Multiplication
    return m * scalar;
}
std::ostream& operator<<(std::ostream& os, const Test& m){  //Show
    unsigned int limit = m.limit_;
    if (limit == 0) return os << "Empty" << std::endl;
    os << "[";
    for ( unsigned int i = 0; i < limit; ++i ){
            os << " " << std::left << std::setw(5) << m.data_[i];
    }
    os << "]" << std::endl;
    return os ;
}
void Test::reset(){ //Reset
        limit_ = 0;
        data_.reset();
}

1 Ответ

0 голосов
/ 11 марта 2020

Есть ли способ отличить временные переменные от обычных переменных?

Это то, для чего нужны ссылки на rvalue - rvalue ref arg (с &&) может связываться только с без имени temp, в то время как lvalue ref arg (с &) может связываться только с именованным (не временным) значением. Таким образом, вы можете перегружать своих операторов версиями, которые принимают либо rvalue, либо lvalue refs, чтобы избежать ненужных копий.

Теперь, пока вы можете иметь несколько перегруженных операторов, в специальном корпусе находятся все различные возможные комбинации, то есть как правило, не нужно. Если вы следуете некоторым базовым c рекомендациям , это на самом деле не так уж плохо. Обычно вы хотите перегрузить бинарные операторы как свободные функции (не методы), которые вызывают операторы присваивания. Если вы определили:

Test &Test::operator +=(const Test &a) {
    // add 'a' into this
    return *this; }

Test operator+(Test a, const Test &b) { return a += b; }  // free function, not a method

и у вас есть конструктор перемещения, то обычно необходимое количество копий будет минимальным.

...