[英]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.