本文最后更新于 36 天前,其中的信息可能已经有所发展或是发生改变。
开发环境
ubuntu下的g++编译器即可,随着gcc一起安装的
g++ (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.编译指令
g++ test.cpp -o teststring类型
在c语言中,是没有string这个类型的。想要表达字符串必须借助字符数组或者字符指针,极其不便。c++中原生支持string类型且自带方便的API调用接口。
例如:
int main() {
// 定义string类型并进行拷贝初始化
string str1 = "hello world";
cout << "str1 : " << str1 << endl;
// 构造函数初始化
//以下都是重载函数,c++的特性,允许同名但参数或返回值不同的函数存在
// string();
// string( size_type length, char ch );
// string( const char *str );
// string( const char *str, size_type length );
// string( string &str, size_type index, size_type length );
// string( input_iterator start, input_iterator end );
string str2("hello");
cout << "str2 : " << str2 << endl;
//部分API接口示例
//赋值api assign
string str3;
str3.assign("this is str3");
cout << "str3 : " << str3 << endl;
//拼接api append
string str4 = str3 + str2;
cout << "str4 : "<<str4<<endl;
//c_str 返回指向字符串的指针
const char* str5 = str3.c_str();
cout << "str5 : "<<str5<<endl;
//迭代器循环遍历
for (auto it = str4.begin(); it != str4.end(); ++it) {
std::cout << *it << " "; // 用 * 访问当前位置的字符
}
std::cout << std::endl;
//capacity()函数返回在重新申请更多的空间前字符串可以容纳的字符数
size_t strlen = str4.capacity();
cout << "str4 capacity: " << strlen << endl;
return 0;
}引用
引用,就是给一个已经存在的变量取了一个“外号/别名”。
1. 现实生活中的比喻
- 指针(Pointer):像一张写着房间号的纸条。
- 纸条上写着“302 房间”。纸条本身占用空间,纸条里的地址可以擦掉重写(指向别的房间),纸条也可能是空的(
nullptr)。
- 纸条上写着“302 房间”。纸条本身占用空间,纸条里的地址可以擦掉重写(指向别的房间),纸条也可能是空的(
- 引用(Reference):像人的“外号”。
- 比如“罗翔”的外号叫“张三”。
- “罗翔”和“张三”指的是同一个实体。给“张三”一拳,“罗翔”脸上也会肿;给“张三”发 100 块红包,“罗翔”的钱包就会变鼓。
2. 代码怎么写?
在 C++ 中,在类型后面加个 & 就代表定义一个引用:
C++
#include <iostream>
using namespace std;
int main() {
int a = 10;
int& ref = a; // ref 是 a 的引用(外号)
cout << "a = " << a << endl; // 输出 10
cout << "ref = " << ref << endl; // 输出 10
ref = 20; // 通过外号修改值
cout << "a = " << a << endl; // a 也变成了 20!
// 它们的内存地址一模一样!
cout << &a << " == " << &ref << endl;
}注意:你在使用 ref 的时候,完全不需要像指针那样加 \* 解引用,把它当成普通变量直接用就行了!
3. 引用的“三大死铁律”(比指针省心的地方)
正是因为以下三条规则,引用用起来比指针安全得多:
| 规则 | 指针(int*) | 引用(int&) |
|---|---|---|
| 1. 必须初始化 | 可以不初始化(悬空指针,危险) | 必须在定义时绑定初始值! int& r; ❌ 编译报错 |
| 2. 从一而终 | 随时可以指向其他变量(p = &b) | 一旦绑定,终生不能更改指向! |
| 3. 是否允许为空 | 可以是 nullptr(使用前需要判断 if(p)) | 绝不允许为空,必定指向某个真实存在的变量 |
💡 “从一而终”的小陷阱:
如果你写
ref = b;,这不是让ref转去引用b,而是把b的值赋值给ref所引用的原变量a!
4. 为什么要发明引用?(C++ 创始人的用心)
在 C 语言时代,我们要写一个交换两个数的函数,必须传指针:
// C 语言方式:传指针
void swap(int* x, int* y) {
int temp = *x; // 必须写一堆 *
*x = *y;
*y = temp;
}
int main() {
int a = 1, b = 2;
swap(&a, &b); // 调用时必须手动取地址 &
}这样写语法非常繁琐,满屏幕都是 * 和 &。
C++ 引入引用后,代码变得无比优雅:
// C++ 方式:传引用
void swap(int& x, int& y) { // x 是 a 的别名,y 是 b 的别名
int temp = x; // 像普通变量一样直接写!
x = y;
y = temp;
}
int main() {
int a = 1, b = 2;
swap(a, b); // 直接传 a 和 b,干净利落!
}5. 底层真相:引用到底是个啥?
引用在 C++ 编译器底层的实现,本质上就是一个“被限制了功能的常指针(Type\* const)”。
你写代码:
int& ref = a;
ref = 20;编译器在后台为你做的事:
int* const ref = &a; // 自动帮你取地址,且指针本身不能修改指向
*ref = 20; // 自动帮你加 * 访问内容看明白了吧?引用其实是编译器提供的一种“语法糖”。它把指针繁琐的取地址(&)、解引用(*)、判空(nullptr)全都在后台自动化了,把方便和安全留给了程序员。
6. 最常用的高级写法:const 引用
我们在写函数参数传递对象(比如 string 或自定义大类)时,最推荐的写法就是:const 类型&。
// 既避免了拷贝大对象的内存消耗(高效),又防止函数内部不小心修改了原数据(安全)
void printMessage(const std::string& str) {
// str.append("!"); // ❌ 编译报错!const 保护了原数据不被改动
cout << str << endl;
}总结
- 指针:是一个独立的变量,里面存的是别人的地址,可以随时换指向,可以为空。(灵活但危险)
- 引用:是别人的一个别名,与原变量同生共死,绑定后不可变,不能为空。(安全且书写简洁)
以后看到 int&,就在脑子里替换成“a 的外号”五个字,你就彻底通透了!


