.h 头文件 定义接口
类的声明
函数原型
成员变量(不写具体逻辑)
必要的 #include
.cc 源文件
函数的具体实现
复杂逻辑
私有 helper 函数
* 的使用
int p; 等价于 int p;
定义时 → 表示“指针类型”
int* p;
定义一个指针
使用时 → 表示“解引用”
int a = 10;
int* p = &a;
cout << *p;
*p 表示:
“取出指针指向的值”
& 的使用
取地址符 &
int a = 10;
int* p = &a;
&a = “a 的地址”
include <...> // 去“系统/库”里找
include "..." // 先在“当前项目”里找
using namespace std;
C++ 标准库里的所有内容(比如 cout, string, vector)都被放在一个叫做 👉 std(standard 的缩写)的命名空间里。
- 确保这个头文件只会被编译一次,避免重复定义错误
ifndef PERSON_H
define PERSON_H
xxx
endif
pragma once
-
野指针 = 指向“已经被释放或不存在的内存”的指针
-
什么时候用-> 什么时候用.
有指针用 ->,没指针用 .
很好,你选了**路线1:指针 & 内存** 👍
这是 C++ 最核心、也是最容易拉开差距的一部分。我帮你讲到**真正理解为止**。
---
# 🧠 一、先建立一个最重要的认知
👉 C++ 里有两种内存:
```text
栈(stack) 👉 自动管理
堆(heap) 👉 手动管理
📦 二、栈 vs 堆(必须搞懂)
✅ 栈(stack)
int a = 10;
👉 特点:
- 自动分配
- 自动释放
- 速度快
👉 生命周期:
出了作用域 → 自动销毁
✅ 堆(heap)
int* p = new int(10);
👉 特点:
- 手动申请
- 手动释放 ❗
- 更灵活
⚠️ 三、最重要的一句话
new 出来的东西,一定要 delete
🔍 四、指针到底是什么?
👉 指针 = 存地址的变量
int a = 10;
int* p = &a;
📊 解释:
| 代码 | 含义 |
|---|---|
a |
值 = 10 |
&a |
a 的地址 |
p |
存的是地址 |
*p |
通过地址取值 |
🔥 记住:
p → 地址
*p → 值
🧪 五、最经典例子(必须手敲)
#include <iostream>
using namespace std;
int main() {
int a = 10;
int* p = &a;
cout << "a = " << a << endl;
cout << "*p = " << *p << endl;
*p = 20; // 通过指针修改
cout << "a = " << a << endl;
return 0;
}
▶️ 输出:
a = 10
*p = 10
a = 20
💣 六、指针 + new(真正重点来了)
✅ 创建堆内存
int* p = new int(10);
📊 内存情况:
栈: p(存地址)
堆: 10(真正的数据)
✅ 使用:
cout << *p << endl;
❗ 释放:
delete p;
⚠️ 七、经典错误(必须避免)
❌ 1. 忘记 delete
int* p = new int(10);
// 没有 delete
👉 内存泄漏 ❗
❌ 2. 野指针
int* p;
*p = 10; // ❌ 崩溃
👉 p 没指向任何合法地址
❌ 3. 重复 delete
delete p;
delete p; // ❌ 崩溃
✅ 正确写法:
delete p;
p = nullptr;
🔥 八、数组 + new(非常常用)
✅ 创建数组
int* arr = new int[5];
使用:
arr[0] = 10;
❗ 释放方式不同:
delete[] arr;
👉 注意是 [] ❗
🧠 九、栈 vs 堆总结(面试高频)
| 对比 | 栈 | 堆 |
|---|---|---|
| 分配 | 自动 | 手动 |
| 释放 | 自动 | delete |
| 速度 | 快 | 慢 |
| 大小 | 小 | 大 |
🚀 十、和类结合(真正实战)
👇 例子:
Person* p = new Person("小明");
p->sayHello();
delete p;
🧠 解释:
p → 指针
*p → 对象
p-> → 调用对象成员
🔥 十一、-> 是什么?
p->sayHello();
等价于:
(*p).sayHello();
🎯 十二、这一节你必须掌握
如果你能理解下面这些,你已经很强了:
✅ 指针 = 地址
✅ *p = 取值
✅ new / delete
✅ 栈 vs 堆
✅ -> 用法
🚀 下一步(关键升级)
接下来你可以继续进阶:
⭐ 强烈推荐下一步:
👉 智能指针(smart pointer)
因为:
手动 delete 很容易出错 ❗
现代 C++ 基本都用:
unique_ptrshared_ptr
* 智能指针(smart pointer)
🧠 一、为什么需要智能指针?
先看一个经典问题:
int* p = new int(10);
// 忘了 delete
👉 结果:
内存泄漏 ❗
💣 更糟糕的情况:
void func() {
int* p = new int(10);
if (true) return; // 提前返回
delete p; // 永远执行不到
}
👉 这就是 C++ 传统指针的坑
🚀 二、智能指针是什么?
👉 本质一句话:
自动帮你 delete 的指针
📦 三、最常用的 3 种智能指针
| 类型 | 作用 |
|---|---|
unique_ptr |
独占(最常用 ⭐) |
shared_ptr |
多人共享 |
weak_ptr |
配合 shared_ptr(防循环引用) |
🥇 四、先学最重要的:unique_ptr
✅ 创建方式
#include <memory>
std::unique_ptr<int> p = std::make_unique<int>(10);
🧠 特点:
一个资源只能有一个主人
❌ 不允许这样:
auto p2 = p; // ❌ 报错
👉 因为不能“复制所有权”
✅ 正确转移(move)
auto p2 = std::move(p);
👉 结果:
p → 空
p2 → 拥有资源
🧪 示例
#include <iostream>
#include <memory>
using namespace std;
int main() {
unique_ptr<int> p = make_unique<int>(10);
cout << *p << endl;
return 0; // 自动释放 ❗
}
🔥 五、和类一起用(实战)
unique_ptr<Person> p = make_unique<Person>("小明");
p->sayHello();
👉 不需要:
delete p; // ❌ 不用写
🥈 六、shared_ptr(共享所有权)
✅ 创建
shared_ptr<int> p1 = make_shared<int>(10);
✅ 可以复制
shared_ptr<int> p2 = p1;
🧠 内部机制:
👉 引用计数(reference count)
p1 → 1
p2 → 2
👉 当计数变成 0:
自动 delete
🧪 示例
shared_ptr<int> p1 = make_shared<int>(10);
{
shared_ptr<int> p2 = p1;
} // p2 销毁,计数减1
// p1 还在,不会释放
💣 七、shared_ptr 的坑(非常重要)
❌ 循环引用
class A;
class B;
class A {
public:
shared_ptr<B> b;
};
class B {
public:
shared_ptr<A> a;
};
👉 结果:
A 引用 B
B 引用 A
→ 永远不会释放 ❗
🛠️ 八、解决:weak_ptr
✅ 用法:
weak_ptr<A> a;
👉 特点:
- 不增加引用计数
- 不影响释放
🧠 九、总结(非常重要)
🥇 unique_ptr
独占资源(推荐默认使用)
🥈 shared_ptr
多个地方共享
🥉 weak_ptr
解决 shared_ptr 循环引用
🚀 十、现代 C++ 最重要的一句话
尽量不用裸指针(new/delete)
优先用智能指针
🎯 十一、你现在的水平
你已经掌握:
✅ 指针本质 ✅ new / delete ✅ 栈 vs 堆 ✅ unique_ptr / shared_ptr
👉 已经进入 中级 C++ 程序员门槛
我帮你讲到真正理解 + 能用在项目里👇
🧠 一、引用(reference)到底是什么?
📌 定义
int a = 10;
int& ref = a;
👉 ref 就是 a 的别名(alias)
🔥 关键理解:
ref 不是新变量
ref 就是 a 本人
🧪 例子
int a = 10;
int& ref = a;
ref = 20;
cout << a << endl; // 20
👉 改 ref 就是改 a
⚠️ 二、引用的规则(必须记住)
❗ 1. 必须初始化
int& ref; // ❌ 错误
❗ 2. 不能改绑定对象
int a = 10;
int b = 20;
int& ref = a;
ref = b; // ❌ 不是改绑定,是赋值
👉 实际是:
把 b 的值赋给 a
🆚 三、引用 vs 指针(超重要)
| 对比 | 引用 & |
指针 * |
|---|---|---|
| 是否必须初始化 | ✅ 必须 | ❌ 不必须 |
| 是否可改指向 | ❌ 不行 | ✅ 可以 |
| 是否可为空 | ❌ 不行 | ✅ 可以 |
| 语法 | 更简单 | 更灵活 |
🧪 对比代码
引用:
int a = 10;
int& ref = a;
指针:
int a = 10;
int* p = &a;
🚀 四、引用最重要用途:函数参数
❌ 普通传值(不会改变外部)
void func(int x) {
x = 100;
}
✅ 用引用(可以改变外部)
void func(int& x) {
x = 100;
}
🧪 示例
int a = 10;
func(a);
cout << a; // 100
🔥 五、const(超级重要,面试必问)
📌 const 是什么?
限制“不能修改”
🧠 六、const + 变量
const int a = 10;
a = 20; // ❌ 报错
🚀 七、const + 引用(重点!)
✅ 写法:
const int& ref = a;
🧠 意义:
通过 ref 不能修改 a
🧪 示例:
int a = 10;
const int& ref = a;
ref = 20; // ❌ 报错
🔥 八、为什么要用 const 引用?
👉 这是C++ 最重要的优化手段之一
❌ 传值(会复制)
void print(string s);
👉 每次调用都会复制一份 string ❗(慢)
✅ const 引用(推荐)
void print(const string& s);
👉 优点:
- 不复制(快)
- 不会被修改(安全)
🧠 九、const + 指针(最容易混乱)
🔥 记这个口诀:
const 修饰谁,谁不能改
✅ 情况1:
const int* p;
👉 意思:
*p 不能改
p 可以改
✅ 情况2:
int* const p = &a;
👉 意思:
p 不能改
*p 可以改
✅ 情况3:
const int* const p = &a;
👉 意思:
都不能改
🧠 十、结合类(非常重要)
👇 成员函数加 const
class Person {
public:
void show() const;
};
👉 意思:
这个函数不会修改成员变量
❗ 如果你写:
void show() const {
name = "xxx"; // ❌ 报错
}
🔥 十一、为什么这很重要?
因为:
const Person p;
👉 只能调用:
p.show(); // show 必须是 const
🎯 十二、总结(一定要内化)
📌 引用
本质 = 别名
📌 const
限制修改
📌 最常用组合(必须记住)
const string& s
👉 含义:
不复制 + 不修改
🚀 十三、你现在的水平
你已经掌握:
✅ 指针 ✅ 智能指针 ✅ 引用 ✅ const
👉 这已经是C++ 中级核心基础完成 🎯
🚀 下一步(真正拉开差距)
我建议你走这条:
⭐ 学 STL(强烈推荐)
因为真实开发几乎全用:
vectormapstringalgorithm
或者你也可以:
👉 学 多态(虚函数 virtual)(面试杀手级)
👉 你下一步说:
- “学 STL(推荐)”
- “学多态”
我带你进入真正“写项目”的阶段 👨💻
你可以把它理解为:
别人已经帮你写好的高质量数据结构 + 算法
🚀 我们从最重要的 3 个开始:
vector(最常用 ⭐⭐⭐⭐⭐)string(你已经在用)map(键值对,非常重要)
🥇 一、vector(动态数组,必须精通)
📌 什么是 vector?
👉 类似数组,但可以自动扩容
✅ 使用
#include <vector>
🧪 基本用法
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v;
v.push_back(10);
v.push_back(20);
v.push_back(30);
cout << v[0] << endl; // 10
}
🧠 常用操作
✅ 添加元素
v.push_back(100);
✅ 获取大小
v.size();
✅ 遍历(推荐写法 ⭐)
for (int x : v) {
cout << x << endl;
}
👉 叫:范围 for(range-based for)
✅ 删除最后一个
v.pop_back();
✅ 清空
v.clear();
⚠️ 注意
v[100]; // ❌ 越界不会报错(危险)
👉 更安全:
v.at(100); // 会抛异常
🥈 二、string(比 C 风格强太多)
📌 使用
#include <string>
🧪 示例
string s = "hello";
s += " world";
cout << s << endl;
🧠 常用操作
✅ 拼接
s = s + "!!!";
✅ 长度
s.size();
✅ 访问字符
s[0];
✅ 查找
s.find("lo");
👉 返回位置(找不到返回 string::npos)
🥉 三、map(键值对,超级重要)
📌 什么是 map?
key → value
类似:
名字 → 分数
✅ 使用
#include <map>
🧪 示例
#include <iostream>
#include <map>
using namespace std;
int main() {
map<string, int> m;
m["Alice"] = 90;
m["Bob"] = 85;
cout << m["Alice"] << endl;
}
🧠 常用操作
✅ 插入
m["Tom"] = 100;
✅ 判断是否存在
if (m.find("Tom") != m.end()) {
cout << "存在";
}
✅ 遍历
for (auto& p : m) {
cout << p.first << " " << p.second << endl;
}
🔥 四、auto(必须掌握)
📌 自动推导类型
auto x = 10; // int
auto s = "hello"; // const char*
👉 在 STL 中超级常用:
for (auto& x : v)
🧠 五、STL 思想(很重要)
👉 STL = 泛型(template)
vector<int>
vector<string>
vector<Person>
👉 同一套代码,适配不同类型
🚀 六、实战小例子(你可以跑)
🎯 统计单词次数
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main() {
map<string, int> count;
count["apple"]++;
count["apple"]++;
count["banana"]++;
for (auto& p : count) {
cout << p.first << ": " << p.second << endl;
}
return 0;
}
🎯 七、你现在必须掌握的
⭐ vector
- push_back
- size
- 遍历
⭐ map
- 插入
- 查找
- 遍历
⭐ string
- 拼接
- 查找
🚀 八、下一步(关键提升)
现在你已经进入:
能写“真实程序”的阶段
接下来两个方向非常关键:
⭐ 方向1(强烈推荐):
👉 algorithm(算法库)
- sort(排序)
- find(查找)
- for_each
⭐ 方向2:
👉 迭代器(iterator)
👉 STL 的底层核心(面试常问)
👉 你下一步说:
- “学 algorithm(推荐)”
- “学 迭代器”
我带你进入 C++ 真正高手区 👨💻
```