繁体   English   中英

从具有'void'返回类型的函数中获取结果,而结果变量是输入参数之一-C ++

[英]Getting results from functions with 'void' return type, while resulting variable is one of the input arguments - C++

我得到了这个数学例程库(没有文档),可以在大学里完成某些任务。 我所遇到的问题是,尽管这些函数相互调用或是另一个函数的一部分,但所有函数都具有void返回类型,因此需要它们的计算结果。

这是从库中提取的一段(简化的)代码。 不要理会代码中的数学,这并不重要。 仅仅传递参数并返回结果令我感到困惑(如代码后所述):

// first function

void vector_math // get the (output) vector we need
 ( 
   double inputV[3], // input vector
   double outputV[3] // output vector
 )
 {
      // some variable declarations and simple arithmetics
      // .....
      //

      transposeM(matrix1, matrix2, 3, 3 ); // matrix2 is the result

      matrixXvector( matrix2, inputV, outputV) // here you get the result, outputV
 }

////////

// second function

 void transposeM // transposes a matrix 
    ( 
      std::vector< std::vector<double> > mat1, // input matrix
      std::vector< std::vector<double> > &mat2, // transposed matrix
      int mat1rows, int mat1columns 
    )
   {         
   int row,col;

    mat2.resize(mat1columns);  // rows
     for (std::vector< std::vector<double> >::iterator it=mat2.begin(); it !=mat2.end();++it)
          it->resize(mat1rows);

 for (row = 0; row < mat1rows; row++)
     {
     for (col = 0; col < mat1columns; col++)
         mat2[col][row] = mat1[row][col];
     }
   }

   ////////

   // third function

   void matrixXvector // multiply matrix and vector
    (
          std::vector< std::vector<double> > inMatrix,
          double inVect[3],
          double outVect[3]
    )
      {
     int row,col,ktr;

     for (row = 0; row <= 2; row++)
      {
     outVect[row]= 0.0;
     for (ktr = 0; ktr <= 2; ktr++)
         outVect[row]= outVect[row] + inMatrix[row][ktr] * inVect[ktr];
       }
   }

因此,主程序正在调用“ vector_math”。 它以inputV作为输入,结果应为outputV。 但是,outputV是输入参数之一,并且该函数返回void。 稍后在调用“ transposeM”和“ matrixXvector”时也会发生类似的过程。

为什么输出变量是输入参数之一? 结果如何返回并用于进一步的计算? 这种传递和返回参数如何工作?

因为我是一个初学者,并且也从未见过这种编码风格,所以我不理解传递参数(尤其是给出输出)在这些函数中的工作方式。 因此,我不知道如何使用它们以及对它们有什么期望(它们将实际做什么)。 因此,我非常感谢您做出这样的解释,使我对这些过程更加清楚。

额外:

谢谢大家的好评。 这是我第一次几乎无法决定接受哪个答案,即使我这样做对其他人也不公平。 但是,如果有人愿意回答(因为评论就足够了),我想添加一个额外的问题。 这种编码输入/输出自变量的“旧式”样式是否具有其名称或引用它的任何其他表达式?

这是一种返回某些或多个值的“旧”(但仍然很流行)样式。 它是这样的:

void copy (const std::vector<double>& input, std::vector<double>& output) {
    output = input;
}

int main () {
    std::vector<double> old_vector {1,2,3,4,5}, new_vector;
    copy (old_vector, new_vector); // new_vector now copy of old_vector
}

因此,基本上,您可以给函数一个或多个输出参数以写入其计算结果。

如果通过值或const引用传递输入参数(即,您不打算更改它们),尽管按值传递只读参数可能在性能上代价高昂,但这并不重要。 在第一种情况下,您将复制输入对象并在函数中使用副本,在后一种情况中,只需让函数看到原始对象并防止使用const对其进行修改。 输入参数的const是可选的,但将其保留为const可以使函数更改其值(可能不是您想要的值),并禁止将临时变量作为输入传递。

输入参数必须由非常量引用传递,以允许函数对其进行更改。

另一个甚至更老的“ C-isher”风格是传递输出指针或原始数组,就像您的第一个函数一样。 这有潜在的危险,因为指针可能未指向有效的内存,但仍散布得很广。 它的工作原理基本上与第一个示例相同:

// Copies in to int pointed to by out
void copy (int in, int* out) {
    *out = in;
}

// Copies int pointed to by in to int pointed to by out
void copy (const int* in, int* out) {
    *out = *in;
}

// Copies length ints beginning from in to length ints beginning at out
void copy (const int* in, int* out, std::size_t length) {
    // For loop for beginner, use std::copy IRL:
    // std::copy(in, in + length, out);
    for (std::size_t i = 0; i < length; ++i)
        out[i] = in[i];
}

第一个示例中的数组基本上像指针一样工作。

鲍姆的答案是准确的,但可能不像C / C ++初学者所希望的那样详尽。

进入函数的实际参数值始终按值(即位模式)传递,并且不能以调用者可读的方式进行更改。 但是-这是关键-参数中的那些位实际上可能是指针(或引用),它们不直接包含数据,而是在内存中包含包含实际值的位置。

示例:在这样的函数中:

void foo(double x, double output) { output = x ^ 2; }

将输出变量命名为“输出不会改变任何内容-调用者无法获得结果。

但是像这样:

void foo(double x, double& output) { output = x ^ 2; }

“&”表示输出参数是对应该存储输出的存储位置的引用。 它是C ++中的语法糖,等效于此“ C”代码:

void foo(double x, double* pointer_to_output) { *pointer_to_output = x ^ 2; }

指针取消引用被引用语法隐藏,但是思想是相同的。

数组执行类似的语法技巧,它们实际上作为指针传递,因此

void foo(double x[3], double output[3]) { ... }

void foo(double* x, double* output) { ... }

基本相同。 请注意,无论哪种情况,都无法确定数组的大小。 因此,通常认为传递指针和长度是一种好的做法:

void foo(double* x, int xlen, double* output, int olen);

这样的输出参数在多种情况下使用。 一种常见的方法是返回多个值,因为函数的返回类型只能是单个值。 (虽然您可以返回包含多个成员的对象,但是不能直接返回多个单独的值。)使用输出参数的另一个原因是速度。 如果所讨论的对象较大且/或构造成本很高,则通常可以更快地在适当位置修改输出。

另一个编程范例是返回一个指示函数成功/失败的值,并在输出参数中返回计算值。 例如,许多历史悠久的Windows API都以这种方式工作。

数组是低级C ++构造。 它可以隐式转换为指向为数组分配的内存的指针。

int a[] = {1, 2, 3, 4, 5};
int *p = a; // a can be converted to a pointer
assert(a[0] == *a);
assert(a[1] == *(a + 1));
assert(a[1] == p[1]);
// etc.

关于数组的令人困惑的事情是函数声明void foo(int bar[]); 等同于void foo(int *bar); 所以foo(a)不会复制数组a 而是将a转换为指针,然后复制该指针(而不是内存)。

void foo(int bar[]) // could be rewritten as foo(int *bar)
{
    bar[0] = 1; // could be rewritten as *(bar + 0) = 1;
}

int main()
{
    int a[] = {0};
    foo(a);
    assert(a[0] == 1);
}

bar指向相同存储器a确实如此修改数组的内容指向bar相同修改数组的内容a

在C ++中,您还可以按引用传递对象( Type &ref; )。 您可以将引用视为给定对象的别名 因此,如果您写:

int a = 0;
int &b = a;
b = 1;
assert(a == 1);

b实际上是一个别名a -通过修改b修改a ,反之亦然。 函数也可以通过引用来接受参数:

void foo(int &bar)
{
    bar = 1;
}

int main()
{
    int a = 0;
    foo(a);
    assert(a == 1);
}

同样, bar仅仅是a的别名,因此通过修改bar您还将修改a


您拥有的数学例程库正在使用这些功能将结果存储在输入变量中。 这样做是为了避免复制并简化内存管理。 如@Baum mit Augen所述,该方法还可以用作返回多个值的一种方法。

考虑以下代码:

vector<int> foo(const vector<int> &bar)
{
    vector<int> result;
    // calculate the result
    return result;
}

在返回resultfoo将复制该向量,并且根据所存储元素的数量(和大小),该副本可能非常昂贵。

注意:大多数编译器会的Elid复制上述使用命名返回值优化(NRVO)中的代码。 但是,在一般情况下,您无法保证会发生这种情况。

避免昂贵拷贝的另一种方法是在heap上创建结果对象,并返回指向已分配内存的指针:

vector<int> *foo(const vector<int> &bar)
{
    vector<int> *result = new vector<int>;
    // calculate the result
    return result;
}

调用方需要管理返回对象的生存期,在不再需要它时调用delete 否则可能会导致内存泄漏 (内存保持分配状态,但实际上无法被应用程序使用)。

注意:有多种解决方案可帮助返回(昂贵的复制)对象。 C ++ 03具有std::auto_ptr包装器,可帮助您对在堆上创建的对象进行生命周期管理。 C ++ 11在语言中添加了移动语义 ,从而允许通过值而不是使用指针来有效地返回对象。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM