从返回类型的函数中获取结果'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。之后在调用"转置"answers"matrixXvector"时会发生类似的过程。
为什么输出变量是输入参数之一?如何返回结果并将其用于进一步的计算?这种传递和返回参数是如何工作的?
因为我是一个初学者,也从来没有见过这种风格的编码,我不明白如何传递参数,特别是在这些函数中给出输出。因此,我不知道如何使用它们,也不知道对它们有什么期望(它们实际会做什么)。因此,如果您能解释清楚这些过程,我将非常感激。
EXTRA:
谢谢大家的精彩回答。这是我第一次几乎无法决定接受哪个答案,即使我这样做了,我也觉得对别人不公平。我想添加一个额外的问题,如果有人愿意回答(作为评论就足够了)。
这是一种返回某些或多个值的"旧"(但仍然流行)风格。它是这样工作的:
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];
}
Baum的回答是准确的,但可能没有C/c++初学者想要的那么详细。
进入函数的实际参数值总是按值传递(即位模式),不能以调用者可读的方式更改。然而——这是关键——实参中的那些位实际上可能是指针(或引用),它们不直接包含数据,而是包含内存中包含实际值的位置。
示例:在这样的函数中:
void foo(double x, double output) { output = x ^ 2; }
将输出变量命名为"output "不会改变任何东西——调用者没有办法得到结果。
但是像这样:
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;
}
当返回result
时,foo
将复制vector对象,根据存储元素的数量(和大小),复制的代价可能非常大。
另一种避免昂贵复制的方法是在堆上创建结果对象,并返回指向已分配内存的指针:注意:大多数编译器会使用命名返回值优化(NRVO)来省略上面代码中的副本。但在一般情况下,你不能保证它会发生。
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在语言中增加了移动语义,允许通过值
- 使用具有默认参数的函数模板进行 decltype 会使结果混乱(一个有趣的问题或 gcc 的错误)
- 执行参数未提供预期结果
- 术语的计算结果不是采用0个参数的函数
- 错误 C2064:术语的计算结果不是采用 3 个参数的函数
- C++:通过函数参数传递的值给出不同的结果
- 结果失败或多个参数无效
- 使用函数结果作为参数的 C++
- 添加可变参数函数的错误结果
- 从可调用可变参数元组中的函数结果创建元组
- (WMI)ExecMethod out 参数 - 无论调用的结果如何,ResultingSnapshot 都是 NULL
- C++:术语的计算结果不是采用 1 个参数的函数
- 在线程错误 C2064 中:term 的计算结果不为 0 个参数的函数
- 错误 C2064:term 的计算结果不是采用 1 个参数的函数 - 关于线程的一些东西
- 提升线程:术语的计算结果不为 0 个参数
- 当类的对象更改其参数时,如何获得结果
- 是否可以使用自动占位符来推断非类型模板参数中的函数结果
- 为什么在类构造函数方法中,std::string 参数在调试时显示不同的结果?
- 使用 constexpr 函数的结果作为模板参数(clang vs gcc)
- 在 c++ 代码中具有相同的函数和参数的不同结果
- 警告:用两个参数构造函数返回对象时,表达结果未使用