C++ 核心基础概念梳理
C++ 兼容 C,这意味着你可以在 C++ 环境下编写 C 代码。
C++之父:本贾尼・斯特劳斯特卢普。
一、命名空间
在 C++ 中,标识符(变量、函数、类、常量、命名空间)的查找遵循特定的规则。理解这些规则有助于避免命名冲突。
1. 标识符查找规则
编译器查找标识符时,会从当前最内层的作用域开始查找;一旦找到,就不会再去外层作用域查找。
2. 命名冲突的本质
同一作用域下不能存在同名的标识符。
来看一个例子:
#include <stdio.h>
#include <stdlib.h>
int main() {
int rand = 10;
printf("%d\n", rand); // 代码可以正确执行
}
这里 rand 是局部变量,遮蔽了 stdlib.h 中的全局函数 rand。但如果写成全局变量呢?
#include <stdio.h>
#include <stdlib.h>
int rand = 10; // 全局变量
int main() {
printf("%d\n", rand); // 会编译报错
}
当 rand 是全局变量时,它与 stdlib.h 中的函数名冲突,导致编译错误。这就是为什么我们需要命名空间来隔离不同的标识符。
3. 命名空间的使用
① 概念与语法
命名空间就像是一个自定义的'域',用来存放标识符,解决同一个域下的命名冲突问题。访问时使用 命名空间名::标识符。
namespace mycode {
int rand = 10;
}
int main() {
printf("%d\n", mycode::rand); // 正常打印出 10
}
② 命名空间的嵌套
如果父域下仍有冲突,可以使用嵌套命名空间。
#include <stdio.h>
namespace code {
namespace codeA {
int a = 10;
}
namespace codeB {
int a = 20;
}
}
int main() {
printf("%d\n", code::codeA::a); // 打印 10
printf("%d\n", code::codeB::a); // 打印 20
}
③ 访问方式
- 指定访问:使用
::运算符。 - 展开某个成员:使用
using 命名空间名::成员;。 - 展开全部成员:使用
using namespace 命名空间名;。
注意:在多个文件中使用命名空间时,C++ 允许同一个命名空间被写在不同的文件中,编译器会自动把它们合并成一个。标准库就存放在名为 std 的命名空间中。
二、C++ 的输入输出
1. <iostream> 头文件
这是 Input Output Stream 的缩写,提供了 cin、cout 以及流操作符 <<、>>。
2. std::cin 和 std::cout
由于标准库都在 std 命名空间中,使用前需加上 std:: 前缀,或者使用 using namespace std;。
cin(console input):控制台输入。cout(console output):控制台输出。
3. 流操作符
<<:流插入运算符,配合cout使用。>>:流提取运算符,配合cin使用。
#include <iostream>
using namespace std;
int main() {
cout << "hello world" << endl;
cout << "Hello C++" << endl << "Welcome" << endl;
}
C++ 能自动识别变量类型,不需要像 C 语言那样手动指定占位符。
4. endl
表示换行并刷新缓冲区。
三、缺省参数
定义函数时,可以给参数指定默认值。调用时如果不传该参数,则使用默认值。
1. 全缺省
所有参数都有默认值。
void Print(int a = 3, int b = 2) {
cout << a << "," << b << endl;
}
// Print(); // 打印 3,2
2. 半缺省
部分参数有默认值。规定缺省值必须从右向左连续设置。调用时必须传够没有缺省值的参数。
void Print(int a, int b = 1, int c = 2) {
cout << a << "," << b << "," << c << endl;
}
// Print(0); // 打印 0,1,2
注意:函数声明和定义分离时,缺省参数只能出现在声明部分。
四、函数重载
在同一作用域内,可以存在多个同名函数,但必须满足以下条件之一:
- 参数个数不同
- 参数类型不同
- 参数顺序不同
int Add(int a, int b) { return a + b; }
int Add(int a, int b, int c) { return a + b + c; }
double Calc(int a, double b) { return a + b; }
double Calc(double a, int b) { return a - b; }
五、引用(reference)
1. 基本概念
引用是给已有变量起的一个别名,直接在原内存上操作,不产生副本。
2. 引用 vs 指针
用 Swap 函数对比一下引用和指针的区别:
指针写法:
void Swap(int* a, int* b) {
int tmp = *a;
*a = *b;
*b = tmp;
}
// 调用:Swap(&a, &b);
引用写法:
void Swap(int& a, int& b) {
int tmp = a;
a = b;
b = tmp;
}
// 调用:Swap(a, b);
引用写法更简洁,且避免了野指针的风险。
3. 引用要求
- 初始化:定义引用时必须初始化。
- 不可重绑定:引用一旦绑定某个实体,就不能再指向其他对象。
- 多引用:一个变量可以有多个引用,修改任意一个都会影响原变量。
4. const 引用
被 const 修饰的引用,权限只能不变或变小(只读)。这常用于修饰临时变量,延长其生命周期。
int main() {
const int& ra = 20; // 安全,临时变量被保护
// int& rra = 20; // 不安全,权限放大
}
六、宏
宏是在预处理阶段进行的文本替换,分为无参宏和有参宏。
#define val 18
#define Add(a,b) ((a) + (b))
注意:有参宏建议给参数加括号,避免运算符优先级问题。宏无法调试,且强制替换,不如函数安全。
七、内联函数
被 inline 修饰的函数,编译器会在调用处展开代码,无需建立栈帧,提高效率。
inline int Add(int a, int b) {
return a + b;
}
区别:内联函数是真正的函数,支持调试,而宏只是文本替换。
八、nullptr
C++11 引入的关键字,用于表示空指针。
void Print(int* x) { cout << "int*" << endl; }
void Print(int x) { cout << "int" << endl; }
int main() {
Print(NULL); // 可能误判为 int
Print(nullptr); // 明确匹配 int*
}
使用 nullptr 可以避免因 NULL 定义为 0 而在函数重载时产生的歧义。

