试图减少内存分配,重载运算符的技巧是什么?

时间:2014-01-15 18:31:18

标签: c++ memory-management operator-overloading

我已经用重载运算符写出了我的类,但我正在尝试减少内存分配量(如Linux上的valgrind所示)。我理解在某些情况下,调用复制构造函数来为函数创建对象的本地副本,但我不确定哪些情况。就目前而言,我正在每个案例中创建一个新对象,所以我觉得如果我要使用已经复制的部分,我可以摆脱一些“新”调用。下面是我的operator +和operator + =供参考。

// ---------------------------------------------------------------------------
// operator+
// Adds two Poly objects
Poly Poly::operator+(const Poly& rhs) const {
    //case where rhs has more terms
    if (maxExponent < rhs.maxExponent) {
        Poly temp(rhs);

        for (int i = 0; i <= maxExponent; i++) {
            temp.polynomial[i] += polynomial[i];
        }

        return temp;
    }
    else {
        Poly temp(*this);

        for (int i = 0; i <= rhs.maxExponent; i++) {
            temp.polynomial[i] += rhs.polynomial[i];
        }

        return temp;
    }
}

// ---------------------------------------------------------------------------
// operator+=
// Adds and assigns two Poly objects
Poly& Poly::operator+=(const Poly& rhs) {
    *this = *this + rhs;
    return *this;
}

这是我的算子=如果技巧依赖于此:

// ---------------------------------------------------------------------------
// operator=
// Assigns a Poly object to another
const Poly& Poly::operator=(const Poly& other) {
    if (&other != this) {
        delete[] polynomial;
        maxExponent = other.maxExponent;
        polynomial = new int[maxExponent + 1];

        for (int i = 0; i <= maxExponent; i++) {
            polynomial[i] = other.polynomial[i];
        }
    }

    return *this;
}

3 个答案:

答案 0 :(得分:2)

您正在寻找的技术称为“表达模板”。

您的operator+需要两个Poly&个对象,并返回should_be_added< Poly&, Poly& >。如果再次添加,它会返回should_be_added< should_be_added<Poly&, Poly&>, Poly& >(或者可能should_be_added< Poly&, Poly&, Poly& >,如果您知道通勤情况并且您希望事情变得平坦,但这是额外的工作。)

should_be_added然后转换为 - Poly,或Poly具有隐式should_be_added< T, U >&&构造函数(效率move这两个是等价的)。此时,您将在编译时获得要分配给Poly的完整表达式树。通过大量的工作和关注,您可以有效地构建单个输出值。

一个好的开始方式是从您的operator+=(Poly const& o)operator+=(Poly&& o)以及类似的“变异”运算符开始。这些原语可以使编写其他操作符更加容易。

您可能希望编写自定义Poly& operator=( should_be_added<T,U>&& src ),以便它重用现有Poly对象中的任何内存。一种简单的方法是在should_be_added中使用Poly result( Poly&& src )的方法,并将operator Poly()实现为operator Poly() const { return result( Poly{} ); }operator=为{{1} }}

现在,这一切都不容易 - 表达式{ swap( *this, src.result(std::move(*this)) ); return *this }是中等深度template - fu。但结果可能是你可以以自然的方式表达你的数学表达式,并且几乎没有任何东西。

请注意,template类的高效move语义应该很容易 - 只需Poly内部缓冲区并清除源代码。

答案 1 :(得分:0)

在第一种情况下,使用operator+,从概念上讲,你无能为力。你需要临时变量,你必须按值返回它。

在第二种情况下,您使用operator+=实施operator+,然后制作副本,然后使用operator=将其复制到对象内。这是非常低效的。

出于上述原因,人们通常更愿意先实施operator+=,然后将operator+实施为:

Poly Poly::operator+(const Poly& rhs) {
    return Poly(*this) += rhs;
}

这与你在这里所做的相反。

答案 2 :(得分:0)

我的解决方案是用以下想法重新实现Poly类:让我们无法修改字段'多项式'并使用shared_ptr在同一Poly的副本之间共享它。通过这种方式,我们可以使用O(1)复制运算符,而运算符+仍然是O(n) - 在乐观情况下可能有O(1):)

让我们也使用std :: vector而不是table,并注意我们的公共接口。作为回报,我们得到:

  • 较小的内存消耗
  • 没有代码重复
  • 无需实现复制构造函数;默认可以正常工作:)

原谅我对运营商&lt;&lt;的草率实施。我将操作符+的乐观情况的实现留给了读者:)。

使用c ++ 11实现,因为我不是受虐狂。

#include <memory>
#include <vector>
#include <initializer_list>
#include <iostream>

class Poly {

public:
    Poly() : polynomial_(NULL) {}

    Poly(std::initializer_list<double> il)
    {
        polynomial_.reset(new std::vector<double>(il.begin(), il.end()));
    }

    unsigned max_exp() const
    {
        return polynomial_ ? polynomial_->size() : 0;
    }

    Poly operator+(const Poly& o) const
    {
        const bool has_bigger_exp = max_exp() > o.max_exp();
        const Poly & poly_big     = has_bigger_exp ? *this : o;
        const Poly & poly_small   = has_bigger_exp ? o : *this;

        auto * tmp = new std::vector<double>(*poly_big.polynomial_);
        for (unsigned i = 0; i < poly_small.max_exp(); ++i) {
            tmp->at(i) += poly_small.polynomial_->at(i);
        }

        Poly ret_obj;
        ret_obj.polynomial_.reset(tmp);
        return ret_obj;
    }

    Poly& operator+=(const Poly& o)
    {
          *this = *this + o;
          return *this;
    }

private:

    std::shared_ptr<const std::vector<double>> polynomial_;

    friend std::ostream& operator<<(std::ostream& os, const Poly& obj);
};

std::ostream& operator<<(std::ostream& os, const Poly& obj)
{
    if (obj.max_exp() == 0) {
        os << "0" << std::endl;
        return os;
    }

    for (unsigned i = obj.max_exp()-1; i > 0; --i) {
        double param = obj.polynomial_->at(i);
        if (param != 0) {
            os << param << " * x^" << i << " + ";
        }
    }

    os << obj.polynomial_->at(0) << std::endl;
    return os;
}

int main() {

    Poly a = {1, 2, 3};
    Poly b = {4, 5};
    Poly c = a + b;
    Poly d;

    std::cout << a << b << c << d;
    a += {1, 1};
    std::cout << a;

    return 0;
}