第一部分:快速上手 ------ 建立 C++ 基本语法与编程范式
目标 :在 2~3 天内掌握能独立编写简单 C++ 程序的核心语法,同时养成「内存与性能」的工程意识。
学习方法:每学一个知识点,立刻动手写几行代码验证,并思考"这个特性在内存里发生了什么"。
1.1 Hello World 与编译流程
第一个程序
cpp
#include <iostream> // 预处理器指令,引入输入输出库
int main() { // 程序入口,操作系统调用此函数
std::cout << "Hello, C++!" << std::endl;
return 0; // 返回 0 表示成功
}
编译与运行
-
保存为
hello.cpp -
编译(使用 g++):
bash
g++ -std=c++17 -o hello hello.cpp-
-std=c++17:指定 C++ 标准(推荐用最新) -
-o hello:指定输出可执行文件名
-
-
运行 :
./hello(Linux/Mac)或hello.exe(Windows)
编译的四个阶段(本质理解)
-
预处理 :处理
#include、宏等,生成.i文件 -
编译 :将预处理后的代码翻译为汇编语言,生成
.s -
汇编 :将汇编转为机器码,生成
.o(目标文件) -
链接 :将多个
.o和库文件合并为最终可执行文件
重点 :每个
.cpp文件独立编译为.o,再链接成可执行文件。头文件(.h)只是声明,不参与编译。
1.2 基础数据类型、变量与运算符
基本内置类型
| 类型 | 字节(常见) | 范围 | 备注 |
|---|---|---|---|
bool |
1 | true/false |
任何非零值转为 true |
char |
1 | -128~127 或 0~255 | 用于字符或小整数 |
int |
4 | -2³¹ ~ 2³¹-1 | 最常用整数 |
float |
4 | 约 1e-38 ~ 1e38 | 单精度浮点 |
double |
8 | 约 1e-308 ~ 1e308 | 双精度浮点(默认) |
void |
无 | 无值 | 用于函数无返回值 |
注意:
char可能有符号也可能无符号,取决于编译器,若需明确用signed char/unsigned char。
变量声明与初始化
cpp
int a = 10; // 拷贝初始化
int b(20); // 直接初始化
int c{30}; // 列表初始化(C++11,更安全,防止窄化转换)
auto d = 3.14; // auto 自动推导为 double
const int e = 100; // 常量,不可修改
auto 的本质
auto 是编译期类型占位符 ,编译器根据初始化表达式推导出实际类型,生成的代码与显式写出类型完全相同,无运行时开销。
运算符
-
算术:
+ - * / %(整数除法取整,%仅整数) -
关系:
== != < > <= >= -
逻辑:
&& || !(短路求值) -
位运算:
& | ^ ~ << >>(操作二进制位) -
赋值:
= += -= *= /= -
自增/自减:
++ --(前缀先变后用,后缀先用后变)
1.3 流程控制与函数
条件语句
cpp
// if-else
int score = 85;
if (score >= 90) {
std::cout << "A";
} else if (score >= 80) {
std::cout << "B";
} else {
std::cout << "C";
}
// switch(仅整型/枚举)
switch (grade) {
case 'A': std::cout << "Excellent"; break;
case 'B': std::cout << "Good"; break;
default: std::cout << "Try harder";
}
循环
cpp
// for 循环(经典)
for (int i = 0; i < 10; ++i) {
std::cout << i << " ";
}
// while 循环
int n = 5;
while (n > 0) {
std::cout << n--;
}
// 基于范围的 for 循环(C++11,配合容器)
int arr[] = {1, 2, 3};
for (int x : arr) {
std::cout << x;
}
函数
cpp
// 函数重载:同名不同参数
int max(int a, int b) {
return (a > b) ? a : b;
}
double max(double a, double b) {
return (a > b) ? a : b;
}
// 默认参数(必须从右向左提供)
void print(int x, int times = 1) {
for (int i = 0; i < times; ++i) std::cout << x << " ";
}
// 内联函数(建议,编译器可忽略)
inline int square(int x) { return x * x; }
内联的本质
inline 只是向编译器发出请求,希望将函数体在调用点展开,以减少函数调用开销。但编译器会根据优化级别和函数大小决定是否内联。现代编译器在 -O2 下会自动内联简短函数。
1.4 类与对象(核心)
C++ 是面向对象语言,类 是用户自定义类型,对象是类的实例。
定义一个简单类
cpp
class Student {
private: // 私有成员,只能被类内部访问
std::string name;
int age;
public: // 公有接口
// 构造函数(无返回值,与类同名)
Student(const std::string& n, int a) : name(n), age(a) {}
// 成员函数
void introduce() const {
std::cout << "I'm " << name << ", " << age << " years old.\n";
}
// 析构函数(对象销毁时自动调用,用于释放资源)
~Student() {
// 一般在这里释放动态分配的内存(但本例没有)
}
};
使用对象
cpp
int main() {
Student s1("Alice", 20); // 栈上构造
s1.introduce();
Student* s2 = new Student("Bob", 22); // 堆上构造(需要手动 delete)
s2->introduce();
delete s2; // 务必释放
return 0;
}
RAII 思想(资源获取即初始化)
本质 :利用对象的生命周期管理资源(内存、文件句柄、锁等)。资源在构造函数中获取,在析构函数中释放。这样当对象离开作用域时,资源自动释放,即使发生异常也能保证释放,这是 C++ 最核心的编程范式。
示例:如果我们使用
std::fstream打开文件,文件在析构时自动关闭,无需手动close()。
1.5 标准库入门
C++ 标准库提供了丰富的数据结构和算法,我们只需掌握最常用的几个。
输入输出(iostream)
cpp
#include <iostream>
using namespace std; // 可省略 std::,但不推荐在头文件或大型项目使用
int main() {
int x;
cout << "Enter a number: ";
cin >> x; // 从键盘读入
cerr << "Error message"; // 标准错误输出
return 0;
}
字符串(std::string)
cpp
#include <string>
std::string s1 = "Hello";
std::string s2 = "World";
std::string s3 = s1 + " " + s2; // 拼接
std::cout << s3.size() << std::endl; // 长度(字符数,非字节)
if (s1 == "Hello") { /* ... */ }
容器 ------ vector(动态数组)
cpp
#include <vector>
std::vector<int> v; // 空 vector
v.push_back(10); // 尾部添加
v.push_back(20);
v[0] = 100; // 下标访问(不检查越界)
for (int i = 0; i < v.size(); ++i) {
std::cout << v[i] << " ";
}
for (int x : v) { // 范围 for
std::cout << x << " ";
}
vector 的简单本质
vector 是动态数组,内部在堆上分配连续内存。当空间不足时,它会重新分配一块更大的内存(通常为当前容量的 2 倍或 1.5 倍),将旧元素复制/移动到新内存,然后释放旧内存。这个过程是自动 的,但需要注意迭代器失效问题(插入/删除可能导致所有迭代器失效)。
容器 ------ map(关联数组)
cpp
#include <map>
std::map<std::string, int> scores;
scores["Alice"] = 95;
scores["Bob"] = 87;
for (auto& pair : scores) { // pair 是 std::pair<const std::string, int>
std::cout << pair.first << ": " << pair.second << std::endl;
}
map底层是红黑树(平衡二叉搜索树),按键有序,插入/查找/删除复杂度 O(log n)。
1.6 内存分区与变量生命周期(重中之重)
为了深刻理解 C++,必须明白程序运行时内存如何划分。
| 区域 | 存储内容 | 生命周期 | 分配/释放 |
|---|---|---|---|
| 栈(Stack) | 局部变量、函数参数、返回地址 | 函数调用时分配,返回时自动释放 | 编译器自动管理,速度极快 |
| 堆(Heap) | 动态分配的对象(new/malloc) |
从 new 到 delete,由程序员控制 |
手动管理,速度慢,可能产生内存碎片 |
| 静态存储区 | 全局变量、static 变量 |
程序启动分配,结束释放 | 编译器自动管理 |
| 常量存储区 | 字符串字面量、const 全局常量 |
程序启动到结束 | 通常只读,可能放在代码段 |
示例分析
cpp
int global = 10; // 静态存储区
void func() {
int local = 20; // 栈
static int static_local = 30; // 静态存储区(只初始化一次)
int* heap_ptr = new int(40); // 堆
delete heap_ptr; // 必须手动释放
}
关键理解 :栈上对象超出作用域自动调用析构函数,堆上对象必须显式
delete才会调用析构。忘记delete会导致内存泄漏。
1.7 练习与巩固
-
练习 1:编写一个程序,从标准输入读取两个整数,输出它们的和、差、积、商(整数除法)。
-
练习 2 :定义一个
Circle类,有半径radius成员,提供计算面积和周长的成员函数。在main中创建对象并测试。 -
练习 3 :使用
vector存储 10 个随机整数(可用rand()),然后遍历输出,并计算总和与平均值。 -
思考题 :如果在函数内创建了一个
vector对象,当函数返回后,这个vector占用的内存是否自动释放?为什么?(提示:RAII)
第一部分总结
你已完成快速入门,掌握了:
-
编译流程和基本语法
-
类与对象的定义和使用,以及 RAII 思想
-
常用标准库组件
-
内存分区和变量生命周期
接下来,我们将进入第二部分,深入探讨 C++ 的内存模型、拷贝控制、移动语义、多态实现等本质原理。
面试问 :"你如何理解 C++ 和 Java 的内存管理差异?"
回答框架:C++ 提供手动和自动(RAII)两种方式,性能可控且无垃圾回收开销;Java 依赖垃圾收集器,编程更简单但可能停顿。C++ 给予程序员更多控制权,也要求更谨慎。