值传递
函数参数传值会发生拷贝,例如:
#include <iostream>
namespace Log {
void print(std::string value){
std::cout << value << std::endl;
}
}
int main(int argc, char *argv[])
{
std::string hello = "hello world!";
Log::print(hello);
return 0;
}
当变量 hello 传给函数 print 时,会先将 hello 的值拷贝一份赋给 print 函数的形式参数 value。
对于其他语言,string 作为基本类型也都是值传递。但是 C/C++可以将其变为引用传递,包括 int 类型等其他任意类型,本质是取地址即指针传递。
引用传递
当然在 C++中也有引用,只是默认值传递。
使用引用:
#include <iostream>
namespace Log {
// 形参为引用类型,传递地址,不发生拷贝
void print(std::string& value){
std::cout << value << std::endl;
}
}
int main(int argc, char *argv[])
{
std::string hello = "hello world!";
// 定义指向变量hello的引用
std::string& hello_ref = hello;
Log::print(hello_ref);
return 0;
}
这样的话,就可以避免发生拷贝,减少内存占用。
常引用
上面的例子有时候不免太麻烦了,定义了变量后,还要在定义一个它的引用,然后后续使用的全是它的引用(和指针差不多)。实际上可以两步合起来,使用常引用即可:
#include <iostream>
namespace Log {
// 形参为常引用类型
void print(const std::string& value){
std::cout << value << std::endl;
}
}
int main(int argc, char *argv[])
{
// 定义常引用hello
const std::string& hello = "hello world!";
Log::print(hello);
return 0;
}
C++不允许这样的定义:
std::string& hello = "hello world!";
这是不安全的,必须标注 const,常引用可以保证引用指向不丢失。
常引用可以避免值拷贝,以地址传递。但缺点是,由于 const 导致不能对引用指向的值做修改。
右值引用与移动语义
C++11 引入的重要特性。解决了常引用不能对引用指向的值做修改的问题,又兼有引用传递的优点。
右值就是“右边的值”,在传递的过程中不会被拷贝,只传递其地址。但又可以修改指向的值。
例如:
#include <iostream>
#include<utility>
namespace Log {
void print(std::string&& value){
value += "test";
std::cout << value << std::endl;
}
}
int main(int argc, char *argv[])
{
std::string hello = "hello world!";
Log::print(std::move(hello));
std::cout << hello << std::endl;
return 0;
}
输出:
hello world!test
hello world!test
可以看到 [引用 value] 指向的 [变量 hello] 的值被修改了。
分析过程:
首先定义了 std::string 类型变量 hello,然后使用移动语义将其强制转为右值,匹配 Log::print 的函数签名成功,将 hello 变的地址赋给形参 value,Log::print 函数内部通过 value 即 hello 变量的地址修改了 hello 变量的值。
右值引用与常引用重载
函数重载会考虑参数的个数和类型,其中右值引用和常引用的类型是不同的,所以可以发生重载。
例如:
#include <iostream>
#include<utility>
namespace Log {
void print(const std::string& value){
std::cout<< "const ref: " << value << std::endl;
}
void print(std::string&& value){
std::cout<< "right ref: " << value << std::endl;
}
}
int main(int argc, char *argv[])
{
std::string hello = "hello world!";
Log::print(hello);
Log::print(std::move(hello));
return 0;
}
输出:
const ref: hello world!
right ref: hello world!
看到使用了移动语义的匹配了右值引用的签名。