C++ 用户定义对象的长算术表达式优化
C++ Long arithmetic expression optimization for user-defined object
我正在尝试重载运算符。我注意到在执行长表达式时,编译器会生成临时变量并在语句末尾(;
之后)销毁它们。但是,随着表达式变长或对象变大,内存会无缘无故地被占用(据我所知)。我想要的是像这样使用后销毁它们:
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
*/
我试过将右值输入运算符重载函数,但它不起作用,因为临时对象是左值。我注意到临时对象是通过移动构造函数创建的,可以利用它,但这并不理想。
问题是:
- 有什么方法可以区分临时的和正常的吗
变量?
- 如果没有,有没有其他方法在使用后销毁它们?
- 在语句末尾销毁它们有什么目的吗?
主要:
#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();
}
Is there any way to differentiate temporaries from normal variables?
这就是右值引用的用途——右值引用参数(&&
)只能绑定到未命名的临时变量,而左值引用参数(&
)只能绑定一个命名的(非临时)值。因此,您可以使用采用右值或左值引用的版本来重载您的运算符,以避免不必要的复制。
现在虽然您 可以 有多个重载运算符特殊外壳所有不同的可能组合,但通常没有必要。如果你关注some basic guidelines,其实还不错。通常,您希望将二元运算符重载为调用赋值运算符的自由函数(而不是方法)。如果你定义:
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
并且有一个move构造函数,那么一般需要的拷贝数会最小化。
我正在尝试重载运算符。我注意到在执行长表达式时,编译器会生成临时变量并在语句末尾(;
之后)销毁它们。但是,随着表达式变长或对象变大,内存会无缘无故地被占用(据我所知)。我想要的是像这样使用后销毁它们:
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
*/
我试过将右值输入运算符重载函数,但它不起作用,因为临时对象是左值。我注意到临时对象是通过移动构造函数创建的,可以利用它,但这并不理想。
问题是:
- 有什么方法可以区分临时的和正常的吗 变量?
- 如果没有,有没有其他方法在使用后销毁它们?
- 在语句末尾销毁它们有什么目的吗?
主要:
#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();
}
Is there any way to differentiate temporaries from normal variables?
这就是右值引用的用途——右值引用参数(&&
)只能绑定到未命名的临时变量,而左值引用参数(&
)只能绑定一个命名的(非临时)值。因此,您可以使用采用右值或左值引用的版本来重载您的运算符,以避免不必要的复制。
现在虽然您 可以 有多个重载运算符特殊外壳所有不同的可能组合,但通常没有必要。如果你关注some basic guidelines,其实还不错。通常,您希望将二元运算符重载为调用赋值运算符的自由函数(而不是方法)。如果你定义:
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
并且有一个move构造函数,那么一般需要的拷贝数会最小化。