[英]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;
}
在返回result
, foo
将复制该向量,并且根据所存储元素的数量(和大小),该副本可能非常昂贵。
注意:大多数编译器会的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.