std::bad_alloc,运算符重载与临时变量

报错代码

写一个复数类 Complex ,支持加号重载:

#include<iostream>
using namespace std;
class Complex{
private:
    double r,i;
public:
    Complex(double _r,double _i=0):r(_r),i(_i){}
    double re(){return r;}
    double im(){return i;}
    void print(){
        cout << r << "+" << i << "i";
    }
    friend Complex& operator+(Complex& a,Complex& b){
        return *(new Complex(a.re()+b.re(),a.im()+b.im()));
    }
};

int main(){
    Complex a(0,0) , b(1,0.5);
    for (int i=0;i<100000000;++i){
        a = a + b + b;
    } 
    a.print();
    return 0;
}


报错

terminate called after throwing an instance of ‘St9bad_alloc’
what(): std::bad_alloc


原因

bad_alloc是指程序无法申请到足够的内存空间。
然而,第一眼看上去,在main函数中似乎并没有申请过多的空间啊?


实际上 ,a = a + b + b 该式中,程序先计算 a + b 并为其分配一定的空间(临时变量),再让该变量再加b,把返回引用赋给 a。
其中, a + b 这个临时变量并没有得到自动的析构,因此在内存中堆积。最后剩余内存不足。


解决方案

重载运算符改为传递值,而不是引用。


虽然传递引用能提高效率,但是在内存上这样的临时变量不会得到及时的析构,这也是C++的一个天然的缺陷(但是提高了效率)。
可是这个问题可能是致命的。举例来讲,如果想要写矩阵库并将其运用到机器学习中,假若不注意内存的及时释放,则会很容易报错。因为超大的数据量以及循环次数极其容易导致 bad_alloc!


在《Microsoft C++ 程序设计指南》一书中(第十一章末),作者Kaare Christian建议限制重载运算符。即,只提供类似于 += , -= , *= , /= 的运算符重载,返回它们的引用(*this)并不会产生额外内存开销。
(除此之外,该书还提到可以通过识别并处理临时变量的建立解决,并指出这一技巧较为困难但值得去做。)


以下提供重载加号为返回值,而非引用的解决方案的代码。注意要同时将一些函数参数和成员函数用 const 修饰!

#include<iostream>
using namespace std;
class Complex{
private:
    double r,i;
public:
    Complex(double _r,double _i=0):r(_r),i(_i){}
    double re() const{return r;}
    double im() const{return i;}
    void print(){
        cout << r << "+" << i << "i";
    }
    friend Complex operator+(const Complex& a,const Complex& b){
        return Complex(a.re()+b.re(),a.im()+b.im());
    }
};

int main(){
    Complex a(0,0) , b(1,0.5);
    for (int i=0;i<100000000;++i){
        a = a + b + b;
    } 
    a.print();
    return 0;
}

运行后,成功输出
2e+008+1e+008i

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值