🚀 C++ 发展简史与核心语法入门
✅ 零基础也能看懂
✅ 从"为什么要有 C++"讲起,到命名空间、输入输出、缺省参数、函数重载、引用、内联函数、nullptr
✅ 所有代码可运行,附详细注释和对比说明
✅ 帮你避开初学者常见"坑点"
第一章:C++ 是怎么来的?
起源:为了解决 C 的"痛"
- 时间:1979 年
- 人物:Bjarne Stroustrup(本贾尼·斯特劳斯特卢普),贝尔实验室研究员
- 背景:他在开发操作系统和仿真软件时,发现C 语言虽然快,但缺乏抽象能力:
- 代码冗长、难维护
- 无法表达"对象"概念(比如一个"学生"包含姓名、成绩等)
- 复用性差
💡 他想:"能不能在 C 的基础上,加上面向对象的能力?"
诞生:C with Classes → C++
-
1983 年 :Stroustrup 在 C 语言基础上加入 类(class)、封装、继承 等特性,命名为 "C with Classes"
-
同年正式更名为C++
++是 C 中的自增运算符,寓意 "C 的进化版"
标准化之路
| 时间 | 事件 |
|---|---|
| 1989 年 | ANSI/ISO 成立联合委员会,启动 C++ 标准化 |
| 1994 年 | 提出第一个标准草案 |
| 1990s 中期 | STL(标准模板库) 被提议纳入标准(由 Alexander Stepanov 等人在惠普开发) → 极大增强泛型编程能力,但也推迟了标准发布时间 |
| 1998 年 | C++98 正式发布!第一个国际标准 |
📌 后续重要标准:
- C++03(小修)
- C++11(现代 C++ 起点,革命性更新)
- C++14 / C++17 / C++20 / C++23(持续进化)
官方文档推荐
| 链接 | 特点 | 适用场景 |
|---|---|---|
| https://legacy.cplusplus.comeference/ | 非官方,只更新到 C++11,但按头文件组织,简洁易读 | 初学者查基础用法 |
| https://zh.cppreference.com/w/cpp | 官方中文版,覆盖最新标准(C++26) | 深度学习、查新特性 |
| https://en.cppreference.com/w/ | 官方英文版,最权威、最全 | 英文好者首选 |
✅ 建议:初学用 cplusplus.com,进阶用 cppreference
第二章:你的第一个 C++ 程序
兼容 C 的写法(不推荐长期使用)
c
// test.cpp
#include <stdio.h>
int main() {
printf("Hello World\n");
return 0;
}
🔧 编译方式:
- Windows (VS):
.cpp后缀自动调用 C++ 编译器- Linux:用
g++ test.cpp(不是gcc!)
真正的 C++ 写法
c++
#include <iostream> // C++ 输入输出流头文件
using namespace std; // 使用 std 命名空间(后面详解)
int main() {
cout << "Hello World" << endl; // 输出 + 换行 + 刷新缓冲区
return 0;
}
🌟 关键点:
cout:标准输出对象(console output)<<:流插入运算符(不是位移!)endl:换行并刷新缓冲区(比\n更"彻底")
第三章:命名空间(namespace)------ 解决名字冲突
问题:C 语言中的"名字灾难"
c
#include <stdlib.h>
int rand = 10; // ❌ 冲突!stdlib.h 中已有 rand() 函数
int main() {
printf("%d\n", rand); // 编译报错:重定义
}
💥 C 语言所有标识符都在全局作用域,极易冲突!
解决方案:命名空间
c++
#include <iostream>
#include <cstdlib>
namespace mylib {
int rand = 10;
int add(int a, int b) { return a + b; }
struct Node { int val; Node* next; };
}
int main() {
std::cout << std::rand() << std::endl; // C 库的 rand()
std::cout << mylib::rand << std::endl; // 我们的 rand
return 0;
}
✅
mylib::rand和std::rand()互不干扰!
命名空间使用方式(三种)
| 方式 | 示例 | 适用场景 |
|---|---|---|
| 1. 显式指定 | mylib::add(1,2) |
✅ 项目推荐,清晰无歧义 |
| 2. 展开单个成员 | using mylib::add; add(1,2); |
常用函数,避免重复写前缀 |
| 3. 展开整个命名空间 | using namespace mylib; |
❌ 项目中禁用!易引发冲突 ✅ 小练习可用 |
⚠️ 重要规则:
- 命名空间可嵌套
- 多文件中同名 namespace 会自动合并
- C++ 标准库所有内容都在
std命名空间中
第四章:C++ 输入 & 输出(IO 流)
基本用法
c++
#include <iostream>
using namespace std;
int main() {
int a; double b; char c;
cin >> a >> b >> c; // 自动识别类型!
cout << a << " " << b << " " << c << endl;
// 混用 C 风格(不推荐)
scanf("%d", &a);
printf("%d\n", a);
}
✅ 优势:
- 无需格式符 (告别
%d %lf)- 支持自定义类型(后续讲运算符重载)
提高 IO 效率(竞赛必备)
c++
ios_base::sync_with_stdio(false); // 解绑 C/C++ IO
cin.tie(nullptr); // 解除 cin 与 cout 绑定
cout.tie(nullptr);
⏱️ 可提速 3~5 倍!适用于大量输入输出场景
第五章:缺省参数(Default Arguments)
什么是缺省参数?
函数调用时,未传实参则使用默认值。
c++
void print(int a = 0, int b = 10) {
cout << a << ", " << b << endl;
}
int main() {
print(); // 输出: 0, 10
print(5); // 输出: 5, 10
print(1, 2); // 输出: 1, 2
}
规则(必须遵守!)
| 规则 | 说明 |
|---|---|
| 半缺省必须靠右 | f(int a, int b=1, int c=2) ✅ f(int a=1, int b, int c=2) ❌ |
| 声明给,定义不给 | .h 文件中写默认值,.cpp 中不写 |
| 调用从左到右 | 不能跳过中间参数 |
📦 实战示例:栈初始化带默认容量
c++
// Stack.h
void STInit(ST* ps, int n = 4); // 默认容量为4
// test.cpp
ST s;
STInit(&s); // 用默认值
STInit(&s, 1000); // 自定义
第六章:函数重载(Overloading)
什么是重载?
同一作用域内,函数名相同,但参数不同(类型/个数/顺序)。
c++
int add(int a, int b) { return a + b; }
double add(double a, double b) { return a + b; }
void add(int a, char b) {}
void add(char b, int a) {} // 参数顺序不同也算重载!
❌ 返回值不同不算重载!
注意:缺省参数可能引发歧义
c++
void f();
void f(int a = 10);
f(); // ❌ 二义性!编译器不知道调哪个
⚠️ 设计时要避免这种情况!
第七章:引用(Reference)------ 别名机制
引用是什么?
引用 = 已存在变量的别名,不占额外内存!
c++
int a = 10;
int& b = a; // b 是 a 的别名
b = 20; // 等价于 a = 20
cout << a; // 输出 20
🔍 查看地址:
c++cout << &a << " " << &b; // 地址完全相同!
引用三大特性
-
必须初始化
int& r;❌ 编译错误 -
一个变量可有多个引用
int& r1 = a; int& r2 = a;✅ -
一经绑定,不可更改
c++int a = 10, b = 20; int& r = a; r = b; // ❌ 不是让 r 引用 b!而是 a = b;
引用 vs 指针
| 特性 | 引用 | 指针 |
|---|---|---|
| 内存 | 不占空间 | 占 4/8 字节 |
| 初始化 | 必须 | 建议但非必须 |
| 可变性 | 绑定后不可变 | 可随时指向新对象 |
| 使用 | 直接用 r |
需 *p 解引用 |
| 安全性 | 不会空引用 | 易出现空指针/野指针 |
✅ 引用更适合做函数参数和返回值
引用实战:简化数据结构代码
c++
// 传统 C 风格(需二级指针)
void ListPushBack(ListNode** phead, int x);
// C++ 引用风格(更简洁)
void ListPushBack(ListNode*& phead, int x) {
// 直接修改 phead 指向
}
const 引用(非常重要!)
c++
const int a = 10;
// int& r = a; ❌ 权限放大(非常量引用常量)
const int& r = a; ✅
int b = 20;
const int& rb = b; ✅ 权限缩小(安全)
// 临时对象必须用 const 引用
const int& tmp = 10 + 20; ✅
// int& tmp = 10 + 20; ❌
💡 临时对象具有常性,只能被 const 引用绑定
第八章:内联函数(inline)------ 替代宏函数
为什么需要 inline?
C 语言用宏实现"函数":
c++
#define ADD(a, b) ((a) + (b)) // 写错一个括号就出 bug!
❌ 宏不进行类型检查,难以调试
inline 函数
c++
inline int add(int a, int b) {
return a + b;
}
✅ 优势:
类型安全
可调试
编译器自动展开(避免函数调用开销)
⚠️ 注意:仅建议用于短小、频繁调用的函数
不要声明和定义分离(否则链接错误!)
第九章:nullptr ------ 安全的空指针
NULL 的问题
c++
void f(int x) { cout << "int\n"; }
void f(int* p) { cout << "pointer\n"; }
f(NULL); // 输出 "int"!因为 NULL = 0
😱 本想调用指针版本,结果调用了整型版本!
nullptr 的解决方案
c++
f(nullptr); // ✅ 明确调用指针版本
✅
nullptr是指针专属字面量,不会被当作整数!
🎯 总结:C++ 核心语法速查表
| 特性 | 关键点 |
|---|---|
| 命名空间 | namespace 隔离名字,std 是标准库 |
| IO 流 | cin/cout 自动识别类型,endl 刷新缓冲区 |
| 缺省参数 | 靠右连续,声明给默认值 |
| 函数重载 | 参数不同即可,返回值不算 |
| 引用 | 别名,必须初始化,不可变,安全高效 |
| inline | 替代宏,短小函数用,勿分离声明定义 |
| nullptr | 安全空指针,告别 NULL 二义性 |
💡 给初学者的建议:
- 先掌握
namespace和cout/cin,写出规范 C++ 程序;- 用引用代替指针传参,代码更简洁安全;
- 遇到函数参数多?考虑缺省参数;
- 需要同名函数?用重载;
- 永远用
nullptr,不用NULL!
这份笔记助你从 C 到 C++ 平稳过渡,打好现代 C++ 编程基础!
加油,未来的 C++ 工程师!🔥