C++凡人修仙法典 - 宗门版
序章:宗门真谕·万界代码修真录
当指尖在键盘叩击出第一串字符时,灵气如星河倒卷般在屏幕上奔涌------此乃**「C++万界宗门」洞开山门的玄妙时刻。本宗统御「数据帝国」「算法王朝」「系统天庭」等万千代码疆域,以 「十七重登仙大典」为修行圭臬,引凡俗修士自红尘叩入「代码洞天」,历经语法规炉、内存雷劫、泛型化虹,终成统御万界数据洪流的「编程太上长老」**。
何以致宗门喻编程? 盖因修仙与编码同遵**「阶阶为梯,厚积薄爆」的天道至理,然宗门之道更显恢弘**:
- 筑基篇:凡俗修士自吐纳炼气始,夯实命轮根基,方可结金丹、育元婴;正如本宗弟子,必先参透变量常量、函数调用之基础心法,再入类与对象、指针引用之真传堂奥------此乃**「万界共通之根基」**;
- 神通篇:仙家有御剑凌空、炼丹聚灵之异术,各安其用;本宗更藏**「STL容器、模板元编程」等镇派神通**,如「玄天剑匣」收纳万千兵刃,「混沌熔炉」化生万象数据,需随境修炼,应机而发;
- 天劫篇:修士最惧心魔噬道、雷火焚身,稍懈则坠魔道;本宗弟子亦需慎防**「内存泄漏、野指针游弋」等九幽凶险**,方能稳渡化神雷劫------此乃**「宗门试炼之必过关卡」**;
- 大道篇 :修真者求与天地同频,悟天道玄机;本宗大成者更追寻**「代码至简」,以精妙逻辑铸就万象系统,最终参透「编译玄机」「并发天道」等无上真谛------此乃「统御万界之终极追求」**。
本书独创**「宗门十七境登仙谱」**,自「炼气期·语基初悟」至「混沌境·本源归真」,每重境界皆对应宗门秘境中的关键法门:
- 凡阶三境(炼气·筑基·金丹):夯实语法根基,通晓数据存取与流程驭法------此为**「外门弟子必修之基础」**;
- 灵阶六境(元婴·化神·炼虚·合体·大乘·渡劫):深研面向对象真意与内存玄机,贯通代码洞天的「周天脉络」------此乃**「内门真传弟子突破之关隘」**;
- 仙阶八境(真仙·天仙·金仙·大罗·准圣·圣人·鸿蒙·混沌):驾驭多线程并流、泛型化生等「通玄神通」,直指语言本源真谛------此为**「宗门长老方能参悟的无上法门」**。
本宗修行,更重**「三重真境」**:
- 见本我:明自身之不足,于「藏经阁」中寻对应典籍,针对性淬炼薄弱环节;
- 见天地:晓C++于「系统天庭」「性能神域」中的无上妙用,参透「底层操控」「极致优化」之玄机;
- 见众生:读懂「万界代码」中的设计思路,与四海道友(全球开发者)隔空论道,共参宗门大道。
今日起,你的**「C++宗门修行」正式启程------ 于编译钟声中听 「雷劫轰鸣」,在调试行间观 「星象流转」,终成那统御「数据帝国」「算法王朝」的「编程太上长老」**!
(宗门谕:十七重境界,层层递进,唯有历经「炼气筑基」之苦、「内存雷劫」之险,方能于「混沌境」中窥见代码与天道的共鸣------此乃万界共尊的修行正道!)
境界1:炼气期 ------ 筑稳C++根基
境界总览
炼气期是修仙之路的起点,如同初入C++世界的修士,需先掌握"灵气吐纳"的基础法门。此阶段的核心是理解数据的形态 (数据类型)、灵气的容器 (变量)和灵气的运转规则(运算符)。唯有筑牢此根基,方能在后续修行中应对更复杂的"法术"(语法)。
核心目标 | 关键技能 | 修炼成果 |
---|---|---|
识别数据的"灵气形态" | 掌握int /float /char 等基础类型 |
能区分不同场景下该用哪种数据类型 |
学会"灵气收纳" | 变量的声明、初始化与使用 | 能通过变量存储和操作数据 |
掌握"灵气转化法诀" | 算术/复合赋值/自增自减运算符 | 能对数据进行计算与更新 |
第一式:识灵气形态 ------ 数据类型
代码世界的"灵气"以不同形态存在:有的厚重如磐石(整数),有的灵动如流水(小数),有的细微如符文(字符)。不同形态的灵气需用不同"容器"承载,这便是数据类型的作用。
1.1 基础数据类型(必学)
类型 | 形态描述 | 适用场景 | 示例 |
---|---|---|---|
int |
整数灵气(无小数) | 年龄、数量、序号等 | int score = 95; (分数) |
float |
单精度浮点灵气(带小数) | 身高、重量等(精度要求低) | float weight = 62.5f; (体重,f 表单精度) |
double |
双精度浮点灵气(更高精度) | 圆周率、坐标等(精度要求高) | double pi = 3.1415926; |
char |
字符灵气(单个符号) | 性别、等级标记等 | char grade = 'A'; (等级,单引号包裹) |
1.2 扩展数据类型(进阶)
当基础灵气不够用时,可调用"扩容法诀":
unsigned int
:仅存非负整数(如unsigned int age = 20;
,不能存负数);long long
:存储极大整数(如long long population = 1400000000;
,人口数);
1.3 实战:查询"容器容量"(sizeof
)
每种数据类型的"容器"大小(占用内存)不同,可用sizeof
查询:
cpp
#include <iostream>
using namespace std;
int main() {
// 查询各类型的"容器容量"(单位:字节)
cout << "int容器:" << sizeof(int) << "字节(约存-21亿~21亿)" << endl;
cout << "float容器:" << sizeof(float) << "字节(约6位有效数字)" << endl;
cout << "double容器:" << sizeof(double) << "字节(约15位有效数字)" << endl;
cout << "char容器:" << sizeof(char) << "字节(存单个字符或-128~127)" << endl;
// 示例:用long long存大数字
long long distance = 9460730472580800LL; // 光年(约9.46万亿公里,LL表long long)
cout << "一光年距离:" << distance << "米" << endl;
return 0;
}
📌 注意 :int
在不同系统中可能是2或4字节,但现代环境多为4字节;long long
几乎都为8字节,适合存大整数。
第二式:纳灵气入器 ------ 变量
变量是存储灵气的"容器",需先"锻造容器"(声明),再"注入灵气"(赋值)。
2.1 变量的声明与初始化
cpp
#include <iostream>
using namespace std;
int main() {
// 方式1:先声明,后赋值(分步锻造与注灵)
int student_count; // 声明:锻造一个叫student_count的int容器
student_count = 45; // 赋值:注入45点灵气
// 方式2:声明时直接初始化(一步到位)
float avg_score = 89.5f; // 锻造avg_score容器,同时注入89.5f灵气
char first_initial = 'L'; // 姓的首字母
// 输出容器中的灵气
cout << "学生人数:" << student_count << endl;
cout << "平均分:" << avg_score << endl;
cout << "首字母:" << first_initial << endl;
return 0;
}
2.2 变量命名规则(避坑指南)
- 只能用字母、数字、下划线(
_
),且不能以数字开头; - 区分大小写(
score
和Score
是两个不同容器); - 不能用C++关键字(如
int
、float
等,这些是"禁忌名")。
cpp
// 正确命名示例
int math_score; // 数学分数(推荐:小写+下划线,清晰)
int age1; // 带数字(合法)
int _temp; // 下划线开头(合法,但少用,避免与系统变量冲突)
// 错误命名示例(会报错)
// int 2nd_place; // 数字开头(禁忌)
// int float; // 用关键字当名字(禁忌)
// int 数学成绩; // 含中文(不推荐,部分环境支持但易出问题)
第三式:转灵气为用 ------ 运算符
运算符是"灵气转化法诀",能将容器中的灵气加工成新的形态。
3.1 算术运算符(基础转化)
用于加减乘除等基本运算,如同"炼化灵气":
cpp
#include <iostream>
using namespace std;
int main() {
// 场景:计算商品总价
int quantity = 3; // 数量
double price = 19.9; // 单价
double total = quantity * price; // 总价 = 数量 × 单价
// 场景:计算平均分
int scores[] = {85, 92, 78}; // 三门成绩(数组暂作了解,后续会详讲)
double avg = (scores[0] + scores[1] + scores[2]) / 3.0; // 除以3.0确保结果为小数
cout << "3件商品总价:" << total << "元" << endl; // 输出59.7
cout << "平均分:" << avg << endl; // 输出85(85+92+78=255,255/3=85)
return 0;
}
3.2 复合赋值运算符(高效转化)
当需要"更新容器灵气"时(如累加、累减),用复合运算符更简洁:
cpp
#include <iostream>
using namespace std;
int main() {
int energy = 100; // 初始灵力值
// 等价写法:energy = energy + 50;(累加50点灵力)
energy += 50;
cout << "加buff后灵力:" << energy << endl; // 输出150
// 等价写法:energy = energy * 2;(灵力翻倍)
energy *= 2;
cout << "使用大招后灵力:" << energy << endl; // 输出300
// 等价写法:energy = energy - 180;(消耗180点灵力)
energy -= 180;
cout << "释放技能后灵力:" << energy << endl; // 输出120
return 0;
}
3.3 自增/自减运算符(灵气微调)
用于让灵气"±1",常见于计数器(如循环计数,后续筑基期会详讲):
cpp
#include <iostream>
using namespace std;
int main() {
int count = 0; // 初始计数
count++; // 等价于 count = count + 1;(后置自增:先使用再+1)
cout << "第一次计数:" << count << endl; // 输出1
++count; // 等价于 count = count + 1;(前置自增:先+1再使用)
cout << "第二次计数:" << count << endl; // 输出2
// 场景:统计登录次数
int login_times = 3;
cout << "当前登录次数:" << login_times++ << endl; // 先输出3,再+1(结果变为4)
cout << "更新后次数:" << login_times << endl; // 输出4
return 0;
}
📌 注意 :单独使用时count++
和++count
效果相同;但在表达式中(如cout << login_times++
),前置会先+1再输出,后置会先输出再+1。
修炼总结与进阶指引
- 修练目标:炼气期虽浅,却是所有高深法术的根基。唯有熟练操控基础灵气,方能在后续修行中步步为营,稳步迈向筑基期。
- 基石检验:能独立写出"存储学生信息(姓名首字母、年龄、成绩)并计算总分"的程序,便通过炼气期考核;
- 避坑要点 :
- 未初始化的变量会含"混沌灵气"(随机值),如
int x; cout << x;
可能输出任意数; - 不同类型运算时会"自动转化"(如
int + double
结果为double
),需注意精度(如5/2=2
,5.0/2=2.5
);
- 未初始化的变量会含"混沌灵气"(随机值),如
- 下一境界预告:当你能够独立编写并调试一个简单的C++程序,比如"学生信息管理系统",能够正确处理输入、存储数据并进行基本的计算和输出,那么你已经掌握了炼气期的基本功。接下来,你将进入筑基期,学习如何让程序根据不同的条件做出不同的反应,以及如何让程序重复执行某些操作,这将使你的程序具备更强的动态性和功能性。
境界2:筑基期 ------ 掌控灵气流转(流程控制)
筑基期是修士建立道基的关键阶段,如同C++代码从"静态数据"迈向"动态逻辑"的转折点。此阶段需掌握灵气分流之术 (分支语句)和灵气循环之道(循环语句),让程序能根据条件"自主判断"或"重复执行",如同修士能灵活调控灵气走向,应对多变的修仙环境。
核心目标 | 关键技能 | 修炼成果 |
---|---|---|
学会"灵气分流" | if /else 分支、switch 多分支 |
程序能根据条件执行不同操作 |
掌握"灵气循环" | while /do-while /for 循环 |
程序能重复执行指定逻辑(如批量处理数据) |
驾驭"流转节奏" | break /continue 控制循环 |
灵活中断或跳过循环,避免无效消耗 |
第一式:灵气分流术 ------ 分支语句
修仙途中常遇岔路(如"遇妖兽是否战斗""丹药够不够突破"),需依条件抉择。C++中,分支语句便是让程序"做选择"的法门。
1.1 单岔路抉择:if
语句
心法:若条件成立,则执行特定操作(如"灵力≥100则释放技能")。
cpp
#include <iostream>
using namespace std;
int main() {
int magic = 80; // 当前灵力值
// 条件:若灵力≥100,则释放大招
if (magic >= 100) {
cout << "释放大招:天雷术!" << endl;
magic -= 100; // 消耗灵力
}
// 无论条件是否成立,都会执行后续代码
cout << "剩余灵力:" << magic << endl; // 输出80(未释放技能)
return 0;
}
1.2 双岔路抉择:if-else
语句
心法:条件成立走一条路,不成立走另一条(如"灵石够则买丹药,否则去挖矿")。
cpp
#include <iostream>
using namespace std;
int main() {
int灵石 = 50;
int丹药价格 = 100;
if (灵石 >= 丹药价格) {
cout << "购买丹药,突破成功率+30%" << endl;
灵石 -= 丹药价格;
} else {
cout << "灵石不足,去矿洞挖矿1小时" << endl;
灵石 += 30; // 挖矿收益
}
cout << "当前灵石:" << 灵石 << endl; // 输出80(挖矿后)
return 0;
}
1.3 多岔路抉择:if-else if-else
语句
心法:面对多个条件(如"修为等级判断"),逐一检查并执行首个满足的分支。
cpp
#include <iostream>
using namespace std;
int main() {
int level = 15; // 修为等级
cout << "当前修为评级:";
if (level >= 20) {
cout << "筑基后期" << endl;
} else if (level >= 10) { // 若前一个条件不满足,检查此条件
cout << "筑基中期" << endl; // 15≥10,执行此分支
} else if (level >= 1) {
cout << "筑基初期" << endl;
} else {
cout << "未筑基" << endl; // 所有条件不满足时执行
}
return 0;
}
1.4 多门阵:switch
语句
心法 :当条件是"离散值"(如"选择菜单功能")时,用switch
更简洁(类似"按号码选门")。
cpp
#include <iostream>
using namespace std;
int main() {
cout << "修仙辅助系统菜单:" << endl;
cout << "1. 查看修为 2. 修炼 3. 退出" << endl;
int choice;
cin >> choice; // 输入选择(1/2/3)
switch (choice) {
case 1: // 若choice=1
cout << "当前修为:炼气9层" << endl;
break; // 退出switch(必加,否则会继续执行下一个case)
case 2: // 若choice=2
cout << "开始修炼...灵力+5" << endl;
break;
case 3: // 若choice=3
cout << "退出系统" << endl;
break;
default: // 若输入其他值
cout << "无效选择" << endl;
}
return 0;
}
📌 避坑点 :switch
的case
后必须用break
终止,否则会"穿透"到下一个case
(如输入2时,若不加break
会同时执行case2和case3)。
第二式:灵气循环道 ------ 循环语句
修仙需反复修炼(如"每日打坐3次""炼制100颗丹药"),循环语句便是让程序"重复执行"的法门,避免重复编写代码。
2.1 未知次数循环:while
语句
心法:只要条件成立,就反复执行(如"灵力未满则持续吸收")。
cpp
#include <iostream>
using namespace std;
int main() {
int magic = 0;
// 条件:当灵力<100时,持续吸收灵气
while (magic < 100) {
magic += 10; // 每次吸收10点灵力
cout << "吸收灵气...当前灵力:" << magic << endl;
}
cout << "灵力已满,停止吸收" << endl;
return 0;
}
2.2 至少执行一次:do-while
语句
心法:先执行一次操作,再判断是否继续(如"先尝试突破,失败则继续")。
cpp
#include <iostream>
using namespace std;
int main() {
int 成功率 = 30; // 初始突破成功率30%
bool 突破成功 = false;
do {
cout << "尝试突破..." << endl;
// 模拟随机成功率(1-100之间的随机数)
int 随机数 = rand() % 100 + 1;
if (随机数 <= 成功率) {
突破成功 = true;
} else {
成功率 += 5; // 失败后成功率+5%
}
} while (!突破成功); // 若未成功则继续尝试
cout << "突破成功!当前成功率:" << 成功率 << "%" << endl;
return 0;
}
2.3 已知次数循环:for
语句
心法 :明确循环次数(如"炼制10颗丹药"),格式:for(初始化; 条件; 更新)
。
cpp
#include <iostream>
using namespace std;
int main() {
// 炼制10颗丹药(i从0到9,共10次)
for (int i = 0; i < 10; i++) {
cout << "炼制第" << (i+1) << "颗丹药...成功!" << endl;
}
cout << "10颗丹药炼制完成,获得经验+100" << endl;
// 进阶:计算1~100的总和(高斯求和)
int sum = 0;
for (int num = 1; num <= 100; num++) {
sum += num; // 累加num到sum中
}
cout << "1+2+...+100 = " << sum << endl; // 输出5050
return 0;
}
第三式:流转控术 ------ 循环中断与跳过
循环中需灵活调整节奏:遇到紧急情况中断(break
),或跳过某次无效操作(continue
)。
3.1 紧急中断:break
心法:立即终止循环(如"修炼中遇妖兽,停止修炼")。
cpp
#include <iostream>
using namespace std;
int main() {
for (int hour = 1; hour <= 24; hour++) { // 模拟24小时修炼
cout << "修炼第" << hour << "小时..." << endl;
if (hour == 6) { // 第6小时遇妖兽
cout << "遭遇妖兽!终止修炼,准备战斗!" << endl;
break; // 跳出循环,不再继续修炼
}
}
return 0;
}
3.2 跳过当前:continue
心法:跳过本次循环剩余内容,直接进入下一次(如"今日灵气紊乱,跳过本次打坐")。
cpp
#include <iostream>
using namespace std;
int main() {
for (int day = 1; day <= 7; day++) { // 连续7天打坐
if (day == 3) { // 第3天灵气紊乱
cout << "第" << day << "天:灵气紊乱,跳过打坐" << endl;
continue; // 跳过本次循环的后续代码,直接进入day=4
}
cout << "第" << day << "天:打坐完成,灵力+20" << endl;
}
return 0;
}
实战合练:灵根检测系统
综合运用分支与循环,实现一个"灵根检测"程序(输入5种灵根数值,判断资质等级):
cpp
#include <iostream>
using namespace std;
int main() {
int 灵根总数 = 5;
int 总灵力 = 0;
int 最高灵根值 = 0;
// 循环输入5种灵根数值
for (int i = 0; i < 灵根总数; i++) {
int 灵根值;
cout << "请输入第" << (i+1) << "种灵根数值(0-100):";
cin >> 灵根值;
总灵力 += 灵根值;
if (灵根值 > 最高灵根值) {
最高灵根值 = 灵根值; // 更新最高灵根值
}
}
// 分支判断资质等级
if (最高灵根值 >= 90) {
cout << "资质:天灵根(万中无一!)" << endl;
} else if (最高灵根值 >= 70 && 总灵力 >= 300) {
cout << "资质:异灵根(上等资质)" << endl;
} else if (总灵力 >= 200) {
cout << "资质:杂灵根(中等资质)" << endl;
} else {
cout << "资质:凡灵根(需加倍努力)" << endl;
}
return 0;
}
修炼总结与进阶指引
- 修练目标:筑基期的核心是"逻辑流转",唯有熟练掌控分支与循环,才能让程序从"被动执行"变为"主动决策",为后续更复杂的"法术"(语法)打下坚实道基。
- 道基检验:能独立实现"猜数字游戏"(程序随机生成1-100的数,用户多次猜测,程序提示"大了"或"小了",直到猜对),便通过筑基期考核;
- 避坑要点 :
- 循环条件若恒为
true
,会导致"灵气暴走"(死循环),如while(1) { ... }
需配合break
使用; if
后的条件需用括号()
包裹,且单条语句可省略{}
,但多条语句必须加(建议无论多少条都加,避免逻辑错误);
- 循环条件若恒为
- 下一境界预告:在筑基期,你已经学会了如何让程序根据条件执行不同的操作,以及如何让程序重复执行某些操作。接下来,你将进入金丹期,学习如何将数据组织成数组,以及如何将常用的代码片段封装成函数,这将使你的程序更加模块化和高效。
境界3:金丹期 ------ 灵力聚合与法术封装
金丹期是修士将分散灵力凝聚为丹的关键境界,标志着从"零散操控"迈向"系统整合"。如同C++中,数组(灵力聚合) 将零散数据整合为有序集合,函数(法术封装) 将重复逻辑提炼为可复用模块,二者结合让代码从"线性执行"升级为"结构化设计",如同金丹修士一念之间便可调动聚合灵力,施展凝练法术。
核心目标 | 关键技能 | 修炼成果 |
---|---|---|
掌握"灵力聚合" | 数组定义/访问/遍历、多维数组 | 批量处理同类型数据(如存储100名弟子的修为) |
学会"法术封装" | 函数定义/调用/参数/返回值 | 将重复逻辑封装为函数(如"计算灵根平均值"可反复调用) |
领悟"内外交互" | 函数参数传递、作用域 | 实现函数与外部数据的安全交互(如同法术需特定灵气驱动) |
第一式:灵力聚合术 ------ 数组
筑基期操控单一灵力(变量),金丹期需同时掌控多股同源灵力(如5种灵根、10天的修炼数据)。数组便是将相同类型数据"聚合"的容器,如同修士的"灵玉匣",按顺序存放多份灵力。
1.1 灵玉匣初成:数组的定义与初始化
心法:数组需指定"容量"(元素个数)和"类型"(灵力属性),可直接存入初始值。
cpp
#include <iostream>
using namespace std;
int main() {
// 定义:类型 数组名[容量]; (容量必须是常量,如5)
int 火灵根值[5]; // 可存储5个火灵根测试值(未初始化,值随机)
// 初始化:定义时直接赋值(可省略容量,编译器自动计算)
int 水灵根值[] = {85, 92, 78, 65, 90}; // 容量=5,存储5个值
// 部分初始化:未赋值的元素自动为0
int 木灵根值[5] = {70, 80}; // 前2个为70,80,后3个自动为0
cout << "第3个水灵根值:" << 水灵根值[2] << endl; // 输出78(下标从0开始)
return 0;
}
📌 关键注 :数组下标从0
开始(如5个元素的下标为0-4),访问水灵根值[5]
会"触碰禁制"(下标越界),导致程序异常。
1.2 遍历灵玉匣:数组的循环访问
心法 :用for
循环结合下标,依次取出数组中所有元素(如同逐个检查匣中灵玉)。
cpp
#include <iostream>
using namespace std;
int main() {
int 弟子修为[] = {12, 15, 8, 20, 17}; // 5名弟子的筑基等级
int 总人数 = sizeof(弟子修为) / sizeof(弟子修为[0]); // 计算数组长度(总字节/单个元素字节)
cout << "弟子修为列表:" << endl;
// 遍历数组:i从0到总人数-1
for (int i = 0; i < 总人数; i++) {
cout << "第" << (i+1) << "名弟子:" << 弟子修为[i] << "级" << endl;
}
// 进阶:计算平均修为
int 总和 = 0;
for (int i = 0; i < 总人数; i++) {
总和 += 弟子修为[i];
}
cout << "平均修为:" << 总和 / 总人数 << "级" << endl; // 输出14
return 0;
}
1.3 多层灵玉匣:二维数组
心法:数组内嵌套数组,用于存储"表格型数据"(如3个门派各5名弟子的灵力值)。
cpp
#include <iostream>
using namespace std;
int main() {
// 二维数组:3个门派,每个门派5名弟子的灵力值
int 门派灵力[3][5] = {
{100, 120, 90, 150, 80}, // 青云门
{85, 95, 110, 75, 105}, // 逍遥派
{130, 115, 95, 140, 125} // 赤霞宗
};
// 遍历:外层循环门派,内层循环弟子
for (int i = 0; i < 3; i++) { // i=0,1,2(门派索引)
cout << "门派" << (i+1) << "弟子灵力:";
for (int j = 0; j < 5; j++) { // j=0-4(弟子索引)
cout << 门派灵力[i][j] << " ";
}
cout << endl;
}
return 0;
}
第二式:法术封装诀 ------ 函数
修士若每次施法都重新运转灵力(重复写代码),会消耗大量心神。函数如同"法术口诀",将一套逻辑预先记录,需要时只需"念咒"(调用函数)即可触发,大幅提升效率。
2.1 法术立形:函数的定义与调用
心法:函数需明确"法术名称(函数名)""施法条件(参数)""法术效果(返回值)"。
cpp
#include <iostream>
using namespace std;
// 函数定义:返回值类型 函数名(参数列表) { 逻辑 }
// 功能:计算两个灵力值的总和(无参数名也可,如int add(int, int))
int 灵力相加(int a, int b) {
int 结果 = a + b;
return 结果; // 返回计算结果
}
// 无返回值的函数(用void)
void 施展火球术() {
cout << "释放火球术!造成50点伤害" << endl;
// 无return语句(或写return;)
}
int main() {
// 调用函数:函数名(参数)
int 总灵力 = 灵力相加(30, 50); // 传入30和50,接收返回值80
cout << "总灵力:" << 总灵力 << endl;
施展火球术(); // 调用无返回值函数
return 0;
}
2.2 法术传参:参数的灵活传递
心法:参数如同"施法所需的灵气",可传递变量、常量或表达式,让函数适配不同场景。
cpp
#include <iostream>
using namespace std;
// 带默认参数的函数:若调用时不传入,使用默认值
int 修炼收益(int 基础灵力, int 倍率 = 2) {
return 基础灵力 * 倍率;
}
// 引用参数(&):直接修改外部变量(如同直接注入灵力)
void 提升修为(int &修为值, int 增幅) {
修为值 += 增幅;
}
int main() {
// 1. 使用默认参数(倍率=2)
cout << "基础修炼收益:" << 修炼收益(10) << endl; // 10*2=20
// 2. 覆盖默认参数(倍率=3)
cout << "双倍修炼收益:" << 修炼收益(10, 3) << endl; // 10*3=30
// 3. 引用参数示例
int 弟子修为 = 15;
提升修为(弟子修为, 5); // 直接修改弟子修为
cout << "提升后修为:" << 弟子修为 << endl; // 输出20
return 0;
}
⚠️ 避坑点 :默认参数必须放在参数列表末尾(如int func(int a, int b=2)
正确,int func(int a=1, int b)
错误)。
2.3 法术藏锋:函数声明与分离编译
心法:复杂功法需先"预告"(声明),再"详解"(定义),如同先告知有此法术,再传授具体口诀。
cpp
#include <iostream>
using namespace std;
// 函数声明(原型):只需返回值、函数名、参数类型,无需实现
int 灵根平均值(int 灵根数组[], int 数量);
int main() {
int 金灵根[] = {90, 85, 78, 92};
int 数量 = 4;
cout << "金灵根平均值:" << 灵根平均值(金灵根, 数量) << endl; // 调用前已声明,可正常使用
return 0;
}
// 函数定义(实现):放在main之后
int 灵根平均值(int 灵根数组[], int 数量) {
int 总和 = 0;
for (int i = 0; i < 数量; i++) {
总和 += 灵根数组[i];
}
return 总和 / 数量; // 计算并返回平均值
}
第三式:金丹融合技 ------ 数组与函数结合
金丹期的核心是"聚合与封装的协同":用数组存储批量数据,用函数处理数据逻辑,二者结合可实现复杂功能(如同用聚合灵力驱动高阶法术)。
实战案例:门派灵根检测系统
cpp
#include <iostream>
using namespace std;
// 函数1:计算灵根最大值(找出最优灵根)
int 最大灵根值(int 灵根数组[], int 数量) {
int 最大值 = 灵根数组[0];
for (int i = 1; i < 数量; i++) {
if (灵根数组[i] > 最大值) {
最大值 = 灵根数组[i];
}
}
return 最大值;
}
// 函数2:判断灵根资质(结合数组和分支)
string 判定资质(int 灵根数组[], int 数量) {
int 最高值 = 最大灵根值(灵根数组, 数量); // 调用函数1
if (最高值 >= 90) {
return "天灵根";
} else if (最高值 >= 70) {
return "异灵根";
} else {
return "杂灵根";
}
}
int main() {
int 弟子灵根[] = {88, 65, 92, 70, 55}; // 5种灵根值
int 灵根数量 = sizeof(弟子灵根) / sizeof(弟子灵根[0]);
cout << "弟子灵根值:";
for (int i = 0; i < 灵根数量; i++) {
cout << 弟子灵根[i] << " ";
}
cout << endl;
cout << "最高灵根值:" << 最大灵根值(弟子灵根, 灵根数量) << endl; // 输出92
cout << "资质评定:" << 判定资质(弟子灵根, 灵根数量) << endl; // 输出"天灵根"
return 0;
}
修炼总结与进阶指引
- 修练目标:金丹期的核心是"结构化思维":用数组聚合数据,用函数封装逻辑,二者结合让代码从"混乱堆砌"变为"有序模块"。唯有熟练此道,方能为后续"元婴化神"(面向对象编程)奠定不朽根基。
- 金丹检验:能独立实现"修仙门派排行榜"(用二维数组存储多个门派的名称和战力值,用函数排序并输出前三名),则金丹已成;
- 避坑要点 :
- 数组作为函数参数时,需额外传递长度(函数无法直接获取数组容量);
- 函数内部定义的变量(局部变量)仅在函数内有效(如同法术效果仅限施法时),外部无法访问;
- 下一境界预告:金丹期让你学会了如何将数据聚合在一起,并将常用的逻辑封装成函数。接下来,你将进入元婴期,学习如何使用指针直接操作内存,以及如何使用结构体来组织和管理复杂的数据结构,这将使你的程序能够处理更复杂的数据和逻辑。
境界4:元婴期 ------ 指针与引用
元婴期是修士脱胎换骨的关键境界:筑基期操控变量(灵力点),金丹期聚合数组(灵力束)、封装函数(法术模块),而元婴期则能让"神魂(程序逻辑)"直接驾驭"法器(内存地址)",并凝聚"元婴形体(复合数据结构)"。如同修士元婴出窍可远程御物,C++中的指针 能直接访问内存地址;如同元婴凝聚灵根、修为等多重属性,结构体可整合不同类型数据,二者结合让代码从"结构化"迈向"灵活化",为后续"化神期(面向对象)"奠定根基。
核心目标 | 关键技能 | 修炼成果 |
---|---|---|
神魂御器 | 指针定义/解引用、指针与数组、指针参数 | 直接操控内存数据(如通过地址修改数组元素) |
形体凝塑 | 结构体定义/成员访问、结构体数组 | 整合多类型数据(如存储修士"姓名+灵根+修为") |
神魂融体 | 结构体与指针结合、const指针 | 高效处理复杂数据(如用指针遍历结构体数组) |
第一式:神魂出窍------指针(Pointer)
金丹期操控数组需通过下标(如同隔着法器外壳操作),而元婴期的指针能直接"触碰内存本源"------通过存储变量的地址,实现对数据的直接读写。如同修士元婴出窍,可无视空间距离操控法器,指针让程序能突破"变量名"的限制,直接访问内存中的数据。
1.1 神魂认主:指针的定义与解引用
心法 :指针是"存储地址的变量",需明确指向的数据类型(如同元婴需匹配法器属性)。通过&
取变量地址,通过*
解引用(访问地址中的数据)。
cpp
#include <iostream>
using namespace std;
int main() {
int 灵力 = 100; // 普通变量:存储灵力值
int* 神魂; // 定义int类型指针(*表示这是指针),可指向int变量的地址
神魂 = &灵力; // 指针存储变量"灵力"的地址(&是取地址符)
cout << "灵力的地址:" << &灵力 << endl; // 输出类似0x61fe14(内存地址)
cout << "神魂指向的地址:" << 神魂 << endl; // 与上面地址相同
// 解引用:通过*访问指针指向的实际数据
cout << "神魂感知到的灵力值:" << *神魂 << endl; // 输出100
// 通过指针修改数据(直接操控内存)
*神魂 = 150; // 等价于 灵力 = 150
cout << "修改后灵力值:" << 灵力 << endl; // 输出150
return 0;
}
📌 关键注:
- 指针类型必须与指向的变量类型一致(如
int*
不能直接指向double
变量,如同火属性元婴不能驾驭水灵法器); - 未初始化的指针(野指针)会随机指向内存,操作时可能导致程序崩溃(如同元婴误入禁地),需避免。
1.2 御器巡行:指针与数组的联动
数组名本质是"指向首元素的指针",因此指针可替代下标遍历数组,且更灵活(如同元婴可自由穿梭于法器内部)。
cpp
#include <iostream>
using namespace std;
int main() {
int 弟子修为[] = {12, 15, 8, 20, 17};
int 人数 = sizeof(弟子修为) / sizeof(弟子修为[0]);
// 数组名 = 首元素地址(弟子修为 等价于 &弟子修为[0])
int* 巡查神魂 = 弟子修为; // 指针指向数组首元素
// 用指针遍历数组(指针自增=移动到下一个元素地址)
cout << "弟子修为列表(指针遍历):" << endl;
for (int i = 0; i < 人数; i++) {
cout << "第" << (i+1) << "名:" << *巡查神魂 << "级" << endl;
巡查神魂++; // 指针向后移动一位(指向后一个元素)
}
// 指针算术:直接定位到第n个元素(下标i = 指针位置 - 首地址)
int* 第三弟子 = 弟子修为 + 2; // 指向第3个元素(下标2)
cout << "第3名弟子修为:" << *第三弟子 << endl; // 输出8
return 0;
}
⚠️ 避坑点:
- 指针自增/自减的步长由类型决定(如
int*
每次移动4字节,double*
移动8字节),无需手动计算; - 不可让指针超出数组范围(如同元婴不可脱离法器边界),否则会访问无效内存。
1.3 神魂传讯:指针作为函数参数
指针作为参数时,函数可直接修改外部变量(通过地址传递,而非值传递),如同元婴远程操控外界灵力,效率远高于"复制灵力再传回"(值传递)。
cpp
#include <iostream>
using namespace std;
// 函数:通过指针修改修为(传地址,直接操作原变量)
void 灌注灵力(int* 修为指针, int 增幅) {
*修为指针 += 增幅; // 解引用后修改原变量
}
// 对比:值传递(仅修改副本,原变量不变)
void 无效灌注(int 修为值, int 增幅) {
修为值 += 增幅;
}
int main() {
int 弟子修为 = 15;
无效灌注(弟子修为, 5);
cout << "无效灌注后修为:" << 弟子修为 << endl; // 仍为15(仅改副本)
灌注灵力(&弟子修为, 5); // 传入地址
cout << "有效灌注后修为:" << 弟子修为 << endl; // 变为20(改原变量)
return 0;
}
实战场景:交换两个变量的值(必须用指针或引用,值传递无法实现)
cpp
// 用指针交换a和b的值
void 交换灵力(int* a, int* b) {
int 临时 = *a;
*a = *b;
*b = 临时;
}
int main() {
int 灵力A = 30, 灵力B = 50;
交换灵力(&灵力A, &灵力B);
cout << "交换后:" << 灵力A << " " << 灵力B << endl; // 输出50 30
return 0;
}
第二式:元婴凝形------结构体(Struct)
金丹期的数组只能存储同类型数据(如同单一灵根修士),而结构体可整合不同类型数据(如"姓名(字符串)+ 灵根(数组)+ 修为(int)"),如同元婴凝聚完整形体,包含多种属性。
2.1 形体初成:结构体的定义与使用
心法:结构体是"自定义复合类型",需先定义"形体模板"(成员变量),再创建"具体元婴"(结构体变量)。
cpp
#include <iostream>
#include <string> // 需包含string头文件
using namespace std;
// 定义结构体(元婴模板):包含修士的多类属性
struct 修士 {
string 姓名; // 字符串类型
int 修为; // int类型
int 灵根[5]; // 数组类型(5种灵根值)
bool 是否筑基; // 布尔类型
};
int main() {
// 创建结构体变量(具体元婴)
修士 韩立;
// 访问成员:用.运算符
韩立.姓名 = "韩立";
韩立.修为 = 88;
韩立.灵根 = {90, 85, 0, 0, 0}; // 金、木灵根突出
韩立.是否筑基 = true;
// 输出信息
cout << "修士信息:" << endl;
cout << "姓名:" << 韩立.姓名 << endl;
cout << "修为:" << 韩立.修为 << "级" << endl;
cout << "金灵根值:" << 韩立.灵根[0] << endl;
cout << "是否筑基:" << (韩立.是否筑基 ? "是" : "否") << endl;
return 0;
}
2.2 众婴同列:结构体数组
当需要管理多名修士时,可将结构体变量存入数组(如同宗门内的元婴队列),结合循环批量处理。
cpp
#include <iostream>
#include <string>
using namespace std;
struct 修士 {
string 姓名;
int 修为;
};
int main() {
// 结构体数组:3名修士
修士 宗门弟子[3] = {
{"韩立", 88},
{"南宫婉", 95},
{"厉飞雨", 76}
};
// 遍历结构体数组,找出修为最高的弟子
修士 首席弟子 = 宗门弟子[0];
for (int i = 1; i < 3; i++) {
if (宗门弟子[i].修为 > 首席弟子.修为) {
首席弟子 = 宗门弟子[i];
}
}
cout << "宗门首席弟子:" << 首席弟子.姓名
<< ",修为:" << 首席弟子.修为 << "级" << endl; // 输出南宫婉
return 0;
}
2.3 结构体与函数:批量淬炼元婴
将结构体作为函数参数,可批量处理复杂数据(如同宗门秘法统一淬炼元婴)。
cpp
#include <iostream>
#include <string>
using namespace std;
struct 修士 {
string 姓名;
int 修为;
};
// 函数:提升修士修为(传结构体引用,避免复制大对象)
void 修炼(修士& 目标, int 增幅) { // &表示引用,等价于直接操作原对象
目标.修为 += 增幅;
cout << 目标.姓名 << "修炼后修为:" << 目标.修为 << endl;
}
// 函数:判断是否达到金丹期(返回布尔值)
bool 可结丹(修士 目标) {
return 目标.修为 >= 100;
}
int main() {
修士 韩立 = {"韩立", 88};
修炼(韩立, 15); // 修为变为103
if (可结丹(Hanli)) {
cout << 韩立.姓名 << "可结丹!" << endl; // 输出可结丹
}
return 0;
}
第三式:神魂融体------指针与结构体结合
元婴期的至高境界是"神魂(指针)"与"元婴形体(结构体)"深度融合:用指针指向结构体,既能直接操控结构体成员,又能灵活传递复杂数据(无需复制整个结构体),效率远超值传递。
实战案例:宗门修士管理系统
cpp
#include <iostream>
#include <string>
using namespace std;
struct 修士 {
string 姓名;
int 修为;
string 门派;
};
// 函数:用指针修改修士门派(传结构体指针)
void 转投门派(修士* 目标, string 新门派) {
(*目标).门派 = 新门派; // 解引用后访问成员(等价于 目标->门派)
// 简化写法:目标->门派 = 新门派; (->是指针访问成员的专用运算符)
}
// 函数:用指针遍历结构体数组,打印所有修士信息
void 展示宗门(修士* 弟子数组, int 人数) {
for (int i = 0; i < 人数; i++) {
cout << "姓名:" << (弟子数组 + i)->姓名
<< ",修为:" << (弟子数组 + i)->修为
<< ",门派:" << (弟子数组 + i)->门派 << endl;
}
}
int main() {
修士 宗门[] = {
{"韩立", 103, "黄枫谷"},
{"南宫婉", 120, "掩月宗"},
{"厉飞雨", 90, "黄枫谷"}
};
int 人数 = sizeof(宗门) / sizeof(宗门[0]);
// 韩立转投青阳城
转投门派(&宗门[0], "青阳城");
// 展示所有修士信息
cout << "宗门成员列表:" << endl;
展示宗门(宗门, 人数);
/* 输出:
姓名:韩立,修为:103,门派:青阳城
姓名:南宫婉,修为:120,门派:掩月宗
姓名:厉飞雨,修为:90,门派:黄枫谷
*/
return 0;
}
📌 语法糖 :指针->成员
等价于 (*指针).成员
,前者更简洁(如同元婴用意念直接操控形体,无需多余动作)。
修炼总结与进阶指引
- 修练目标:元婴期的核心是"灵活操控与复合整合":指针打破数据访问的限制,结构体整合多维度信息,二者结合让代码从"处理单一数据"升级为"驾驭复杂实体"。唯有熟练此道,方能在化神期(面向对象编程)中凝聚元神,掌控更高阶的C++神通。
- 元婴检验:能独立实现"修仙拍卖行系统"(用结构体数组存储拍品信息:名称、底价、当前价,用指针遍历并修改最高价,用函数判断拍卖结果),则元婴已成;
- 避坑要点 :
- 指针未初始化(野指针)、结构体成员未赋值就使用,会导致程序异常(如同元婴形体不稳);
- 结构体作为函数参数时,优先用指针或引用(
&
),避免值传递(复制整个结构体,浪费灵力);
- 下一境界预告:元婴期让你学会了如何使用指针和结构体来直接操作内存和处理复杂数据。接下来,你将进入化神期,学习如何使用类和对象来封装数据和行为,以及如何使用继承和多态来实现代码的复用和扩展,这将使你的程序更加灵活和强大。
境界5:化神期 ------ 类与对象基础
化神期是修士从"操控外物"到"元神自主"的质变:元婴期的结构体(元婴形体)仅能承载数据,而化神期的"类(Class)"将数据(属性)与操作数据的方法(法术)绑定,如同元婴成长为能自主施展神通的元神。若说指针是神魂御器,结构体是形体凝塑,那么类与对象便是"元神化形"------让数据与法术共生,实现代码的高度封装与复用,为后续"炼虚期(继承与多态)"奠定根基。
核心目标 | 关键技能 | 修炼成果 |
---|---|---|
元神塑型 | 类的定义、成员变量与成员函数 | 创建"数据+操作"绑定的元神模板(类) |
神形合一 | 对象的创建与使用、成员访问 | 实例化具体元神(对象),调用其自带法术 |
元神自塑 | 构造函数与析构函数 | 自动完成元神诞生(初始化)与消散(资源释放) |
神禁之术 | 封装与访问控制(public/private) | 保护元神核心数据,仅开放必要法术接口 |
第一式:元神铸模------类的定义
元婴期的结构体是"无自主意识的形体"(仅含数据),而化神期的"类"是"有自主神通的元神模板"------既包含属性(如修为、灵根),又包含操控这些属性的法术(如修炼、突破)。如同宗门秘法定义了元神的标准形态与能力,类定义了对象的"属性"与"行为"。
1.1 元神雏形:类的基本结构
心法 :类通过class
关键字定义,内部包含"成员变量"(属性,如姓名、修为)和"成员函数"(行为,如修炼、战斗),二者共同构成元神的完整形态。
cpp
#include <iostream>
#include <string>
using namespace std;
// 定义"修士"类(元神模板):包含属性与法术
class 修士 {
// 成员变量(元神核心属性,默认私有)
string 姓名; // 修士姓名
int 修为; // 当前修为
int 灵根[5]; // 五行灵根强度
// 成员函数(元神自带法术)
public: // 公开接口:允许外部调用的法术
// 设定修士基础信息
void 初始化(string 名, int 初始修为, int 根[5]) {
姓名 = 名;
修为 = 初始修为;
for (int i = 0; i < 5; i++) {
灵根[i] = 根[i];
}
}
// 修炼法术:提升修为
void 修炼(int 时长) {
修为 += 时长 * 2; // 每小时提升2点修为
cout << 姓名 << "修炼" << 时长 << "小时,修为提升至" << 修为 << endl;
}
// 展示状态法术
void 显形() {
cout << "【" << 姓名 << "】修为:" << 修为 << ",灵根:";
for (int i = 0; i < 5; i++) {
cout << 灵根[i] << " ";
}
cout << endl;
}
};
📌 关键注:
- 类是"模板",本身不占用内存,如同"元神蓝图";
- 成员变量与函数的默认访问权限是
private
(私有),需通过public
关键字开放外部可调用的接口(如同元神仅对外开放部分法术)。
1.2 神形之别:类与结构体的关联
类与结构体的核心差异在于默认访问权限 :结构体默认public
(适合纯数据载体),类默认private
(适合封装数据与操作)。化神期的类是元婴期结构体的"进化形态"------从"承载数据"升级为"自主行动"。
cpp
// 对比:结构体(元婴)vs 类(元神)
struct 元婴 { // 默认public,适合纯数据
string 姓名;
int 修为;
};
class 元神 { // 默认private,适合封装
string 姓名; // 私有:外部不可直接访问
int 修为;
public:
void 提升修为(int 增幅) { // 公开方法:外部通过方法操作
修为 += 增幅;
}
};
第二式:元神显形------对象的创建与使用
类是"元神模板",而"对象"是根据模板实例化的具体元神(如"韩立""南宫婉")。如同按蓝图炼制的元神实体,对象占用内存,可调用类中定义的法术(成员函数)。
2.1 具象化神:对象的创建与成员访问
心法 :通过类名 对象名
创建对象,用.
运算符调用成员函数(如同呼唤元神施展法术)。
cpp
int main() {
// 创建对象(具象化元神)
修士 韩立;
修士 南宫婉;
// 初始化对象(赋予元神初始属性)
int 韩立灵根[] = {90, 85, 0, 0, 0}; // 金木灵根
int 婉灵根[] = {0, 95, 0, 0, 80}; // 木水灵根
韩立.初始化("韩立", 103, 韩立灵根);
南宫婉.初始化("南宫婉", 120, 婉灵根);
// 调用成员函数(元神施展法术)
韩立.显形(); // 输出:【韩立】修为:103,灵根:90 85 0 0 0
南宫婉.修炼(5); // 输出:南宫婉修炼5小时,修为提升至130
南宫婉.显形(); // 输出:【南宫婉】修为:130,灵根:0 95 0 0 80
return 0;
}
⚠️ 避坑点:
-
若成员函数未在类内实现,需在类外声明(用
类名::函数名
指定归属),否则编译器会报错(如同元神找不到对应的法术口诀):cpp// 类外实现成员函数 void 修士::突破() { // 必须加"修士::",表明属于修士类 修为 += 50; cout << 姓名 << "成功突破,修为暴涨至" << 修为 << endl; }
2.2 元神集群:对象数组
当需要管理多名修士(多个元神)时,可创建"对象数组",如同宗门内的元神军团,通过循环批量调用法术。
cpp
int main() {
// 创建对象数组(3名修士)
修士 宗门弟子[3];
int 灵根1[] = {80, 0, 0, 0, 0};
int 灵根2[] = {0, 70, 0, 0, 0};
int 灵根3[] = {0, 0, 90, 0, 0};
// 初始化数组中的对象
宗门弟子[0].初始化("张三", 80, 灵根1);
宗门弟子[1].初始化("李四", 75, 灵根2);
宗门弟子[2].初始化("王五", 90, 灵根3);
// 批量修炼:所有弟子修炼3小时
cout << "\n宗门集体修炼后:" << endl;
for (int i = 0; i < 3; i++) {
宗门弟子[i].修炼(3);
}
return 0;
/* 输出:
张三修炼3小时,修为提升至86
李四修炼3小时,修为提升至81
王五修炼3小时,修为提升至96
*/
}
第三式:元神自生------构造函数与析构函数
元婴期需手动初始化结构体(如韩立.姓名 = "韩立"
),而化神期的"构造函数"可在对象创建时自动初始化(如同元神诞生时自动吸收天地灵气);"析构函数"则在对象销毁时自动清理资源(如同元神坐化时释放灵力),二者实现了元神的"自主生灭"。
3.1 元神降世:构造函数
心法:构造函数与类同名,无返回值,在对象创建时自动调用,用于初始化成员变量(支持重载,适配不同初始化需求)。
cpp
class 修士 {
string 姓名;
int 修为;
int 灵根[5];
public:
// 构造函数1:无参构造(默认初始化)
修士() {
姓名 = "无名修士";
修为 = 10;
for (int i = 0; i < 5; i++) {
灵根[i] = 10; // 普通灵根
}
cout << 姓名 << "(元神)诞生!" << endl;
}
// 构造函数2:带参构造(自定义初始化)
修士(string 名, int 初始修为, int 根[5]) {
姓名 = 名;
修为 = 初始修为;
for (int i = 0; i < 5; i++) {
灵根[i] = 根[i];
}
cout << 姓名 << "(元神)凝聚成功!" << endl;
}
void 显形() {
cout << 姓名 << ":修为" << 修为 << endl;
}
};
int main() {
修士 路人; // 调用无参构造:输出"无名修士(元神)诞生!"
int 灵根[] = {90, 85, 0, 0, 0};
修士 韩立("韩立", 103, 灵根); // 调用带参构造:输出"韩立(元神)凝聚成功!"
路人.显形(); // 输出:无名修士:修为10
韩立.显形(); // 输出:韩立:修为103
return 0;
}
3.2 元神寂灭:析构函数
心法 :析构函数格式为~类名()
,无参数无返回值,在对象销毁时自动调用(如程序结束、对象出作用域),用于释放动态内存等资源(如同元神消散时回收灵力)。
cpp
#include <cstring> // 用于strcpy
class 法器 {
char* 名称; // 动态分配字符串
public:
// 构造函数:动态申请内存
法器(const char* 名) {
名称 = new char[strlen(名) + 1]; // 分配内存
strcpy(名称, 名);
cout << 名称 << "(法器)铸成!" << endl;
}
// 析构函数:释放动态内存(必须手动写,否则内存泄漏)
~法器() {
cout << 名称 << "(法器)崩解,灵力回收!" << endl;
delete[] 名称; // 释放内存
}
};
int main() {
法器 青竹蜂云剑("青竹蜂云剑"); // 构造:输出"青竹蜂云剑(法器)铸成!"
{
法器 墨大夫拂尘("墨大夫拂尘"); // 作用域内创建
} // 离开作用域,自动调用析构:输出"墨大夫拂尘(法器)崩解..."
return 0;
} // 程序结束,青竹蜂云剑析构:输出"青竹蜂云剑(法器)崩解..."
📌 关键注:
- 若类中无动态内存(如仅用
string
而非char*
),编译器会生成默认析构函数,无需手动定义; - 若有动态内存(如
new
分配的空间),必须手动定义析构函数释放,否则会导致"内存泄漏"(如同元神消散后灵力遗散,浪费资源)。
第四式:神禁护核------封装与访问控制
化神期的元神需保护核心机密(如本源灵力),仅对外开放必要能力(如战斗法术)。C++通过public
(公开)、private
(私有)、protected
(保护)控制成员访问权限,实现"封装"------隐藏内部细节,仅暴露安全接口。
4.1 三层禁制:访问控制符
private
(核心禁制):仅类内部可访问(如元神本源,不可外泄);public
(公开法门):类内外均可访问(如对外法术,允许调用);protected
(宗门秘法):类内部及子类可访问(为炼虚期继承铺垫)。
cpp
class 修士 {
// 私有成员(核心禁制:仅类内函数可操作)
int 本源灵力; // 不可被外部直接修改
void 修复本源() { // 仅内部调用的自救法术
本源灵力 += 5;
}
public:
// 公开成员(公开法门:外部可调用)
string 姓名;
int 修为;
// 公开方法:外部通过此方法间接影响本源
void 重伤() {
本源灵力 -= 20;
if (本源灵力 < 0) 修复本源(); // 内部调用私有方法
cout << 姓名 << "本源受损,剩余本源:" << 本源灵力 << endl;
}
protected:
// 保护成员(宗门秘法:子类可继承)
int 宗门贡献;
};
int main() {
修士 韩立;
韩立.姓名 = "韩立"; // 公开成员:可直接访问
韩立.修为 = 103;
韩立.重伤(); // 公开方法:可调用
// 韩立.本源灵力 = 100; // 错误:私有成员,外部不可访问
// 韩立.宗门贡献 = 50; // 错误:保护成员,外部不可访问
return 0;
}
4.2 封装的意义:安全与复用
封装如同给元神加了"防护罩":外部无法直接修改核心数据(如本源灵力
),只能通过预设方法(如重伤()
)操作,避免误操作(如同禁止外人直接触碰元神本源)。同时,隐藏实现细节后,只需保证接口不变,内部逻辑可随意修改(如优化修复本源()
的算法),不影响外部使用。
第五式:元神御使------对象指针与引用
化神期可通过"元神印记"(指针)远程操控其他元神,或通过"神念连接"(引用)直接作用于元神本体。对象指针与引用能高效传递复杂对象(无需复制),如同元婴期指针操控结构体的进阶版。
5.1 元神印记:对象指针
心法 :指针指向对象地址,用->
运算符访问成员(替代.
, 更简洁),适合动态创建对象(如不确定数量的修士)。
cpp
int main() {
修士 韩立("韩立", 103, 灵根); // 栈上对象
修士* 韩立_ptr = &韩立; // 指针指向韩立
// 用指针访问成员:-> 等价于 (*指针).
韩立_ptr->修炼(2); // 输出:韩立修炼2小时,修为提升至107
(*韩立_ptr).显形(); // 等价操作:输出【韩立】修为:107...
// 动态创建对象(堆上):需用指针接收,用完手动释放
修士* 南宫婉_ptr = new 修士("南宫婉", 120, 婉灵根);
南宫婉_ptr->修炼(3); // 输出:南宫婉修炼3小时,修为提升至126
delete南宫婉_ptr; // 手动调用析构:释放堆内存
return 0;
}
5.2 神念连接:对象引用
引用是对象的"别名"(如同元神的分身),必须初始化且不可更改指向,适合作为函数参数(避免对象复制,效率更高)。
cpp
// 函数:通过引用提升修士修为(直接操作原对象)
void 灌顶(修士& 目标, int 灵力) { // &表示引用
目标.修炼(灵力 / 2); // 直接调用原对象的方法
}
int main() {
修士 韩立("韩立", 103, 灵根);
灌顶(韩立, 10); // 传递引用:等价于直接操作韩立
// 输出:韩立修炼5小时,修为提升至113
return 0;
}
修炼总结与进阶指引
- 修练目标:化神期的核心是"封装与自主":类将数据与操作绑定,构造/析构函数实现自动管理,访问控制保障安全,对象指针与引用提升操控效率。唯有熟练此道,方能在炼虚期(继承与多态)中让元神分化演化,掌控更复杂的C++天地。
- 化神检验 :能独立实现"修仙门派管理系统"(用
门派
类包含修士
对象数组,通过成员函数实现招收弟子、集体修炼、评选首席等功能),则元神已成; - 避坑要点 :
- 构造函数无返回值,析构函数无参数,二者不可重载(如同元神生灭有固定法则);
- 动态对象(
new
创建)需手动delete
,否则内存泄漏(如同元神滞留人间,扰乱秩序); - 访问控制需合理:核心数据设为
private
,接口设为public
(如同元神不轻易暴露本源);
- 下一境界预告:化神期让你学会了如何使用类和对象来封装数据和行为,以及如何使用继承和多态来实现代码的复用和扩展。接下来,你将进入炼虚期,学习如何使用STL容器来管理数据,以及如何使用泛型编程来实现更灵活和高效的代码,这将使你的程序能够处理更大规模和更复杂的数据。
境界6:炼虚期 ------ 继承与多态
炼虚期是修士突破单体局限、构建谱系传承的关键境界:化神期(类与对象)已能凝聚独立元神(封装完整的类),而炼虚期则能通过"血脉传承"(继承)吸纳先辈元神的核心神通,并以"千面化形"(多态)根据战场变化展现不同形态。在C++世界中,继承让类与类形成"宗门谱系",实现代码复用与层级扩展;多态让同一接口呈现不同实现,如同修士可化剑、化盾、化雷,极大提升代码的灵活性与扩展性,为后续"合体期(泛型与面向对象结合)"奠定根基。
核心目标 | 关键技能 | 修炼成果 |
---|---|---|
血脉传承 | 基类与派生类定义、继承方式(public/protected/private)、构造函数初始化 | 构建类的层级关系(如"生物→修士→门派弟子"),实现代码复用 |
千面化形 | 虚函数、动态绑定、纯虚函数与抽象类 | 同一接口呈现不同行为(如"施法"接口,剑修放剑气、法修放火球) |
谱系统御 | 多层继承、多态调用、继承与组合的取舍 | 设计复杂类体系(如宗门内"长老→执事→弟子"的权限与行为体系) |
第一式:血脉传承------继承(Inheritance)
化神期的类如同独立修士,各自拥有独特神通;而炼虚期的继承能让新修士(派生类)继承先辈(基类)的核心能力(成员变量与函数),并在此基础上新增或改良神通。如同青阳城的修士继承黄枫谷的基础心法,再修炼专属剑诀,既保留传承又具特色。
1.1 血脉认主:基类与派生类的定义
心法 :通过class 派生类 : 继承方式 基类
声明继承关系,派生类自动获得基类的非私有成员。继承方式决定基类成员在派生类中的访问权限(如同宗门规矩决定弟子能学习多少先辈秘法)。
cpp
#include <iostream>
#include <string>
using namespace std;
// 基类(先辈修士):定义所有修士的基础能力
class Cultivator {
protected: // 保护成员:派生类可访问,外部不可见(如同宗门内传秘法)
string name; // 姓名
int power; // 灵力值
public:
// 基类构造函数
Cultivator(string n, int p) : name(n), power(p) {}
// 基础神通:展示信息
void showInfo() {
cout << "姓名:" << name << ",灵力:" << power << endl;
}
// 基础攻击(可被派生类改良)
void attack() {
cout << name << "使用基础掌法攻击,造成" << power * 0.5 << "点伤害" << endl;
}
};
// 派生类(剑修):继承自Cultivator,新增剑修专属能力
class SwordCultivator : public Cultivator { // public继承:基类public成员仍为public
private:
string swordName; // 专属飞剑
public:
// 派生类构造函数:必须先初始化基类(用初始化列表)
SwordCultivator(string n, int p, string sn) : Cultivator(n, p), swordName(sn) {}
// 新增剑修专属神通:御剑
void flyWithSword() {
cout << name << "驾驭" << swordName << "飞行,速度极快" << endl;
}
// 改良攻击:重写基类的attack(仅能重写非private成员)
void attack() {
cout << name << "挥舞" << swordName << "攻击,造成" << power * 1.2 << "点伤害" << endl;
}
};
int main() {
// 创建剑修对象(自动继承基类的属性与方法)
SwordCultivator hanli("韩立", 100, "青竹蜂云剑");
hanli.showInfo(); // 调用基类方法(继承的基础能力)
hanli.attack(); // 调用重写后的攻击(改良的神通)
hanli.flyWithSword(); // 调用派生类新增方法(专属能力)
return 0;
}
📌 关键注:
- 派生类构造函数必须在初始化列表中先调用基类构造函数(如同弟子入门需先拜入先辈门下);
- 继承方式影响访问权限:
public
继承保留基类访问级别,protected
继承将基类public成员变为protected,private
继承则将基类非private成员变为private(实际开发中public
继承最常用,如同正统传承)。
1.2 血脉分支:多层继承与成员访问
宗门传承往往有多代,如同"祖师→长老→弟子",C++支持多层继承(派生类再作为基类)。但需注意:派生类只能访问基类的public和protected成员,private成员如同祖传秘宝,仅基类自身可使用。
cpp
#include <iostream>
#include <string>
using namespace std;
// 祖师级(顶层基类)
class Ancestor {
protected:
string sect; // 所属宗门(保护成员,后代可访问)
private:
string coreSecret; // 核心秘典(私有成员,仅祖师自己知道)
public:
Ancestor(string s, string cs) : sect(s), coreSecret(cs) {}
};
// 长老级(派生自Ancestor)
class Elder : public Ancestor {
protected:
int disciples; // 管辖弟子数
public:
Elder(string s, string cs, int d) : Ancestor(s, cs), disciples(d) {}
void manage() {
cout << "长老管理" << disciples << "名弟子,宗门:" << sect << endl;
// cout << coreSecret; // 错误:无法访问基类的private成员
}
};
// 弟子级(派生自Elder,即多层继承)
class Disciple : public Elder {
private:
int level; // 修为等级
public:
Disciple(string s, string cs, int d, int l) : Elder(s, cs, d), level(l) {}
void practice() {
cout << "弟子在" << sect << "修炼,当前等级:" << level << endl;
// cout << disciples; // 正确:可访问父类(Elder)的protected成员
}
};
int main() {
Disciple li("黄枫谷", "青元剑诀", 50, 3);
li.manage(); // 调用Elder的方法
li.practice(); // 调用自己的方法
return 0;
}
⚠️ 避坑点:
- 避免"钻石继承"(一个派生类继承自两个有共同基类的类),会导致基类成员重复(如同一个弟子同时拜两位同宗祖师,出现传承冲突),需用虚继承解决(后续境界详解);
- 继承层次不宜过深(建议不超过3层),否则如同宗门谱系混乱,代码难以维护。
第二式:千面化形------多态(Polymorphism)
炼虚期修士的核心神通是"化形":同一修士可根据对手切换形态(如对水怪化火形,对妖兽化雷形)。C++中的多态通过"虚函数"实现:基类指针/引用指向派生类对象时,调用虚函数会自动执行派生类的重写版本,实现"同一接口,不同行为"。
2.1 化形根基:虚函数与动态绑定
心法 :在基类函数前加virtual
关键字声明为虚函数,派生类用override
显式重写(推荐),调用时会根据对象实际类型(而非指针类型)执行对应版本(动态绑定)。
cpp
#include <iostream>
#include <string>
using namespace std;
// 基类:修士
class Cultivator {
protected:
string name;
public:
Cultivator(string n) : name(n) {}
// 虚函数:施法(可被化形的基础)
virtual void castSpell() {
cout << name << "使用基础法术" << endl;
}
};
// 派生类:火修(化火形)
class FireCultivator : public Cultivator {
public:
FireCultivator(string n) : Cultivator(n) {}
// 重写虚函数(加override更清晰,编译器会检查是否正确重写)
void castSpell() override {
cout << name << "释放烈火燎原!" << endl;
}
};
// 派生类:水修(化水形)
class WaterCultivator : public Cultivator {
public:
WaterCultivator(string n) : Cultivator(n) {}
void castSpell() override {
cout << name << "施展水淹七军!" << endl;
}
};
int main() {
// 基类指针指向不同派生类对象(如同修士元神可附于不同形态)
Cultivator* c1 = new FireCultivator("萧炎");
Cultivator* c2 = new WaterCultivator("海波东");
// 同一接口(castSpell),根据对象实际类型执行不同行为(多态)
c1->castSpell(); // 输出:萧炎释放烈火燎原!
c2->castSpell(); // 输出:海波东施展水淹七军!
// 释放内存(虚析构函数后续讲解)
delete c1;
delete c2;
return 0;
}
📌 关键注:
- 动态绑定仅在"基类指针/引用指向派生类对象"时生效,直接用派生类对象调用函数会优先执行自身版本(非多态场景);
override
关键字不是必须的,但加上后编译器会检查是否真的重写了基类虚函数(如函数名、参数是否一致),避免拼写错误导致的隐藏bug。
2.2 化形极致:纯虚函数与抽象类
有些基类仅作为"形态模板",自身不实现具体神通(如同"五行修士"只是概念,需具体到金、木、水、火、土)。这类基类可声明"纯虚函数"(virtual 函数名() = 0
),包含纯虚函数的类称为"抽象类",无法实例化,只能作为基类被继承。
cpp
#include <iostream>
#include <string>
using namespace std;
// 抽象类:五行修士(仅定义接口,无具体实现)
class FiveElementCultivator {
protected:
string name;
public:
FiveElementCultivator(string n) : name(n) {}
// 纯虚函数:必须由派生类实现(如同五行修士必须掌握对应属性法术)
virtual void elementAttack() = 0;
virtual void elementDefense() = 0;
};
// 具体派生类:金系修士
class MetalCultivator : public FiveElementCultivator {
public:
MetalCultivator(string n) : FiveElementCultivator(n) {}
// 必须实现所有纯虚函数,否则仍是抽象类
void elementAttack() override {
cout << name << "用金系法术·锐金剑气攻击" << endl;
}
void elementDefense() override {
cout << name << "布下金刚不坏防御" << endl;
}
};
int main() {
// FiveElementCultivator f("张三"); // 错误:抽象类不能实例化
FiveElementCultivator* m = new MetalCultivator("庚金"); // 正确:抽象类指针指向派生类
m->elementAttack(); // 输出:庚金用金系法术·锐金剑气攻击
m->elementDefense(); // 输出:庚金布下金刚不坏防御
delete m;
return 0;
}
实战场景 :游戏角色系统
用抽象类Role
定义所有角色的通用接口(attack()
、defense()
),派生类Warrior
、Mage
、Archer
分别实现不同战斗方式,通过基类指针数组统一管理,动态调用各自技能。
第三式:神魂统御------继承与多态实战
炼虚期的终极修炼是将"血脉传承"与"千面化形"结合,构建完整的宗门功法体系。以下案例模拟一个宗门的任务系统:不同职位(长老、执事、弟子)继承自SectMember
基类,通过多态实现各自的任务处理逻辑。
cpp
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// 基类:宗门成员
class SectMember {
protected:
string name;
string position; // 职位
public:
SectMember(string n, string p) : name(n), position(p) {}
// 虚函数:处理任务(不同职位处理方式不同)
virtual void handleTask(string task) = 0; // 纯虚函数,基类为抽象类
// 非虚函数:展示身份
void showIdentity() {
cout << "【" << position << "】" << name;
}
};
// 派生类:长老(处理核心任务)
class Elder : public SectMember {
public:
Elder(string n) : SectMember(n, "长老") {}
void handleTask(string task) override {
showIdentity();
cout << "主持" << task << ",调动全宗门资源" << endl;
}
};
// 派生类:执事(处理日常任务)
class Deacon : public SectMember {
public:
Deacon(string n) : SectMember(n, "执事") {}
void handleTask(string task) override {
showIdentity();
cout << "安排弟子执行" << task << ",监督进度" << endl;
}
};
// 派生类:弟子(执行具体任务)
class Disciple : public SectMember {
public:
Disciple(string n) : SectMember(n, "弟子") {}
void handleTask(string task) override {
showIdentity();
cout << "执行" << task << ",努力完成中" << endl;
}
};
int main() {
// 用基类指针数组存储不同职位成员(多态容器)
vector<SectMember*> members;
members.push_back(new Elder("墨大夫"));
members.push_back(new Deacon("张铁"));
members.push_back(new Disciple("韩立"));
// 发布任务,统一调用handleTask(多态效果:自动匹配各自实现)
vector<string> tasks = {"宗门大典", "灵田耕种", "妖兽围剿"};
for (int i = 0; i < members.size(); i++) {
members[i]->handleTask(tasks[i]);
}
/* 输出:
【长老】墨大夫主持宗门大典,调动全宗门资源
【执事】张铁安排弟子执行灵田耕种,监督进度
【弟子】韩立执行妖兽围剿,努力完成中
*/
// 释放资源
for (auto m : members) {
delete m;
}
return 0;
}
⚠️ 避坑点:
- 基类析构函数必须声明为虚函数(
virtual ~基类()
),否则删除基类指针指向的派生类对象时,可能只调用基类析构函数,导致内存泄漏(如同化形后元神未完全收回); - 优先用"组合"而非"继承":若两个类是"有一个"关系(如"修士有一把剑"),而非"是一个"关系(如"剑修是一种修士"),应使用组合(类中包含另一个类的对象),避免继承滥用导致的层级臃肿。
修炼总结与进阶指引
- 修练目标:炼虚期的核心是"传承与变化的平衡":继承确保代码复用与体系清晰,多态实现接口统一与动态适配。唯有理解二者的本质(继承是"is-a",多态是"接口不变,实现可变"),才能在C++的修仙路上构建出既有序又灵活的代码宗门。
- 炼虚检验 :能独立设计"修仙门派对战系统"------用抽象类
Faction
定义门派接口(attack()
、defend()
),派生Qingyun
、BloodFiend
等门派类实现不同战法,通过基类指针实现随机对战(多态调用),则炼虚功成; - 避坑要点 :
- 继承不是"代码复制",若派生类与基类无"是一个"关系,强行继承会导致逻辑混乱(如同强行让妖兽继承人类血脉);
- 虚函数会增加内存开销(每个类有虚函数表),简单场景无需滥用;
- 下一境界预告:炼虚期让你学会了如何使用继承和多态来实现代码的复用和扩展。接下来,你将进入合体期,学习如何使用STL容器来管理数据,并结合泛型编程来实现更灵活和高效的代码,这将使你的程序能够处理更大规模和更复杂的数据。
境界7:合体期 ------ STL容器基础
合体期是修士将"神魂(逻辑)""元婴(数据结构)""法器(内存)"深度融合的境界:此前修士需手动管理数组大小、链表节点(如同亲手锻造储物袋),而合体期可驾驭"先天灵宝(STL容器)"------这些容器自带"储物规则"(封装好的数据结构),能自动处理内存分配、元素增删,让修士专注于"御使灵物(业务逻辑)"而非"锻造容器"。如同合体修士心念一动即可收纳万千法宝,STL容器让数据管理从"手动操控"升级为"自动化整合"。
核心目标 | 关键技能 | 修炼成果 |
---|---|---|
灵宝认主 | 常用容器(vector/list/map)定义与初始化 | 掌握不同特性的"储物灵宝"(连续存储/链式存储/键值存储) |
神识御物 | 迭代器(iterator)遍历、元素访问 | 用神识(迭代器)高效操作容器内的"灵物"(数据) |
灵宝神通 | 容器常用成员函数(增删改查) | 熟练调用容器自带神通(如vector::push_back、map::find) |
灵宝合璧 | 容器与算法结合(如sort) | 让容器与"秘法(算法)"配合,实现复杂数据处理 |
第一式:灵宝图鉴------STL容器基础
STL(Standard Template Library)容器是C++标准库提供的"预制储物灵宝",按存储方式分为序列式容器 (如vector、list)和关联式容器(如map),如同修仙界的乾坤袋、灵链、宝鉴等,各有独特神通。
1.1 乾坤袋:vector(动态数组)
特性:连续内存存储,支持随机访问(如同乾坤袋内物品按序排列,可直接伸手取第n件),自动扩容(装不下时自动变大)。适合频繁访问、较少插入删除的场景。
cpp
#include <iostream>
#include <vector> // 需包含vector头文件
using namespace std;
int main() {
// 初始化乾坤袋(vector):存储修士灵力值
vector<int> 灵力袋; // 空袋
vector<int> 弟子灵力 = {10, 20, 30, 45}; // 初始装入4个灵力值
// 装物(添加元素):push_back(灵物) 从尾部放入
灵力袋.push_back(5);
灵力袋.push_back(15);
灵力袋.push_back(25);
// 查物(访问元素):[]下标或at()(越界时at()会报错,更安全)
cout << "灵力袋第2件(下标1):" << 灵力袋[1] << endl; // 15
cout << "弟子灵力第4件:" << 弟子灵力.at(3) << endl; // 45
// 观袋(容器信息)
cout << "灵力袋容量(当前能装多少):" << 灵力袋.capacity() << endl; // 4(自动扩容)
cout << "灵力袋实际物品数:" << 灵力袋.size() << endl; // 3
// 清空袋中部分物品:erase(起始位置, 结束位置)
弟子灵力.erase(弟子灵力.begin() + 1, 弟子灵力.begin() + 3); // 删除下标1-2的元素
cout << "删除后弟子灵力:";
for (int i = 0; i < 弟子灵力.size(); i++) {
cout << 弟子灵力[i] << " "; // 输出10 45
}
return 0;
}
📌 关键注:
-
vector扩容时会申请新内存并复制旧数据,频繁扩容影响效率(如同乾坤袋扩容时需转移所有物品),可提前用
reserve(n)
预留容量; -
迭代器(iterator)是访问容器的"神识",
begin()
返回首元素神识,end()
返回尾元素后一位神识(不可访问):cpp// 用神识(迭代器)遍历灵力袋 for (vector<int>::iterator 神识 = 灵力袋.begin(); 神识 != 灵力袋.end(); 神识++) { cout << *神识 << " "; // *解引用,获取神识指向的元素(5 15 25) }
1.2 灵链:list(双向链表)
特性:非连续内存存储,元素通过指针连接(如同灵珠串成的链),插入删除效率高(只需修改指针,无需移动其他元素),但不支持随机访问(不能直接取第n个元素,需从头遍历)。适合频繁插入删除的场景。
cpp
#include <iostream>
#include <list> // 需包含list头文件
using namespace std;
int main() {
// 初始化灵链(list):存储任务名称
list<string> 任务链 = {"采集灵草", "斩杀妖兽", "炼制丹药"};
// 链头加任务:push_front
任务链.push_front("拜见师尊");
// 链尾加任务:push_back
任务链.push_back("打坐修炼");
// 用神识遍历灵链(list无下标,必须用迭代器)
cout << "当前任务链:";
for (list<string>::iterator 神识 = 任务链.begin(); 神识 != 任务链.end(); 神识++) {
cout << *神识 << " → "; // 拜见师尊 → 采集灵草 → 斩杀妖兽 → 炼制丹药 → 打坐修炼
}
cout << "完成" << endl;
// 删除中间任务(先定位到"斩杀妖兽",再删除)
list<string>::iterator 目标 = 任务链.begin();
advance(目标, 2); // 神识移动2步(指向第3个元素"斩杀妖兽")
任务链.erase(目标);
// 插入新任务到"炼制丹药"前
目标 = 任务链.begin();
advance(目标, 3); // 移动到"炼制丹药"位置
任务链.insert(目标, "购买丹炉");
cout << "修改后任务链:";
for (auto 神识 : 任务链) { // C++11范围for循环(自动用迭代器遍历)
cout << 神识 << " → "; // 拜见师尊 → 采集灵草 → 炼制丹药 → 购买丹炉 → 打坐修炼
}
cout << "完成" << endl;
return 0;
}
⚠️ 避坑点:
- list的迭代器在插入/删除元素后仍有效(灵链断接后指针可重连),但vector的迭代器在扩容后会失效(乾坤袋换了位置,旧神识找不到);
- 不可用
[]
访问list元素,需用advance(迭代器, n)
移动神识,时间复杂度为O(n)(遍历n步)。
1.3 宝鉴:map(键值对容器)
特性:存储"键-值"对(如同宝鉴中"宝物名称→详细信息"),自动按键排序,支持快速查找(类似字典查字)。适合需要通过唯一标识(键)快速定位数据的场景。
cpp
#include <iostream>
#include <map> // 需包含map头文件
#include <string>
using namespace std;
int main() {
// 初始化宝鉴(map):键=法宝名(string),值=灵力值(int)
map<string, int> 法宝宝鉴;
// 录入法宝信息:insert(pair<键类型, 值类型>(键, 值)) 或 直接赋值
法宝宝鉴["青竹蜂云剑"] = 500;
法宝宝鉴["墨大夫的小瓶"] = 1000;
法宝宝鉴.insert(pair<string, int>("巨阙 sword", 800));
// 查找法宝:find(键) 返回迭代器,未找到则返回end()
string 想找的法宝 = "墨大夫的小瓶";
map<string, int>::iterator 结果 = 法宝宝鉴.find(想找的法宝);
if (结果 != 法宝宝鉴.end()) {
cout << "找到" << 想找的法宝 << ",灵力值:" << 结果->second << endl; // second是值
} else {
cout << "未找到" << 想找的法宝 << endl;
}
// 遍历宝鉴(自动按键的字典序排序)
cout << "法宝列表(按名称排序):" << endl;
for (auto 条目 : 法宝宝鉴) { // 条目.first是键,条目.second是值
cout << 条目.first << ":" << 条目.second << "灵力" << endl;
}
/* 输出:
青竹蜂云剑:500灵力
墨大夫的小瓶:1000灵力
巨阙 sword:800灵力
*/
// 删除法宝
法宝宝鉴.erase("巨阙 sword");
cout << "删除后法宝数量:" << 法宝宝鉴.size() << endl; // 2
return 0;
}
📌 关键注:
- map的键唯一(同一法宝名不能录两次),若需重复键,用
multimap
; - 查找效率为O(log n)(宝鉴有索引),远高于vector/list的O(n)(逐个翻找)。
第二式:神识御宝------迭代器与容器算法
合体期修士的"神识(迭代器)"不仅能遍历容器,还能配合"天道法则(STL算法)"施展神通,如排序、查找、计数等,无需手动编写循环。
2.1 排序神通:sort算法
cpp
#include <iostream>
#include <vector>
#include <algorithm> // 需包含算法头文件(sort在此处)
using namespace std;
int main() {
vector<int> 弟子修为 = {80, 50, 90, 30, 70};
// 排序前
cout << "排序前修为:";
for (int x : 弟子修为) cout << x << " "; // 80 50 90 30 70
// 调用sort算法(默认升序):sort(起始迭代器, 结束迭代器)
sort(弟子修为.begin(), 弟子修为.end());
// 排序后
cout << "\n升序排序后:";
for (int x : 弟子修为) cout << x << " "; // 30 50 70 80 90
// 降序排序(用greater<int>()指定规则)
sort(弟子修为.begin(), 弟子修为.end(), greater<int>());
cout << "\n降序排序后:";
for (int x : 弟子修为) cout << x << " "; // 90 80 70 50 30
return 0;
}
💡 扩展:对结构体容器排序(需自定义比较规则)
cpp
#include <vector>
#include <algorithm>
struct 修士 {
string 姓名;
int 修为;
};
// 自定义比较函数:按修为降序
bool 按修为排序(const 修士& a, const 修士& b) {
return a.修为 > b.修为;
}
int main() {
vector<修士> 宗门 = {{"韩立", 88}, {"南宫婉", 120}, {"厉飞雨", 90}};
sort(宗门.begin(), 宗门.end(), 按修为排序); // 用自定义规则排序
for (auto x : 宗门) {
cout << x.姓名 << ":" << x.修为 << endl; // 南宫婉→厉飞雨→韩立
}
return 0;
}
2.2 查找神通:find与count
cpp
#include <iostream>
#include <list>
#include <algorithm>
using namespace std;
int main() {
list<string> 丹药列表 = {"回春丹", "筑基丹", "回春丹", "破障丹"};
// 查找"筑基丹"
auto 找到 = find(丹药列表.begin(), 丹药列表.end(), "筑基丹");
if (找到 != 丹药列表.end()) {
cout << "找到" << *找到 << endl;
}
// 计数"回春丹"出现次数
int 回春丹数量 = count(丹药列表.begin(), 丹药列表.end(), "回春丹");
cout << "回春丹数量:" << 回春丹数量 << endl; // 2
return 0;
}
第三式:灵宝合璧------容器实战案例
案例:宗门资源管理系统
需求:用vector存储弟子信息(姓名+修为),用map记录弟子的任务完成情况(姓名→完成数),最后按修为排序并输出所有信息。
cpp
#include <iostream>
#include <vector>
#include <map>
#include <algorithm>
#include <string>
using namespace std;
// 弟子结构体
struct 弟子 {
string 姓名;
int 修为;
};
// 排序规则:修为从高到低
bool 排序规则(const 弟子& a, const 弟子& b) {
return a.修为 > b.修为;
}
int main() {
// 1. 存储弟子信息(vector)
vector<弟子> 弟子列表 = {
{"韩立", 103},
{"南宫婉", 120},
{"厉飞雨", 90},
{"陈巧倩", 85}
};
// 2. 记录任务完成情况(map)
map<string, int> 任务完成;
任务完成["韩立"] = 5;
任务完成["南宫婉"] = 8;
任务完成["厉飞雨"] = 3;
任务完成["陈巧倩"] = 4;
// 3. 按修为排序弟子
sort(弟子列表.begin(), 弟子列表.end(), 排序规则);
// 4. 输出所有信息
cout << "宗门弟子信息(按修为排序):" << endl;
for (const auto& 弟 : 弟子列表) { // const auto& 避免复制,提高效率
cout << "姓名:" << 弟.姓名
<< ",修为:" << 弟.修为
<< ",完成任务:" << 任务完成[弟.姓名] << "个" << endl;
}
/* 输出:
姓名:南宫婉,修为:120,完成任务:8个
姓名:韩立,修为:103,完成任务:5个
姓名:厉飞雨,修为:90,完成任务:3个
姓名:陈巧倩,修为:85,完成任务:4个
*/
return 0;
}
修炼总结与进阶指引
- 修练目标:合体期的核心是"工具复用与效率提升":STL容器封装了成熟的数据结构,让修士无需重复造轮子,专注于业务逻辑。唯有熟练驾驭各类容器的特性与算法,方能在炼虚期进一步突破,实现数据管理的"虚实转换"。
- 合体检验:能独立实现"修仙拍卖行系统"(用vector存储拍品,map记录拍品ID与价格,list维护竞拍队列,用sort排序最高价),则合体功成;
- 避坑要点 :
- 容器迭代器失效问题:vector扩容后迭代器失效,list删除元素后其他迭代器仍有效,map插入元素不影响迭代器;
- 选择合适容器:频繁访问用vector,频繁增删用list,需键值查找用map(选错容器如同用乾坤袋装活物,效率低下);
- 下一境界预告:合体期让你学会了如何使用STL容器来管理数据。接下来,你将进入大乘期,学习如何使用模板来编写通用的代码,这将使你的程序能够处理不同类型的数据,实现更高的代码复用和灵活性。
境界8:大乘期 ------ 模板基础
大乘期是修士突破类型桎梏的关键境界:此前境界的代码往往绑定特定类型(如int
函数只能处理整数,string
类只能存储字符串),如同修士被单一灵根限制;而大乘期的"模板"之术,能凝练出与类型无关的"通用法诀",如同大乘修士可凭同一神通驾驭金、木、水、火、土等万类灵根,让代码从"一对一适配"迈向"一对多通用",为后续"渡劫期(泛型算法)"奠定根基。
核心目标 | 关键技能 | 修炼成果 |
---|---|---|
万法归一 | 函数模板定义/实例化、模板参数推导 | 编写通用函数(如同一交换函数适配int /double /string ) |
万象同体 | 类模板定义/成员实现、模板类实例化 | 构建通用容器(如同一栈结构可存储int /struct /指针) |
通变之术 | 模板特化、非类型模板参数 | 针对特殊类型定制逻辑(如对指针类型的特殊处理) |
第一式:万法归一------函数模板(Function Template)
筑基期写交换int
的函数,金丹期再写交换double
的函数,本质是重复劳动;大乘期的函数模板能将"交换"这一逻辑抽象为通用法诀,自动适配所有类型,如同修士以同一印诀操控不同属性的灵力。
1.1 法诀凝练:函数模板的定义与使用
心法 :函数模板通过template<typename T>
声明"类型参数T
",将函数中的具体类型替换为T
,编译器会根据传入的实参自动推导T
的类型并生成对应代码(模板实例化)。
cpp
#include <iostream>
#include <string>
using namespace std;
// 定义函数模板:通用交换法诀(T为类型参数,可代表任意类型)
template<typename T> // 声明模板参数T(typename可换为class)
void 交换法诀(T& a, T& b) { // 用T替代具体类型
T 临时 = a;
a = b;
b = 临时;
}
int main() {
// 实例1:交换int类型(编译器自动推导T=int)
int 灵力A = 100, 灵力B = 200;
交换法诀(灵力A, 灵力B);
cout << "交换后灵力:" << 灵力A << " " << 灵力B << endl; // 200 100
// 实例2:交换double类型(T=double)
double 精元A = 3.5, 精元B = 7.2;
交换法诀(精元A, 精元B);
cout << "交换后精元:" << 精元A << " " << 精元B << endl; // 7.2 3.5
// 实例3:交换string类型(T=string)
string 功法A = "青元剑诀", 功法B = "长春功";
交换法诀(功法A, 功法B);
cout << "交换后功法:" << 功法A << " " << 功法B << endl; // 长春功 青元剑诀
return 0;
}
📌 关键注:
- 模板不会直接生成代码,只有当传入具体类型时,编译器才会"实例化"出对应版本(如
交换法诀<int>
、交换法诀<string>
); - 类型参数
T
可自定义名称(如template<typename 灵根类型>
),但通常用T
(Type)、U
、V
等简洁标识。
1.2 多参法诀:多模板参数函数
若函数需处理不同类型的参数(如比较两个不同类型的值),可声明多个模板参数,如同大乘修士同时操控两种灵根施展复合神通。
cpp
#include <iostream>
using namespace std;
// 多参数模板:返回两个不同类型参数中的较大值
template<typename T, typename U> // 两个类型参数T和U
auto 取最大值(T a, U b) -> decltype(a > b ? a : b) {
// decltype用于自动推导返回值类型(C++11特性)
return (a > b) ? a : b;
}
int main() {
int 修士A修为 = 150;
double 修士B修为 = 149.9;
cout << "修为更高者:" << 取最大值(修士A修为, 修士B修为) << endl; // 150(int)
long 灵石A = 100000;
short 灵石B = 99999;
cout << "灵石更多者:" << 取最大值(灵石A, 灵石B) << endl; // 100000(long)
return 0;
}
⚠️ 避坑点:
- 多参数模板的类型推导需保证逻辑合法(如
a > b
必须有定义),否则会编译报错(如同两种灵根属性冲突无法兼容); - 若返回值类型无法通过
decltype
自动推导,可显式指定返回类型(如template<typename T, typename U> T 取最大值(...)
)。
第二式:万象同体------类模板(Class Template)
结构体和类只能存储固定类型的数据(如struct 修士
只能存string
姓名和int
修为),如同单一容器只能装特定物品;类模板能创建"通用容器",如同大乘修士的储物袋可容纳丹药、法器、符箓等万物,同一类模板可存储任意类型的数据。
2.1 储物袋雏形:类模板的定义与实例化
心法 :类模板在类定义前声明template<typename T>
,类内成员变量和函数可使用T
作为类型,实例化时需显式指定类型(如储物袋<int>
、储物袋<修士>
)。
cpp
#include <iostream>
#include <string>
using namespace std;
// 定义类模板:通用储物袋(可存储任意类型物品)
template<typename T>
class 储物袋 {
private:
T 物品; // 存储的物品(类型为T)
bool 是否为空;
public:
// 构造函数:初始化储物袋
储物袋() : 是否为空(true) {}
// 存入物品
void 存入(T 新物品) {
物品 = 新物品;
是否为空 = false;
cout << "已存入物品" << endl;
}
// 取出物品(返回引用,可修改)
T& 取出() {
if (是否为空) {
cout << "储物袋为空!" << endl;
// 为简化示例,返回临时值(实际开发需更严谨处理)
static T 空物品;
return 空物品;
}
return 物品;
}
};
int main() {
// 实例1:存储int类型(灵石数量)
储物袋<int> 灵石袋;
灵石袋.存入(1000);
cout << "取出灵石:" << 灵石袋.取出() << endl; // 1000
// 实例2:存储string类型(功法名称)
储物袋<string> 功法袋;
功法袋.存入("大衍诀");
cout << "取出功法:" << 功法袋.取出() << endl; // 大衍诀
// 实例3:存储结构体类型(修士对象)
struct 修士 { string 姓名; int 修为; };
储物袋<修士> 修士袋;
修士袋.存入({"韩立", 10000});
修士 取出的修士 = 修士袋.取出();
cout << "取出修士:" << 取出的修士.姓名 << "(修为" << 取出的修士.修为 << ")" << endl;
return 0;
}
2.2 进阶储物袋:带成员函数实现的类模板
类模板的成员函数若在类外实现,需重复声明模板参数,如同储物袋的复杂功能需额外刻制法阵符文。
cpp
#include <iostream>
using namespace std;
// 类模板:通用栈(后进先出容器)
template<typename T>
class 灵栈 {
private:
T* 数据; // 用指针动态分配数组
int 容量; // 栈的最大容量
int 顶部索引; // 栈顶位置(-1表示空)
public:
// 构造函数:初始化栈
灵栈(int 最大容量) : 容量(最大容量), 顶部索引(-1) {
数据 = new T[容量]; // 动态分配T类型数组
}
// 析构函数:释放内存
~灵栈() {
delete[] 数据;
}
// 入栈(类外实现成员函数)
void 入栈(T 元素);
// 出栈(类外实现)
T 出栈();
// 判断是否为空
bool 为空() {
return 顶部索引 == -1;
}
};
// 类外实现入栈函数(需带模板参数)
template<typename T>
void 灵栈<T>::入栈(T 元素) {
if (顶部索引 + 1 >= 容量) {
cout << "灵栈已满,无法入栈!" << endl;
return;
}
数据[++顶部索引] = 元素;
cout << "元素" << 元素 << "已入栈" << endl;
}
// 类外实现出栈函数
template<typename T>
T 灵栈<T>::出栈() {
if (为空()) {
cout << "灵栈为空,无法出栈!" << endl;
return T(); // 返回T类型的默认值(如int返回0,string返回空)
}
T 弹出元素 = 数据[顶部索引--];
cout << "元素" << 弹出元素 << "已出栈" << endl;
return 弹出元素;
}
int main() {
// 实例化一个存储double的灵栈(容量3)
灵栈<double> 灵力栈(3);
灵力栈.入栈(10.5);
灵力栈.入栈(20.3);
灵力栈.入栈(5.7);
灵力栈.入栈(8.9); // 栈满提示
while (!灵力栈.为空()) {
灵力栈.出栈(); // 依次弹出5.7、20.3、10.5
}
return 0;
}
📌 关键注:
- 类模板的成员函数必须与类模板定义在同一文件(通常是头文件)中,否则编译器无法实例化(如同储物袋和配套法诀必须存放在一起);
- 实例化不同类型的类模板会生成完全独立的类(如
灵栈<int>
和灵栈<double>
是两个不同的类),彼此无关联。
第三式:通变之术------模板特化与非类型参数
大乘修士不仅能通用施法,更能针对特殊情况(如对阵上古妖兽)调整法诀;模板特化允许为特定类型定制模板实现,非类型参数则能将常量作为参数传入模板,让通用代码更灵活。
3.1 法诀变体:模板特化
当通用模板对某类类型不适用(如指针类型需要特殊处理),可通过特化定义专属逻辑,如同对魔气侵染的法器需用净化版法诀。
cpp
#include <iostream>
#include <string>
using namespace std;
// 通用模板:打印物品(直接输出)
template<typename T>
void 显形术(T 物品) {
cout << "显形:" << 物品 << endl;
}
// 特化版本:针对指针类型(输出指针指向的内容,而非地址)
template<> // 特化声明
void 显形术<int*>(int* 指针物品) { // 明确指定T为int*
if (指针物品 == nullptr) {
cout << "显形:空指针(无物品)" << endl;
} else {
cout << "显形(指针):" << *指针物品 << endl;
}
}
// 特化版本:针对string*类型
template<>
void 显形术<string*>(string* 指针物品) {
if (指针物品 == nullptr) {
cout << "显形:空指针(无法器)" << endl;
} else {
cout << "显形(法器指针):" << *指针物品 << endl;
}
}
int main() {
int 灵石 = 500;
显形术(灵石); // 调用通用模板:显形:500
int* 灵石指针 = &灵石;
显形术(灵石指针); // 调用int*特化:显形(指针):500
string 法器 = "青竹蜂云剑";
string* 法器指针 = &法器;
显形术(法器指针); // 调用string*特化:显形(法器指针):青竹蜂云剑
return 0;
}
3.2 定数法印:非类型模板参数
模板参数不仅可以是类型,还可以是常量(如整数、指针、引用),称为非类型参数,如同法诀中嵌入固定符文数量,让模板更具针对性。
cpp
#include <iostream>
using namespace std;
// 非类型模板参数:数组大小N(常量int)
template<typename T, int N> // T是类型参数,N是非类型参数
class 固定容量储物袋 {
private:
T 物品[N]; // 数组大小由N指定
int 已存数量 = 0;
public:
// 存入物品
void 存入(T 新物品) {
if (已存数量 >= N) {
cout << "储物袋已满(容量" << N << ")" << endl;
return;
}
物品[已存数量++] = 新物品;
cout << "存入第" << 已存数量 << "个物品" << endl;
}
// 打印所有物品
void 清点() {
cout << "储物袋(容量" << N << ")内有" << 已存数量 << "个物品:";
for (int i = 0; i < 已存数量; i++) {
cout << 物品[i] << " ";
}
cout << endl;
}
};
int main() {
// 实例1:容量为3的int型储物袋
固定容量储物袋<int, 3> 小袋;
小袋.存入(10);
小袋.存入(20);
小袋.存入(30);
小袋.存入(40); // 容量3,提示已满
小袋.清点(); // 10 20 30
// 实例2:容量为2的string型储物袋
固定容量储物袋<string, 2> 法袋;
法袋.存入("破障丹");
法袋.存入("传音符");
法袋.清点(); // 破障丹 传音符
return 0;
}
⚠️ 避坑点:
- 非类型参数必须是编译期常量(如
3
、sizeof(int)
),不能是变量(如同法印的符文数量不能临时更改); - 特化模板时需匹配所有参数(包括非类型参数),如
template<> class 固定容量储物袋<int, 5> { ... }
是针对T=int
且N=5
的特化。
修炼总结与进阶指引
- 修练目标:大乘期的核心是"通用与适配":函数模板打破函数的类型绑定,类模板实现容器的万物兼容,特化与非类型参数则让通用代码兼顾特殊场景。唯有掌握模板的"不变中含万变"之道,方能在渡劫期从容应对各类数据结构的挑战,真正实现"一法通,万法通"的编程境界。
- 大乘检验 :实现一个通用的"修仙者排行榜"类模板,支持存储任意类型的修仙者(如
struct 低阶修士
、struct 高阶修士
),能通过模板函数排序,并对nullptr
指针(表示失踪修士)进行特化处理,即为大乘功成; - 避坑要点 :
- 模板代码不能分离到
.cpp
文件(链接时会找不到实例化代码),需全部写在头文件或.hpp
文件中; - 过度使用模板会导致代码膨胀(每个实例化类型都会生成独立代码),需平衡通用性与效率;
- 模板代码不能分离到
- 下一境界预告:大乘期让你学会了如何使用模板来编写通用的代码。接下来,你将进入渡劫期,学习如何管理动态内存和使用智能指针来避免内存泄漏和悬垂指针,这将使你的程序更加健壮和安全。
境界9:渡劫期 ------ 动态内存与智能指针
渡劫期是修士突破凡尘桎梏的关键考验:此前境界的内存(如数组、结构体)多为编译时固定大小(如同预先开凿的洞府),而渡劫期需应对"动态变化的天地灵气"------程序运行时根据需求灵活分配/释放内存(如用户输入决定数据量)。这如同渡雷劫,稍有不慎便会因"内存泄漏"(灵气滞留不散)、"悬垂指针"(灵气溃散后仍强行操控)而"身死道消"(程序崩溃)。唯有掌握动态内存管理 与智能指针这两门"渡劫神通",方能稳渡此劫,为飞升真仙期(更复杂的资源管理)铺路。
核心目标 | 关键技能 | 修炼成果 |
---|---|---|
识劫:动态内存本质 | new/delete操作、动态数组、内存泄漏原理 | 理解"运行时内存分配"的必要性与风险 |
御劫:智能指针护道 | unique_ptr独占所有权、shared_ptr共享计数、weak_ptr破环引用 | 自动管理内存,杜绝泄漏与悬垂指针 |
渡劫:实战综合运用 | 动态结构体数组、智能指针嵌套、资源池设计 | 安全处理复杂场景的动态资源(如动态加载的修士列表) |
第一式:雷劫降临------动态内存的必要性与风险
炼气期到元婴期的内存(变量、数组)均在编译时确定大小(如int a[10]
),但若需根据运行时数据(如用户输入的修士数量)分配空间,固定大小的内存便如同"容量固定的丹炉",无法容纳超额灵气。动态内存(new
/delete
)正是为"按需开辟洞府"而生,但也伴随着"雷劫"------若忘记关闭洞府(释放内存),或重复关闭(重复释放),便会引发灾难。
1.1 开府辟地:new
与delete
的基础用法
心法 :new
在堆区动态分配内存并返回地址(如同开辟临时洞府),delete
释放对应内存(关闭洞府),二者必须成对出现。
cpp
#include <iostream>
using namespace std;
int main() {
// 动态分配单个int(开辟1个修士的临时修为记录)
int* 临时修为 = new int; // new返回int*,指向堆区的int内存
*临时修为 = 89; // 写入数据
cout << "临时修为:" << *临时修为 << endl; // 输出89
// 动态分配数组(根据输入决定修士数量)
int 弟子数量;
cout << "请输入弟子数量:";
cin >> 弟子数量;
int* 弟子灵力 = new int[弟子数量]; // 动态数组,大小为弟子数量
// 赋值并使用
for (int i = 0; i < 弟子数量; i++) {
弟子灵力[i] = 50 + i * 10; // 初始化灵力值
}
cout << "最后一名弟子灵力:" << 弟子灵力[弟子数量 - 1] << endl;
// 释放内存(关键!否则内存泄漏)
delete 临时修为; // 释放单个变量
delete[] 弟子灵力; // 释放数组(必须加[])
return 0;
}
📌 雷劫预警:
- 漏写
delete
/delete[]
:内存被永久占用(内存泄漏),如同洞府废弃却仍消耗灵气,最终耗尽系统资源; - 重复
delete
:同一内存被释放多次,会导致堆区损坏(程序崩溃),如同强行拆除已坍塌的洞府; - 释放后仍使用指针(悬垂指针):访问已回收的内存,数据随机且危险,如同踏入已关闭的禁制区域。
1.2 劫火炼心:内存泄漏与悬垂指针案例
反例1:内存泄漏(忘记释放)
cpp
void 泄漏演示() {
int* 灵气 = new int[100]; // 分配100个int的内存
// 未写delete[] 灵气; 函数结束后,指针消失但内存未释放,永久泄漏
}
int main() {
while (true) {
泄漏演示(); // 反复调用,内存迅速耗尽,程序崩溃
}
return 0;
}
反例2:悬垂指针(释放后误用)
cpp
int main() {
int* 灵力 = new int(100);
delete 灵力; // 内存已释放
*灵力 = 200; // 错误!访问已释放内存(悬垂指针操作),结果不可预测
return 0;
}
第二式:御劫之术------智能指针(Smart Pointer)
手动管理new
/delete
如同徒手引雷,稍有疏忽便会渡劫失败。C++11引入的智能指针 是"自动护山大阵"------封装了原始指针,能在指针生命周期结束时自动释放内存,从根源上避免泄漏与悬垂指针。
2.1 独占之盾:unique_ptr
(独占所有权)
心法 :unique_ptr
确保同一时间只有一个指针拥有内存所有权(如同独门秘宝,仅归一人所有),销毁时自动释放内存,禁止复制(避免所有权混乱)。
cpp
#include <iostream>
#include <memory> // 智能指针头文件
using namespace std;
struct 修士 {
string 姓名;
int 修为;
修士(string 名, int 力) : 姓名(名), 修为(力) {} // 构造函数
};
int main() {
// 创建unique_ptr,指向动态分配的修士(自动管理内存)
unique_ptr<修士> 韩老魔(new 修士("韩立", 1500));
// 访问成员(用->,同原始指针)
cout << 韩老魔->姓名 << "修为:" << 韩老魔->修为 << endl; // 输出韩立修为:1500
// 转移所有权(只能移动,不能复制,用std::move)
unique_ptr<修士> 韩天尊 = move(韩老魔); // 韩老魔不再拥有所有权(变为空)
if (!韩老魔) { // 空指针判断
cout << "韩老魔已转移所有权" << endl;
}
cout << 韩天尊->姓名 << "当前修为:" << 韩天尊->修为 << endl;
// 函数结束时,韩天尊销毁,自动调用delete释放修士内存(无需手动操作)
return 0;
}
💡 实战技巧 :用make_unique
创建更安全(避免异常导致内存泄漏)
cpp
// 推荐:make_unique自动分配内存,比直接new更安全
auto 南宫婉 = make_unique<修士>("南宫婉", 1800); // auto简化类型书写
2.2 共享之阵:shared_ptr
(共享所有权)
心法 :shared_ptr
通过"引用计数"实现多指针共享内存(如同多人共用一处灵脉),每多一个指针指向内存,计数+1;指针销毁时计数-1;计数为0时自动释放内存。
cpp
#include <iostream>
#include <memory>
using namespace std;
struct 功法 {
string 名称;
功法(string 名) : 名称(名) {
cout << 名称 << "被创建" << endl;
}
~功法() { // 析构函数,释放时调用
cout << 名称 << "被销毁" << endl;
}
};
int main() {
// 创建shared_ptr,引用计数=1
shared_ptr<功法> 青元剑诀(new 功法("青元剑诀"));
cout << "当前引用计数:" << 青元剑诀.use_count() << endl; // 输出1
// 复制指针,引用计数=2
shared_ptr<功法> 韩老魔副本 = 青元剑诀;
cout << "复制后计数:" << 青元剑诀.use_count() << endl; // 输出2
// 副本销毁(作用域结束),计数=1
{
shared_ptr<功法> 临时副本 = 青元剑诀;
cout << "临时副本计数:" << 青元剑诀.use_count() << endl; // 输出3
} // 临时副本销毁,计数变回2
// 主指针销毁,计数=0,自动释放内存(调用析构函数)
return 0;
}
/* 输出:
青元剑诀被创建
当前引用计数:1
复制后计数:2
临时副本计数:3
青元剑诀被销毁
*/
💡 推荐用法 :用make_shared
创建(内存分配更高效)
cpp
auto 小衍术 = make_shared<功法>("小衍术"); // 比直接new更优
2.3 破环之针:weak_ptr
(解决循环引用)
劫点 :shared_ptr
的循环引用会导致计数无法归零(如同两个阵法相互绑定,无法自行解除),内存永久泄漏。weak_ptr
是"破环针"------不增加引用计数,仅观察内存是否存活。
问题案例:循环引用导致泄漏
cpp
#include <memory>
using namespace std;
struct 修士; // 前置声明
struct 法宝 {
shared_ptr<修士> 主人; // 法宝引用主人
~法宝() { cout << "法宝销毁" << endl; }
};
struct 修士 {
shared_ptr<法宝> 本命法宝; // 修士引用法宝
~修士() { cout << "修士销毁" << endl; }
};
int main() {
shared_ptr<修士> 韩立(new 修士);
shared_ptr<法宝> 青竹蜂云剑(new 法宝);
韩立->本命法宝 = 青竹蜂云剑; // 修士引用法宝(计数=2)
青竹蜂云剑->主人 = 韩立; // 法宝引用修士(计数=2)
// 函数结束,韩立和青竹蜂云剑销毁,计数各减1(变为1)
// 循环引用导致计数无法归零,析构函数不执行(内存泄漏)
return 0;
}
/* 输出:(无任何销毁信息,内存泄漏) */
解决方案:用weak_ptr
打破循环
cpp
struct 法宝 {
weak_ptr<修士> 主人; // 改为weak_ptr,不增加计数
~法宝() { cout << "法宝销毁" << endl; }
};
struct 修士 {
shared_ptr<法宝> 本命法宝;
~修士() { cout << "修士销毁" << endl; }
};
int main() {
shared_ptr<修士> 韩立(new 修士);
shared_ptr<法宝> 青竹蜂云剑(new 法宝);
韩立->本命法宝 = 青竹蜂云剑; // 法宝计数=2
青竹蜂云剑->主人 = 韩立; // weak_ptr不增加修士计数(仍为1)
// 函数结束:韩立销毁(计数0,调用析构)→ 法宝计数1→0(调用析构)
return 0;
}
/* 输出:
修士销毁
法宝销毁
*/
📌 weak_ptr
用法 :需通过lock()
获取shared_ptr
才能访问数据(确保内存存活)
cpp
if (auto 主人 = 青竹蜂云剑->主人.lock()) { // lock()返回shared_ptr,若内存已释放则为空
cout << "法宝有主人" << endl;
} else {
cout << "法宝无主" << endl;
}
第三式:渡劫实战------动态资源综合管理
结合动态结构体数组与智能指针,实现一个"动态修士管理系统",模拟渡劫期灵活处理未知数量的修士数据,并确保内存安全。
cpp
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
// 修士结构体
struct 修士 {
string 姓名;
int 修为;
修士(string 名, int 力) : 姓名(名), 修为(力) {}
};
// 宗门类:管理动态修士列表
class 宗门 {
private:
// 用vector存储shared_ptr,动态管理修士(自动扩容,智能指针自动释放)
vector<shared_ptr<修士>> 弟子列表;
public:
// 添加弟子(动态创建,用shared_ptr管理)
void 添加弟子(string 名, int 力) {
弟子列表.push_back(make_shared<修士>(名, 力));
}
// 展示所有弟子(用weak_ptr遍历,不影响计数)
void 展示弟子() {
cout << "\n宗门弟子列表:" << endl;
for (const auto& 弟子指针 : 弟子列表) {
// 用weak_ptr观察,不增加计数
weak_ptr<修士> 观察指针 = 弟子指针;
if (auto 有效指针 = 观察指针.lock()) {
cout << 有效指针->姓名 << ",修为:" << 有效指针->修为 << endl;
}
}
}
// 移除修为低于阈值的弟子(直接 erase,shared_ptr自动释放内存)
void 清理弟子(int 最低修为) {
auto it = 弟子列表.begin();
while (it != 弟子列表.end()) {
if ((*it)->修为 < 最低修为) {
cout << "移除弟子:" << (*it)->姓名 << endl;
it = 弟子列表.erase(it); // erase后,shared_ptr销毁,内存释放
} else {
++it;
}
}
}
};
int main() {
宗门 黄枫谷;
// 动态添加弟子(数量运行时确定)
黄枫谷.添加弟子("韩立", 1500);
黄枫谷.添加弟子("厉飞雨", 800);
黄枫谷.添加弟子("陈巧倩", 950);
黄枫谷.展示弟子();
// 清理修为低于1000的弟子
黄枫谷.清理弟子(1000);
黄枫谷.展示弟子();
// 程序结束时,宗门销毁→弟子列表销毁→所有shared_ptr销毁→修士内存自动释放
return 0;
}
/* 输出:
宗门弟子列表:
韩立,修为:1500
厉飞雨,修为:800
陈巧倩,修为:950
移除弟子:厉飞雨
移除弟子:陈巧倩
宗门弟子列表:
韩立,修为:1500
*/
渡劫总结与飞升指引
- 修练目标:渡劫期的核心是"掌控动态变化":动态内存赋予程序灵活应对未知需求的能力,而智能指针则是驾驭这种能力的"护身符"。唯有做到"内存自动生灭,指针不悬不漏",方能平稳渡过此劫,真正踏入C++的"仙途"。
- 渡劫检验:能独立实现"动态拍卖行系统"(用智能指针管理动态拍品数组,支持拍品添加/删除/竞价,确保无内存泄漏),则渡劫功成;
- 避坑真言 :
- 优先使用
make_unique
/make_shared
,避免直接用new
初始化智能指针(减少异常导致的泄漏风险); - 禁用
unique_ptr
的复制,必要时用move
转移所有权; - 警惕
shared_ptr
的循环引用,及时用weak_ptr
破解;
- 优先使用
- 下一境界预告:渡劫期让你学会了如何管理动态内存和使用智能指针。接下来,你将进入真仙期,学习如何处理程序运行时的错误和异常,这将使你的程序更加稳定和可靠。