C++ 传值 传指针 传引用
文章目录
- C++ 传值 传指针 传引用
- 引用
- const 引用
- 指针的引用和引用的指针
- 引用作为函数参数
- 例子
C++ 函数传参主要是三种方法:传值、传指针、传引用。
说在前面:
- 传引用相对来说是简单一点的,也更加优雅。
明确几点:
- 指针也是一个变量,指针中存的值是一个地址,
- 非main函数的其他调用函数传递的形参只在其函数范围内起作用。即单纯的形式参数,只在其调用函数范围内其作用,
- 由于2的原因,当我们在调用函数中改变了某些数据,并且希望这种改动能够被携带出来。我们需要使用引用& 或 指针*。
- 使用传值的方法,当变量在调用函数中改变时,在返回的函数中是不会改变的。(原因在于,传值方法,调用函数生成了变量的副本,改变的是变量的副本)默认的参数传递语义是值传递(pass-by-value):函数接收其参数的副本。修改这些参数后,原始实参将保持不变。
请注意本文中的很多内容来自《C++20高级编程 第5版》,感谢本书的作者和翻译人员。
引用
C++中的引用(reference)是另一个变量的别名。对引用的所有修改都会更改其引用的变量的值。可以将引用视为隐式指针,它省去了获取变量地址和解引用指针的麻烦。
引用变量必须总是在创建时初始化。
具体来说,就是
int x = 3, y = 4;
int &xRef = x; // 其中xRef是引用变量, 该语句不能写为int &xRef; xRef = x; 这是不合法的
xRef = y; // 此时,x 中的值被修改,此时x = 4;xRef = 5; // 这是被允许的,这相当于将 x值改为5;xRef = &y; // 这是不合法的,编译器报错。
该程序也表明,引用与指针非常相似。
int main() {std::cout << "Hello, World!" << std::endl;int x = 5;int &xrf = x;std::cout << x << std::endl;std::cout << xrf << std::endl;xrf = 6;std::cout << x << std::endl;std::cout<< xrf << std::endl;std::cout << &xrf << std::endl;std::cout << &x << std::endl;return 0;
}
const 引用
- 引用默认是 const(因为我们不能改变引用的指向)。
- 无法创建引用的引用(即不能对别名本身在起一个别名, 引用必须针对一个实体)。
int main(){std::cout << "Hello, World!" << std::endl;int x = 5, y = 4;int &xrf = x;xrf = &y; // 针对1的解释,即不能改变引用的指向int &xxrf = xrf; //针对2的解释,即不能创建引用的引用。return 0;
}
针对引用使用 const,其含义为:
int z;
const int& zRef {z};
zRef = 4; // 编译器报错
对于引用使用const,使得我们无法通过该引用来改变变量的值。然而,我们可以使用变量自身进行值的更改。
指针的引用和引用的指针
// 指向int的指针的引用
int* intP{ nullptr };
int*& ptrRef {intP};
ptrRef = new int;
*ptrRef = 5;
ptrRef是对intP的引用,intP是对int的指针,修改ptrRef会更改intP。也就是说ptrRef是一个指针的引用。
int x = 3; // int x {3};
int& xRef {x};
int* xPtr {&xRef};
*xPtr = 100;
这是一个引用的指针。也就是说,该代码通过取x的引用的地址来将xPtr设置为指向x。将100赋值给*xPtr会将x的值更改为100。
xPtr是指针,xPtr存放的是一个地址,*对该地址中的内容进行访问。xPtr==&xRef
,也就是xRef不是一个地址
引用作为函数参数
C++程序员通常不使用独立的引用变量或引用数据成员,引用的最常见用途是用于函数的参数。默认的参数传递语义是值传递(pass-by-value):函数接收其参数的副本。修改这些参数后,原始实参将保持不变。
传指针:栈中变量的指针经常在C语言中使用,以允许函数修改其他栈帧中的变量。通过对指针解引用,函数可以修改表示该变量的内存,即使该变量不在当前的栈帧中。然而,指针是比较麻烦的。
传引用:C++中提供了一种更好的机制,称为引用传递(pass-by-reference),参数是引用而不是指针。
从函数返回对象的推荐方法是通过值返回,而不是使用一个输出参数。
引用传递(pass-by-reference)的好处:
- 效率: 复制大型的对象可能花费很长时间,引用传递知识将该对象的一个引用传给函数。
- 支持:不是所有的类都允许值传递。
建议多使用:const &
例子
使用引用可以比使用指针干净得多。本书中使用了一个很好的例子,该例子中使用了标准库容器vector。
void seperateOddsAndEvens(const vector<int>& arr, vector<int>& odds, vector<int>& evens){for (int i : arr){if (i % 2 == 1){odds.push_back(i);}else{evens.push_back(i);}}
}
使用引用能够让代码非常简洁,而且效率也非常高,这是可以明确的。使用引用,可以把值传递出来。
vector<int> vecUnSplit {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
vector<int> odds, evens;
separateOddsAndEvens(vecUnSplit, odds, evens)
其对应的指针版本为:
void separateOddsAndEvens(const int arr[], size_t size, int** odds, size_t* numOdds, int** evens, size_t* numEvens){//count the number of odds and evens*numOdds = *numEvens = 0;for(size_t i = 0; i < size; ++i){if (arr[i] % 2 == 1){++(*numOdds);}else{++(*numEvens);}}// 指针需要先统计一下// Allocate two new arrays of the approriate size*odds = new int [*numOdds];*evens = new int [*numEvens];// Copy the odds and evens to the new arrayssize_t oddsPos = 0, evenPos = 0;for (size_t i = 0; i < size; ++i){if (arr[i] % 2 == 1){(*odds)[oddsPos++] = arr[i];}else{(*evens)[evensPos++] = arr[i];}}
}