1. C++ 的第一个程序
学习任何编程语言,第一个程序通常都是输出"Hello World"。下面是一个最基础的 C++ 程序,它展示了 C++ 程序的基本框架:
cpp
#include <iostream>
using namespace std;
int main() {
cout << "Hello, World!" << endl;
return 0;
}
这个程序虽然简单,但包含了 C++ 程序的核心结构:
- 头文件引入 :
#include <iostream>引入输入输出流库,是使用cin、cout的前提。 - 命名空间 :
using namespace std;引入标准命名空间,之后才能直接使用cout、endl等。 - 主函数 :
int main()是程序的入口,程序从这里开始执行。 - 返回值 :
return 0;表示程序正常结束。
理解这个基础框架后,下面我们逐步深入 C++ 的核心特性。
2. 命名空间 namespace
命名空间是 C++ 中用于组织代码、避免命名冲突的重要机制。当多个库或模块中存在同名函数、变量或类时,命名空间可以将它们隔离在不同的作用域中。
2.1 namespace 的价值:解决命名冲突问题
在大型项目中,不同开发者或不同库之间很容易出现同名函数、同名变量。例如,两个库都定义了 print() 函数,如果没有命名空间,编译器就无法区分该调用哪一个,从而产生命名冲突。
cpp
// 两个库都定义了同名函数,产生冲突
// 库A
void print() {
cout << "Library A" << endl;
}
// 库B
void print() {
cout << "Library B" << endl;
}
// 直接调用 print() 会报错:无法确定调用哪个版本
命名空间通过将代码划分到不同的作用域中,完美解决了这个问题。不同命名空间中的同名成员互不干扰,通过作用域限定符即可区分。
2.2 namespace 的定义:
自定义命名空间的语法非常简单,使用 namespace 关键字后跟命名空间名称,再用花括号包裹成员即可(注意花括号后面无分号):
cpp
#include <iostream>
namespace LRT {
int value = 100;
}
}
int main() {
// 使用命名空间中的成员
std::cout << LRT::value << std::endl;
return 0;
}
上述代码中,LRT 是一个自定义命名空间,通过 :: 作用域解析运算符来访问其中的成员。命名空间可以嵌套定义,也可以在不同文件中多次定义同一个命名空间,编译器会自动合并。
2.3 命名空间使用:三种使用方式
C++ 提供了三种使用命名空间成员的方式,各有适用场景:
方式一:域限定符(::)
通过 命名空间名::成员名 的方式访问,最安全、最明确,不会产生歧义:
cpp
#include <iostream>
namespace LRT {
int value = 100;
}
int main() {
std::cout << LRT::value << std::endl; // 通过 :: 访问
return 0;
}
方式二:using 声明
使用 using 命名空间名::成员名; 只引入指定的单个成员,按需引入,既简化书写又避免引入过多名称:
cpp
#include <iostream>
using std::cout;
using std::endl;
int main() {
cout << "Hello, World!" << endl; // 直接使用 cout 和 endl
return 0;
}
方式三:using 编译指令
使用 using namespace 命名空间名; 一次性引入命名空间中的所有成员,书写最方便,但容易引入命名冲突:
cpp
#include <iostream>
using namespace std; // 引入 std 中所有成员
int main() {
cout << "Hello, World!" << endl; // 直接使用,无需 std::
return 0;
}
三种方式对比总结:
| 使用方式 | 语法 | 优点 | 缺点 |
|---|---|---|---|
| 域限定符 | std::cout |
最安全,无歧义 | 书写繁琐 |
| using 声明 | using std::cout; |
按需引入,较安全 | 每个成员都要单独声明 |
| using 编译指令 | using namespace std; |
书写最方便 | 易引入命名冲突 |
在实际开发中,建议优先使用域限定符或 using 声明,在大型项目中避免使用 using 编译指令,防止命名冲突。
3. C++ 输入 & 输出
C++ 使用 cin 和 cout 进行标准输入输出,它们定义在 <iostream> 头文件中,属于 std 命名空间。
cout 输出
cout 配合插入运算符 << 向屏幕输出内容,endl 表示换行并刷新缓冲区:
cpp
#include <iostream>
using namespace std;
int main() {
int age = 20;
cout << "我的年龄是:" << age << endl;
cout << "两行输出" << endl;
cout << "第二行" << endl;
return 0;
}
cin 输入
cin 配合提取运算符 >> 从键盘读取数据,可以连续读取多个变量:
cpp
#include <iostream>
using namespace std;
int main() {
int a, b;
cout << "请输入两个整数:";
cin >> a >> b; // 连续输入,用空格或回车分隔
cout << "a + b = " << a + b << endl;
return 0;
}
<<是流插⼊运算符,>>是流提取运算符。(C语⾔还⽤这两个运算符做位运算左移/右移)
字符串输入
读取字符串时,cin 遇到空格会停止读取,如果需要读取包含空格的整行,使用 getline 函数:

cpp
#include <iostream>
#include <string>
using namespace std;
int main() {
string name;
cout << "请输入姓名:";
getline(cin, name); // 读取整行,包含空格
cout << "你好," << name << endl;
return 0;
}
IO 流是 C++ 程序与外界交互的基础,几乎所有程序都需要用到输入输出功能。
4. 缺省参数
缺省参数(默认参数)允许在函数声明时为形参指定默认值,调用函数时可以省略该参数,编译器会自动使用默认值。
cpp
#include <iostream>
using namespace std;
// 带缺省参数的函数
void greet(string name = "游客") {
cout << "你好," << name << endl;
}
int main() {
greet(); // 省略参数,使用默认值"游客"
greet("小明"); // 传入参数,使用"小明"
return 0;
}
缺省参数的使用规则:
- 从右向左连续设置 :缺省参数必须从右往左连续设置,不能跳跃。例如
void f(int a, int b = 10, int c = 20);合法,而void f(int a = 1, int b, int c = 3);不合法。 - 声明和定义不能同时设置:缺省参数通常在函数声明中给出,定义中不要再重复设置,否则会报错。
- 调用时从左向右传参:调用时省略参数只能从右往左省略,不能跳过中间的参数。
cpp
#include <iostream>
using namespace std;
// 缺省参数从右向左连续设置
void printInfo(string name, int age = 18, string city = "北京") {
cout << name << "," << age << "岁,来自" << city << endl;
}
int main() {
printInfo("张三"); // 全部使用默认值
printInfo("李四", 25); // city 使用默认值
printInfo("王五", 30, "上海"); // 全部传入
return 0;
}
缺省参数让函数调用更加灵活,减少了重复代码,是 C++ 中非常实用的特性。
5. 函数重载
函数重载允许在同一作用域 中定义多个函数名相同 但参数列表不同 的函数。编译器根据调用时传入的参数类型和个数,自动选择匹配的函数版本。
注意:返回值不同的函数不算是函数重载。
cpp
#include <iostream>
using namespace std;
// 三个同名函数,参数列表不同
int add(int a, int b) {
return a + b;
}
double add(double a, double b) {
return a + b;
}
int add(int a, int b, int c) {
return a + b + c;
}
int main() {
cout << add(1, 2) << endl; // 调用 int add(int, int),输出3
cout << add(1.5, 2.5) << endl; // 调用 double add(double, double),输出4
cout << add(1, 2, 3) << endl; // 调用 int add(int, int, int),输出6
return 0;
}
函数重载的规则:
- 参数列表必须不同:参数的类型、个数或顺序至少有一项不同。
- 返回值类型不能作为区分依据:仅返回值不同不能构成重载。
- 同一作用域:重载函数必须在同一作用域中定义。
函数重载让代码更加直观,同一个函数名可以处理不同类型的数据,提高了代码的可读性和复用性。
6. 引用(C++ 核心重点)
引用是 C++ 中非常重要的特性,它相当于给变量起了一个别名,操作引用就是操作原变量本身。
6.1 引用的概念和定义
引用使用 & 符号定义,语法为 类型& 引用名 = 原变量;:
cpp
#include <iostream>
using namespace std;
int main() {
int a = 10;
int& b = a; // b 是 a 的引用(别名)
cout << "a = " << a << endl; // 10
cout << "b = " << b << endl; // 10
b = 20; // 修改 b 就是修改 a
cout << "a = " << a << endl; // 20
return 0;
}
引用和被引用变量指向同一块内存空间,修改任何一个,另一个也会同步变化。
6.2 引用的特性
引用具有以下几个重要特性:
- 必须初始化:引用在定义时必须绑定一个变量,不能先声明后绑定。
- 不能更改引用目标:引用一旦绑定某个变量,就不能再改为引用其他变量。
- 引用不是独立变量:引用不占用独立的内存空间,它只是原变量的别名。
cpp
#include <iostream>
using namespace std;
int main() {
int a = 10;
int b = 20;
int& ref = a; // 必须初始化
// int& ref2; // 错误:引用必须初始化
ref = b; // 注意:这不是让 ref 引用 b,而是把 b 的值赋给 a
cout << "a = " << a << endl; // 20
cout << "ref = " << ref << endl; // 20
cout << "b = " << b << endl; // 20
return 0;
}
上述代码中,ref = b 只是把 b 的值赋给了 a,ref 仍然引用 a,并没有改变引用目标。
6.3 引用的使用:做函数参数、做函数返回值
引用做函数参数
引用传参可以让函数直接操作实参本身,避免拷贝开销,同时可以在函数内部修改实参的值:
cpp
#include <iostream>
using namespace std;
void swapByValue(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
// 引用传递:直接修改实参
void swapByRef(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
int main() {
int x = 10, y = 20;
swapByValue(&x, &y);
cout << "取地址传递后:x = " << x << ", y = " << y << endl;
swapByRef(x, y);
cout << "引用传递后:x = " << x << ", y = " << y << endl;
return 0;
}
引用做函数返回值
引用作为返回值时,返回的是变量的别名,可以避免拷贝,并且可以对返回值进行赋值操作:
cpp
#include <iostream>
using namespace std;
int globalValue = 100;
// 返回引用
int& getGlobal() {
return globalValue;
}
int main() {
cout << "初始值:" << globalValue << endl; // 100
getGlobal() = 200; // 通过引用返回值直接修改全局变量
cout << "修改后:" << globalValue << endl; // 200
return 0;
}
需要注意的是,不能返回局部变量的引用,因为局部变量在函数结束后就被销毁了,返回的引用会变成悬空引用。
6.4 const 引用:常引用
const 引用(常引用)使用 const 类型& 定义,它不能通过引用修改原变量的值,主要用于保护数据不被修改,同时可以接收常量:
cpp
#include <iostream>
using namespace std;
int main() {
int a = 10;
const int& ref = a; // 常引用
// ref = 20; // 错误:不能通过常引用修改值
a = 20; // 可以直接修改原变量
cout << "ref = " << ref << endl; // 20
// 常引用可以接收常量
const int& c = 100;
cout << "c = " << c << endl; // 100
return 0;
}
常引用在函数参数中非常常用,既能避免拷贝开销,又能保证数据不被意外修改:
cpp
#include <iostream>
#include <string>
using namespace std;
// 常引用做参数,保护数据不被修改
void printInfo(const string& name) {
// name = "修改"; // 错误:不能修改
cout << "姓名:" << name << endl;
}
int main() {
string name = "张三";
printInfo(name);
printInfo("李四"); // 常引用可以接收常量
return 0;
}
注意:在有临时变量拷贝的情况下,C++规定临时对象具有常性,引用都需要有 const 来修饰
这里有涉及权限的放大缩小问题,详见主页讲解。
6.5 指针和引用的关系
指针和引用都可以间接访问变量,但它们在语法和使用上有明显区别:
| 对比项 | 引用 | 指针 |
|---|---|---|
| 定义 | 变量的别名 | 存储变量地址的变量 |
| 初始化 | 必须初始化 | 可以不初始化 |
| 是否可改变指向 | 不能改变引用目标 | 可以改变指向 |
| 占用空间 | 不单独占用空间 | 占用独立空间 |
| 访问方式 | 直接使用,无需解引用 | 需要 * 解引用 |
| 空值 | 不能为空 | 可以为 NULL |
cpp
#include <iostream>
using namespace std;
int main() {
int a = 10;
// 引用
int& ref = a;
ref = 20; // 直接使用
// 指针
int* ptr = &a;
*ptr = 30; // 需要解引用
cout << "a = " << a << endl; // 30
cout << "ref = " << ref << endl; // 30
cout << "*ptr = " << *ptr << endl; // 30
return 0;
}
从底层来看,引用在编译后本质上也是通过指针实现的,但引用在语法层面更安全、更简洁。在 C++ 中,优先使用引用,只有在需要改变指向或可能为空时才使用指针。
7. inline 内联函数
内联函数用于解决小函数频繁调用带来的性能开销。普通函数调用需要压栈、跳转、返回等操作,而内联函数在编译时直接将函数体代码展开到调用处,避免了函数调用的开销。
cpp
#include <iostream>
using namespace std;
// 内联函数:编译时直接展开
inline int square(int x) {
return x * x;
}
int main() {
// 调用 square(5) 时,编译器直接展开为 5 * 5
cout << "5的平方 = " << square(5) << endl;
cout << "8的平方 = " << square(8) << endl;
return 0;
}
使用内联函数的注意事项:
- 适用于小函数:函数体很小(通常几行)时使用内联才有意义。
- inline 只是建议 :
inline关键字只是向编译器提出建议,编译器会根据函数复杂度决定是否真正内联。 - 避免复杂函数:包含循环、递归或大量代码的函数不适合内联,编译器通常会忽略 inline 建议。
- 定义放在头文件:内联函数通常在头文件中定义,以便每个编译单元都能看到完整定义。
内联函数以空间换时间,适合频繁调用的小函数,如简单的 getter、setter 或数学计算函数。
8. nullptr 空指针
nullptr 是 C++11 引入的空指针常量,用于替代传统的 NULL。它类型安全,可以避免 NULL 在重载场景下产生的歧义问题。
cpp
#include <iostream>
using namespace std;
void func(int x) {
cout << "调用 int 版本:" << x << endl;
}
void func(char* p) {
cout << "调用 char* 版本" << endl;
}
int main() {
// NULL 在 C++ 中通常被定义为 0,会产生歧义
// func(NULL); // 错误:NULL 是整数 0,无法确定调用哪个版本
func(nullptr); // 明确调用 char* 版本,类型安全
return 0;
}
NULL 与 nullptr 的区别:
- NULL :在 C++ 中通常被定义为整数
0,本质是整型常量,在函数重载场景下会产生歧义。 - nullptr :是真正的空指针类型
std::nullptr_t,可以隐式转换为任何指针类型,但不能转换为整数类型,类型安全。
cpp
#include <iostream>
using namespace std;
int main() {
int* p1 = nullptr; // 正确:空指针
int* p2 = NULL; // 正确:NULL 等价于 0
if (p1 == nullptr) {
cout << "p1 是空指针" << endl;
}
// nullptr 不能赋值给整数类型
// int x = nullptr; // 错误:无法转换
// NULL 可以赋值给整数类型(因为 NULL 就是 0)
int y = NULL; // 合法但不推荐
cout << "y = " << y << endl; // 0
return 0;
}
在现代 C++ 编程中,建议统一使用 nullptr 表示空指针,避免 NULL 带来的类型歧义问题。
9. 综合示例:综合运用本节知识点
下面通过一个综合示例,将本节学习的命名空间、输入输出、缺省参数、函数重载、引用、内联函数和 nullptr 结合起来使用:
cpp
#include <iostream>
using namespace std;
// 内联函数:计算平方
inline int square(int x) {
return x * x;
}
// 函数重载:计算面积
int area(int w, int h) {
return w * h;
}
double area(double r) {
return 3.14159 * r * r;
}
// 引用做参数:交换两个数
void swap(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
// 缺省参数
void showInfo(string name, int age = 18) {
cout << name << "," << age << "岁" << endl;
}
int main() {
// 输入输出
int x, y;
cout << "请输入两个整数:";
cin >> x >> y;
// 引用交换
swap(x, y);
cout << "交换后:x = " << x << ", y = " << y << endl;
// 内联函数
cout << "x的平方 = " << square(x) << endl;
// 函数重载
cout << "矩形面积 = " << area(3, 4) << endl;
cout << "圆面积 = " << area(2.0) << endl;
// 缺省参数
showInfo("张三");
showInfo("李四", 25);
// nullptr
int* ptr = nullptr;
if (ptr == nullptr) {
cout << "ptr 是空指针" << endl;
}
return 0;
}
C++ 是一门功能强大、应用广泛的编程语言,虽然学习曲线相对陡峭,但掌握这些核心基础后,就能逐步应对更复杂的开发需求。希望本文能帮助你扎实掌握 C++ 的这些关键知识点,后续可以结合官方文档和实际项目不断深入。