一、C++ 第一个程序:兼容 C 与标准 C++ 版本
C++ 完全兼容 C 语言语法,同时提供了更简洁的标准库用法,两个经典 "Hello World" 实现如下:
1. 兼容 C 语言版本
直接使用 C 语言的stdio.h库,写法与 C 完全一致:
cpp
#include <stdio.h>
int main()
{
printf("Hello World!\n");
return 0;
}
2. 标准 C++ 版本
使用 C++ 标准输入输出库iostream,通过cout输出(需配合命名空间std):
cpp
#include <iostream>
using namespace std; // 展开std命名空间(后续详解)
int main()
{
cout << "Hello World" << endl; // 流插入运算符<<,endl=换行+刷新缓冲区
return 0;
}
二、命名空间
C 语言中没有命名空间概念,当多个文件或库定义同名变量 / 函数时,会出现命名冲突。C++ 的namespace正是为解决此问题而生。
1. C 语言的命名冲突问题
cpp
#include <stdio.h>
#include <stdlib.h> // 该库中定义了rand()函数
int rand = 10; // 错误:与stdlib.h中的rand函数重定义冲突
int main()
{
printf("%d\n", rand);
return 0;
}
编译报错:error C2365: "rand": 重定义;以前的定义是"函数"
2. 命名空间的定义与本质
- 定义语法 :
namespace 命名空间名 { 变量/函数/类型定义 }- 本质:创建一个独立的作用域(域),与全局域相互独立,同名标识符在不同域中可共存
- 特性 :
- 命名空间只能定义在全局域,支持嵌套定义
- 多文件中同名的命名空间会被合并为一个(不会冲突)
- 不影响变量生命周期(仅影响访问范围)
cpp
#include <stdio.h>
#include <stdlib.h>
// 定义命名空间a,内部可定义变量、函数、结构体
namespace a
{
int rand = 10; // 与stdlib.h的rand无冲突
int Add(int left, int right)
{
return left + right;
}
struct Node
{
struct Node* next;
int val;
};
}
// 嵌套命名空间
namespace a
{
namespace x
{
int rand = 1;
}
namespace y
{
int rand = 2;
}
}
int main()
{
// 访问命名空间中的成员需用域作用限定符::
printf("%d\n", a::rand); // 10(访问a域的rand)
printf("%d\n", a::x::rand); // 1(访问a::x域的rand)
printf("%d\n", a::Add(1, 1)); // 2(访问a域的Add函数)
struct a::Node p1; // 定义a域的Node类型变量
p1.val = 6;
printf("%d\n", p1.val); // 6
return 0;
}
3. 域作用限定符::与命名空间使用方式
- 域作用限定符 :指定访问的作用域,
::变量名访问全局域 ,命名空间名::变量名访问指定命名空间域- 命名空间 3 种使用方式 :
- 指定命名空间访问(推荐):
s::a- 展开单个成员:
using s::b- 展开所有成员(仅小练习使用):
using namespace s
cpp
int m = 0; // 全局变量m
int main()
{
int m = 1;
printf("%d ", m); // 1(局部m)
printf("%d ", ::m); // 0(全局m,显式加::)
namespace s { int a=0, b=1; }
using s::b; // 展开单个成员
printf("%d\n", b); // 1
return 0;
}
注意:C++ 标准库(如
cout、cin)都放在std命名空间中,日常练习可using namespace std简化代码,实际项目建议用指定命名空间访问(如std::cout)。
三、C++ 输入输出
C++ 提供iostream库,通过cin(输入)和cout(输出)实现 IO 操作,相比 C 语言的scanf/printf更灵活:
- 无需手动指定格式(如
%d、%f),自动识别变量类型(本质是函数重载)- 支持链式操作(
cout << a << b、cin >> a >> b)endl:等价于\n+ 刷新缓冲区- 需通过
std命名空间访问
cpp
#include <iostream>
using namespace std;
int main()
{
// 竞赛级IO优化(大量输入输出场景使用)
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
// 输出
int i = 1;
double j = 3.14;
cout << "i = " << i << endl; // i = 1
cout << "j = " << j << endl; // j = 3.14
// 输入
int a;
char c;
cin >> a >> c; // 链式输入
cout << "a = " << a << ", c = " << c << endl;
return 0;
}
四、缺省参数(默认参数)
缺省参数是声明或定义函数时为参数指定默认值,调用时若未传实参则使用默认值,否则使用实参。
- 全缺省参数:所有参数都指定默认值
- 半缺省参数:部分参数指定默认值(必须从右往左连续缺省,不能跳跃)
- 声明与定义分离:缺省参数只能在声明中指定
cpp
#include <iostream>
using namespace std;
// 全缺省参数
void Func1(int a = 10, int b = 20, int c = 30)
{
cout << "a = " << a << ", b = " << b << ", c = " << c << endl;
}
// 半缺省参数(从右往左连续缺省)
void Func2(int a, int b = 10, int c = 20)
{
cout << "a = " << a << ", b = " << b << ", c = " << c << endl;
}
int main()
{
Func1(); // a=10, b=20, c=30
Func1(1, 2); // a=1, b=2, c=30
Func2(100); // a=100, b=10, c=20
Func2(100, 200, 300); // a=100, b=200, c=300
return 0;
}
五、函数重载
C++ 支持同一作用域中定义同名函数,只要形参不同(个数、类型、顺序不同),称为函数重载,实现多态行为。
- 重载条件:形参不同(个数、类型、顺序),与返回值无关
- 避免歧义:调用时需让编译器能明确区分同名函数
cpp
#include <iostream>
using namespace std;
// 1. 形参类型不同
int Add(int left, int right) { cout << "int Add\n"; return left+right; }
double Add(double left, double right) { cout << "double Add\n"; return left+right; }
// 2. 形参个数不同
void f() { cout << "f()\n"; }
void f(int a) { cout << "f(int a)\n"; }
// 3. 形参顺序不同
void f(int a, char b) { cout << "f(int, char)\n"; }
void f(char b, int a) { cout << "f(char, int)\n"; }
int main()
{
Add(10, 20); // int版本
Add(10.1, 20.2); // double版本
f(10, 'a'); // int+char版本
f('a', 10); // char+int版本
return 0;
}
错误示例:
cpp
// 错误1:仅返回值不同,不构成重载
// void f() {}
// int f() { return 0; }
// 错误2:调用歧义
void f1() {}
void f1(int a = 10) {}
// f1(); // 报错:无法区分
六、引用
引用是给已存在变量取的别名,编译器不会为引用开辟内存空间,与原变量共用同一块内存。
1. 定义语法与特性
cpp
类型& 引用别名 = 原变量;
- 必须初始化
- 一个变量可以有多个引用
- 一旦绑定某个变量,就不能再绑定其他变量
cpp
#include <iostream>
using namespace std;
// 引用传参(替代指针传参)
void Swap(int& rx, int& ry)
{
int tmp = rx;
rx = ry;
ry = tmp;
}
int main()
{
int a = 0;
int& b = a; // b是a的别名
++b;
cout << a << endl; // 1(a被修改)
int x = 0, y = 1;
Swap(x, y); // 直接传变量,无需取地址
cout << x << " " << y << endl; // 1 0
return 0;
}
2. const 引用(关键用法)
const 引用用于:传参时不拷贝原变量、不修改原变量,或引用临时对象(临时对象具有常性)。
cpp
#include <iostream>
using namespace std;
int main()
{
const int a = 10;
const int& ra = a; // 正确:const引用引用const变量
int b = 20;
const int& rb = b; // 正确:权限缩小(b可修改,rb只读)
// rb++; // 错误:const引用不可修改
// 引用临时对象(必须用const引用)
const int& rc = 30; // 正确
double d = 12.34;
const int& rd = d; // 正确(类型转换产生临时对象)
return 0;
}
3.引用与指针的核心区别
|------|-----------------|------------------|
| 特性 | 引用 | 指针 |
| 内存空间 | 不开辟空间(共用原变量空间) | 开辟空间(存储变量地址) |
| 初始化 | 必须初始化 | 建议初始化(可置空) |
| 指向修改 | 一旦绑定,不能修改指向 | 可随时修改指向 |
| 访问方式 | 直接访问(无需解引用) | 需解引用(*)访问目标变量 |
| 安全性 | 无空引用 / 野引用(更安全) | 存在空指针 / 野指针(风险高) |
七、inline:内联函数(替代 C 语言宏函数)
用inline修饰的函数叫做内联函数,编译时编译器会在调用处展开函数体,避免函数调用的栈帧开销,提高效率。
inline是编译器的建议,长函数、递归函数会被编译器忽略- 替代 C 语言宏函数(宏函数易出错、不便调试)
- 不建议声明和定义分离(会导致链接错误)
cpp
#include <iostream>
using namespace std;
// 内联函数(短函数,频繁调用场景)
inline int Add(int x, int y)
{
return x + y;
}
int main()
{
int ret = Add(1, 2); // 编译时展开为:int ret = 1 + 2;
cout << ret << endl; // 3
return 0;
}
与 C 语言宏函数的对比:C 语言宏函数需注意优先级和分号问题,而 inline 函数更安全。
cpp
// C语言宏函数(需加多层括号,避免优先级问题)
#define ADD(a,b) ((a)+(b))
// C++ inline函数(无需担心优先级,支持调试)
inline int Add(int a, int b) { return a + b; }
八、nullptr
nullptr是 C++11 引入的关键字 ,用于替代 C 语言中的NULL,解决了NULL在类型转换和函数重载中的歧义问题。
1. C 语言中 NULL 的缺陷
C 语言中NULL是宏定义,C++ 中常定义为0,导致函数重载歧义:
cpp
#include <iostream>
using namespace std;
void f(int x) { cout << "f(int x)\n"; }
void f(int* ptr) { cout << "f(int* ptr)\n"; }
int main()
{
f(NULL); // 输出:f(int x)(歧义!本意是调用指针版本)
return 0;
}
2. nullptr 的核心特性与使用
- 类型为
std::nullptr_t,可隐式转换为任意指针类型,但不能转换为整数类型- 精准匹配指针类型的重载函数,无歧义
cpp
#include <iostream>
using namespace std;
void f(int x) { cout << "f(int x)\n"; }
void f(int* ptr) { cout << "f(int* ptr)\n"; }
int main()
{
f(nullptr); // 输出:f(int* ptr)(精准匹配)
// 类型安全:不能转换为整数
// int x = nullptr; // 编译报错
// 可隐式转换为任意指针类型
int* p1 = nullptr;
char* p2 = nullptr;
return 0;
}
3. NULL 与 nullptr 对比
|--------|---------------------|-------------------------|
| 特性 | NULL | nullptr |
| 本质 | 宏定义(可能为 0 或 void*) | 关键字(类型为 std::nullptr_t) |
| 函数重载匹配 | 可能匹配 int 版本(歧义) | 精准匹配指针版本(无歧义) |
| 类型转换 | 可转换为 int 或指针 | 仅可转换为指针(类型安全) |
如果有疑问或补充,欢迎在评论区交流~