简体   繁体   English

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

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

I got this library of mathematical routines ( without documentation ) to work on some task at college. 我得到了这个数学例程库(没有文档),可以在大学里完成某些任务。 The problem I have with it is that all of its functions have void return type, although these functions call one another, or are part of another, and the results of their computations are needed. 我所遇到的问题是,尽管这些函数相互调用或是另一个函数的一部分,但所有函数都具有void返回类型,因此需要它们的计算结果。

This is a piece of ( simplified ) code extracted from the libraries. 这是从库中提取的一段(简化的)代码。 Don't bother about the mathematics in code, it is not significant. 不要理会代码中的数学,这并不重要。 Just passing arguments and returning results is what puzzles me ( as described after code ) : 仅仅传递参数并返回结果令我感到困惑(如代码后所述):

// 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];
       }
   }

So "vector_math" is being called by the main program. 因此,主程序正在调用“ vector_math”。 It takes inputV as input and the result should be outputV. 它以inputV作为输入,结果应为outputV。 However, outputV is one of the input arguments, and the function returns void. 但是,outputV是输入参数之一,并且该函数返回void。 And similar process occurs later when calling "transposeM" and "matrixXvector". 稍后在调用“ transposeM”和“ matrixXvector”时也会发生类似的过程。

Why is the output variable one of the input arguments ? 为什么输出变量是输入参数之一? How are the results being returned and used for further computation ? 结果如何返回并用于进一步的计算? How this kind of passing and returning arguments works ? 这种传递和返回参数如何工作?

Since I am a beginner and also have never seen this style of coding, I don't understand how passing parameters and especially giving output works in these functions. 因为我是一个初学者,并且也从未见过这种编码风格,所以我不理解传递参数(尤其是给出输出)在这些函数中的工作方式。 Therefore I don't know how to use them and what to expect of them ( what they will actually do ). 因此,我不知道如何使用它们以及对它们有什么期望(它们将实际做什么)。 So I would very much appreciate an explanation that will make these processes clear to me. 因此,我非常感谢您做出这样的解释,使我对这些过程更加清楚。

EXTRA : 额外:

Thank you all for great answers. 谢谢大家的好评。 It was first time I could barely decide which answer to accept, and even as I did it felt unfair to others. 这是我第一次几乎无法决定接受哪个答案,即使我这样做对其他人也不公平。 I would like to add an extra question though, if anyone is willing to answer ( as a comment is enough ). 但是,如果有人愿意回答(因为评论就足够了),我想添加一个额外的问题。 Does this "old" style of coding input/output arguments have its name or any other expression with which it is referred ? 这种编码输入/输出自变量的“旧式”样式是否具有其名称或引用它的任何其他表达式?

This is an "old" (but still popular) style of returning certain or multiple values. 这是一种返回某些或多个值的“旧”(但仍然很流行)样式。 It works like this: 它是这样的:

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
}

So basically you give the function one or multiple output parameter to write the result of its computation to. 因此,基本上,您可以给函数一个或多个输出参数以写入其计算结果。

If you pass input parameters (ie you don't intend to change them) by value or by const reference does not matter, although passing read only arguments by value might be costly performance-wise. 如果通过值或const引用传递输入参数(即,您不打算更改它们),尽管按值传递只读参数可能在性能上代价高昂,但这并不重要。 In the first case, you copy the input object and use the copy in the function, in the latter you just let the function see the original and prevent it from being modified with the const . 在第一种情况下,您将复制输入对象并在函数中使用副本,在后一种情况中,只需让函数看到原始对象并防止使用const对其进行修改。 The const for the input parameters is optional, but leaving it out allows the function to change their values which might not be what you want, and inhibits passing temporaries as input. 输入参数的const是可选的,但将其保留为const可以使函数更改其值(可能不是您想要的值),并禁止将临时变量作为输入传递。

The input parameter(s) have to be passed by non-const reference to allow the function to change it/them. 输入参数必须由非常量引用传递,以允许函数对其进行更改。

Another, even older and "C-isher" style is to passing output-pointer or raw-arrays, like the first of your functions does. 另一个甚至更老的“ C-isher”风格是传递输出指针或原始数组,就像您的第一个函数一样。 This is potentially dangerous as the pointer might not point to a valid piece of memory, but still pretty wide spread. 这有潜在的危险,因为指针可能未指向有效的内存,但仍散布得很广。 It works essentially just like the first example: 它的工作原理基本上与第一个示例相同:

// 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];
}

The arrays in your first example basically work like pointers. 第一个示例中的数组基本上像指针一样工作。

Baum's answer is accurate, but perhaps not as detailed as a C/C++ beginner would like. 鲍姆的答案是准确的,但可能不像C / C ++初学者所希望的那样详尽。

The actual argument values that go into a function are always passed by value (ie a bit pattern) and cannot be changed in a way that is readable by the caller. 进入函数的实际参数值始终按值(即位模式)传递,并且不能以调用者可读的方式进行更改。 HOWEVER - and this is the key - those bits in the arguments may in fact be pointers (or references) that don't contain data directly, but rather contain a location in memory that contains the actual value. 但是-这是关键-参数中的那些位实际上可能是指针(或引用),它们不直接包含数据,而是在内存中包含包含实际值的位置。

Examples: in a function like this: 示例:在这样的函数中:

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

naming the output variable "output doesn't change anything - there is no way for the caller to get the result. 将输出变量命名为“输出不会改变任何内容-调用者无法获得结果。

But like this: 但是像这样:

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

the "&" indicates that the output parameter is a reference to the memory location where the output should be stored. “&”表示输出参数是对应该存储输出的存储位置的引用。 It's syntactic sugar in C++ that is equivalent to this 'C' code: 它是C ++中的语法糖,等效于此“ C”代码:

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

The pointer dereference is hidden by the reference syntax but the idea is the same. 指针取消引用被引用语法隐藏,但是思想是相同的。

Arrays perform a similar syntax trick, they are actually passed as pointers, so 数组执行类似的语法技巧,它们实际上作为指针传递,因此

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

and

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

are essentially equivalent. 基本相同。 Note that in either case there is no way to determine the size of the arrays. 请注意,无论哪种情况,都无法确定数组的大小。 Therefore, it is generally considered good practice to pass pointers and lengths: 因此,通常认为传递指针和长度是一种好的做法:

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

Output parameters like this are used in multiple cases. 这样的输出参数在多种情况下使用。 A common one is to return multiple values since the return type of a function can be only a single value. 一种常见的方法是返回多个值,因为函数的返回类型只能是单个值。 (While you can return an object that contains multiple members, but you can't return multiple separate values directly.) Another reason why output parameters are used is speed. (虽然您可以返回包含多个成员的对象,但是不能直接返回多个单独的值。)使用输出参数的另一个原因是速度。 It's frequently faster to modify the output in place if the object in question is large and/or expensive to construct. 如果所讨论的对象较大且/或构造成本很高,则通常可以更快地在适当位置修改输出。

Another programming paradigm is to return a value that indicates the success/failure of the function and return calculated value(s) in output parameters. 另一个编程范例是返回一个指示函数成功/失败的值,并在输出参数中返回计算值。 For example, much of the historic Windows API works this way. 例如,许多历史悠久的Windows API都以这种方式工作。

An array is a low-level C++ construct. 数组是低级C ++构造。 It is implicitly convertible to a pointer to the memory allocated for the array. 它可以隐式转换为指向为数组分配的内存的指针。

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.

The confusing thing about arrays is that a function declaration void foo(int bar[]); 关于数组的令人困惑的事情是函数声明void foo(int bar[]); is equivalent to void foo(int *bar); 等同于void foo(int *bar); . So foo(a) doesn't copy the array a ; 所以foo(a)不会复制数组a instead, a is converted to a pointer and the pointer - not the memory - is then copied. 而是将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 points to the same memory that a does so modifying the contents of array pointed to by bar is the same as modifying the contents of array a . bar指向相同存储器a确实如此修改数组的内容指向bar相同修改数组的内容a

In C++ you can also pass objects by reference ( Type &ref; ). 在C ++中,您还可以按引用传递对象( Type &ref; )。 You can think of references as aliases for a given object. 您可以将引用视为给定对象的别名 So if you write: 因此,如果您写:

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

b is effectively an alias for a - by modifying b you modify a and vice versa. b实际上是一个别名a -通过修改b修改a ,反之亦然。 Functions can also take arguments by reference: 函数也可以通过引用来接受参数:

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

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

Again, bar is little more than an alias for a , so by modifying bar you will also modify a . 同样, bar仅仅是a的别名,因此通过修改bar您还将修改a


The library of mathematical routines you have is using these features to store results in an input variable. 您拥有的数学例程库正在使用这些功能将结果存储在输入变量中。 It does so to avoid copies and ease memory management. 这样做是为了避免复制并简化内存管理。 As mentioned by @Baum mit Augen, the method can also be used as a way to return multiple values. 如@Baum mit Augen所述,该方法还可以用作返回多个值的一种方法。

Consider this code: 考虑以下代码:

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

While returning result , foo will make a copy of the vector, and depending on number (and size) of elements stored the copy can be very expensive. 在返回resultfoo将复制该向量,并且根据所存储元素的数量(和大小),该副本可能非常昂贵。

Note: Most compilers will elide the copy in the code above using Named Return Value Optimization (NRVO). 注意:大多数编译器会的Elid复制上述使用命名返回值优化(NRVO)中的代码。 In general case, though, you have no guarantee of it happening. 但是,在一般情况下,您无法保证会发生这种情况。

Another way to avoid expensive copies is to create the result object on heap , and return a pointer to the allocated memory: 避免昂贵拷贝的另一种方法是在heap上创建结果对象,并返回指向已分配内存的指针:

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

The caller needs to manage the lifetime of the returned object, calling delete when it's no longer needed. 调用方需要管理返回对象的生存期,在不再需要它时调用delete Faililng to do so can result in a memory leak (the memory stays allocated, but effectively unusable, by the application). 否则可能会导致内存泄漏 (内存保持分配状态,但实际上无法被应用程序使用)。

Note: There are various solutions to help with returning (expensive to copy) objects. 注意:有多种解决方案可帮助返回(昂贵的复制)对象。 C++03 has std::auto_ptr wrapper to help with lifetime management of objects created on heap. C ++ 03具有std::auto_ptr包装器,可帮助您对在堆上创建的对象进行生命周期管理。 C++11 adds move semantics to the language, which allow to efficiently return objects by value instead of using pointers. C ++ 11在语言中添加了移动语义 ,从而允许通过值而不是使用指针来有效地返回对象。

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

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