2

B假设我有一个已经定义并在 C++ 代码中某处使用的数组。现在,假设我有另一个A已定义和初始化的数组。我想创建一个f转换的函数A(例如,FFT),并且我希望将转换的结果分配给B(当然,在 的转换之后AB将更改其值)。我想通过保持语法来做到这一切

B=f(A);

即,不将 的地址B作为参数传递给f。可能吗:

  1. 没有创造临时工?
  2. 创建临时对象,但没有内存泄漏?

谢谢你。

编辑:以下答案中提供的解决方案摘要

感谢 RiaD、James Kanze、Shahbaz 和 Razispio 的回答。

我要问的是需要AB成为数组类的对象才能获得效率和有效性。此外,在“标准”实现中,例如,对于配备复制构造函数的数组类,类似的语法B=f(A);将需要创建临时对象。然而应该提到,临时变量不一定是限制,因为许多编译器可以省略额外的临时变量。与此相反,类似这样的语法f(A,B);会避免临时性。使用表达式模板B=f(A);的解决方案在内部使用时启用语法f(A,B);,使临时变量的使用可以忽略不计。一种有效的替代解决方案是使用移动赋值运算符,例如

在 C++11 中移动语义和右值引用

有关详细信息,请参阅下面提供的答案。

4

4 回答 4

3

最简单的使用方法std::vectorstd::array

例子:

vector<int> f(const vector<int>& a) {
   return a;
}

b = f(a);

实际上,你不必使用这些类之一来存储,你可以使用你自己的类,它有operator =

YourClass& operator = (const vector<int>&/* there will be returned value of f, it may be std::array, std::vector or your own class.*/ v) {
    //fill it here.
    return *this;
}

您还可以为其提供移动构造函数以避免不必要的复制。

例如

class Array {
    int* data;   
    YourClass& operator = (YourClass&& v) {
        swap(v.data, data);
    }
}

YourClass f(const YourClass& a) {
   //
}

Array a,b;
b = f(a);
于 2013-04-27T17:11:48.613 回答
1

首先,这取决于数组的含义。使用通常的 C++ 含义 ( std::vector),内存泄漏永远不会有任何问题;具有通常的 C 含义 ( T[]),B = f( A )是非法的。如果您定义自己的数组类型,那么它在这方面的行为应该或多或少类似std::vector

关于额外的临时:f应该把它的参数作为一个常量引用,这样就不会有复制来传递参数。至于返回值,有正式的副本,但允许编译器复制它,而且大多数情况下,至少在某些时候是这样。在赋值语句中,数组的实际数据可能复制到赋值本身中。

在 C++11 中,您可以提供移动构造函数和移动赋值运算符,并且(可能)进一步减少复制的机会。

于 2013-04-27T17:36:30.407 回答
1

由于f没有对 的任何引用B,因此它只能在本地数组上工作。因此,您必须将这些值复制到B. 所以在目前的形式下,没有没有办法。但是,如果您进行f内联,优化器可能只会帮助您解决1问题,但这对于 FFT 来说不是一个好主意。

使用临时值但没有内存泄漏,您可以简单地将数组包装在一个类中(或使用vector已经这样做的类)并返回它。请注意,非动态数组副本本身不会产生内存泄漏,只是不可能在 C++ 中编写some_array = another_array.

如果您可以选择重新设计,最好的方法是要求f(A, B)最高性能,


1如果编译器足够聪明,它会识别出一个本地数组 inf将被复制到B,并且在它的内联版本中f它可以使用B自己,因为它可以访问它。

于 2013-04-27T17:14:03.950 回答
1

如果您愿意f(a)b. 这个想法是你已经有了b,因此你想在语义上将它作为参数传递给f. 例如,我们可以为此重载赋值运算符:

首先,定义一个表达式概念(这里表示为一个抽象类,但我们并不想使用虚拟调度)。这封装了一个计算,该计算可以Evaluate并将结果返回到target.

template<typename T>
class Expression
{
public:
    virtual void Evaluate(T& target) const = 0;
}

现在定义你的数组类,它可以从一个Expression类中分配。当一个表达式被赋值时,它被计算。

template<typename T>
class Array
{
   T* ptr;
public:
   template<typename Expression>
   const Array<T> operator =(Expression const & expression)
   {
      expression.Evaluate(ptr);
   }
}

现在,定义您的函数f以返回一个表达式,该表达式在计算时返回f(a)

class MyExpression
{
    float* a;
public:
    MyExpression(float* a) : a(a) {}
    void Evaluate(float* target)
    {
        f(a, b);
    }
}

MyExpression<float> f(float* a) 
{ 
    return MyExpression(a);
}

然后您可以按如下方式调用

Array b;
b = f(a);

这确实会创建一个临时的(在堆栈上),但它可以被内联。例如,假设我们有电话

b = f(a);

这相当于

b.operator = (f (a));

内联调用operator =,我们得到

(f(a)).Evaluate(b);

现在,我们也可以内联f调用

(MyExpression(f)).Evaluate(b);

现在,编译器可以注意到这和做的完全一样

f(a, b);

并立即执行此操作。因此,不需要创建临时的MyExpression,尽管它的成本首先可以忽略不计(它是 的堆栈分配sizeof(Array)

请注意,矩阵库特征使用这种类型的策略来避免例如临时性。

于 2013-04-27T17:23:50.007 回答