局部类="定义在函数体里面的类"——它只在那个函数里存在,出了函数就没人认识它了。它有两条硬限制:① 成员函数必须写在类里面(因为函数外面没法引用它,也就没法写 类名::函数名);② 不能有静态数据成员(10.4.3 那种)。它几乎不用——现代 C++ 里,需要"函数内临时的小帮手"时,用 lambda 表达式更顺手。本篇的目标是"认得它",不必练熟。
它是"作用域层层嵌套"这条线的最内一层:全局 → 类(10.4.5 嵌套类)→ 函数 → 块(6.4)。局部类把"类"放进了函数的作用域里。这个特性现在基本被 lambda 替代(C++11 引入,1.1.1 提过一嘴),所以它在本章是"知识完整性"的需要,不是"实用技能"。
一、写法
把类的定义,写在一个函数的里面:
#include <iostream>
using namespace std;
void demo() {
class Local { // ★ 类定义写在函数体内
private:
int v;
public:
Local(int x) : v(x) { }
void show() { cout << "Local: " << v << endl; }
}; // ← 分号别漏
Local a(42); // 在本函数里可以正常用
a.show();
}
int main() {
demo(); // Local: 42
// Local b(1); // ❌ 编译错误:出了 demo() 就没人认识 Local
return 0;
}
作用域规则一句话:局部类只在定义它的那个函数内可见——和函数里的局部变量一个待遇(6.4)。
二、两条硬限制
限制 1:成员函数必须在类里写完
void demo() {
class Local {
public:
void f(); // 只能声明
};
// void Local::f() { } // ❌ 报错:函数外面引用不到 Local
}
原因:Local::f 这种写法要求在"能看见 Local 这个作用域"的地方写。而 Local 只活在 demo() 里,函数外面根本没有 Local 这个名字。所以成员函数只能就地写完(10.1.2 的"写法一")。
限制 2:不能有静态数据成员
void demo() {
class Local {
public:
static int count; // ❌ 报错:局部类里不能有静态数据成员
};
}
原因:静态成员变量需要"类外定义一次"(10.4.3:int Local::count = 0;)——可这个定义该写在哪? 类外没有 Local,函数里又不能给它分配"全程序唯一"的内存。标准干脆禁止。(静态成员函数也是不许的。)
但有两样东西是允许的:
| 允许 | 说明 |
|---|---|
| 静态常量整型成员 | 如 static const int N = 10;——相当于在类里写个常量 |
| 枚举(8.6) | 局部类里可以用 enum 定义常量 |
三、它和"外层函数的局部变量"的关系
又一个反直觉的地方:局部类不能直接访问函数的局部变量。
void demo() {
int outside = 10;
class Local {
public:
void show() {
// cout << outside; // ❌ 报错:抓不到外面的 outside
}
void showBetter(int v) { // ✅ 正确做法:当参数传进来
cout << v << endl;
}
};
Local l;
l.showBetter(outside); // 手动把值送进去
}
为什么:局部类的对象可能比那个局部变量活得更久(比如被复制出去)——如果它能直接引用外层变量,变量早没了,就出悬空问题(6.7.4、7.6.3 讲过的"别返回局部变量的地址"是同一类担忧)。
所以规矩是:要什么,明明白白当参数传进来。 这反而更清楚。
四、为什么它几乎不用
| 原因 | 说明 |
|---|---|
| 限制多 | 不能有静态成员、成员函数必须就地写、抓不到外层变量——写起来处处受缚 |
| 有更好的替代 | C++11 的 lambda 表达式专门干"函数内临时小帮手"这件事,又短又直观 |
| 可读性差 | 函数里塞一个类定义,函数立刻变长,看代码的人得跳着读 |
对照一下(感受差别,不用现在学会 lambda):
// 局部类的写法:一大段
void sortByLocal() {
class Cmp {
public:
bool operator()(int a, int b) const { return a > b; } // 还得懂运算符重载
};
// ... 再用 Cmp 去排序
}
// lambda 的写法:一行(C++11 起)
sort(arr, arr + n, [](int a, int b) { return a > b; });
同一个意思,一个是半屏代码,一个是一行。 这就是它能被替代的原因。
本课的态度:认得它就行——看到别人代码里函数内部有个
class,知道"这是个局部类,只在这个函数里管用",就足够了。
五、动手 Demo:见一次真容
#include <iostream>
using namespace std;
// 一个函数,里面定义一个"只服务本函数"的小类
void reportCard() {
// ── 局部类:只在 reportCard 里存在 ──
class Score {
private:
int value;
char subject[20];
public:
Score(const char *s, int v) : value(v) {
strcpy(subject, s); // 别忘了 <cstring>
}
void show() const {
cout << " " << subject << ":" << value << " 分";
if (value >= 60) cout << "(及格)";
else cout << "(不及格)";
cout << endl;
}
};
cout << "成绩单:" << endl;
Score a("语文", 88);
Score b("数学", 55);
a.show();
b.show();
} // ← Score 在这里"消失"
int main() {
reportCard();
// Score x("英语", 70); // ❌ 出了 reportCard 就不认识 Score
return 0;
}
看到什么算成功:
成绩单:
语文:88 分(及格)
数学:55 分(不及格)
读结果:功能上和"把 Score 写到外面当普通类"没有任何区别。唯一的不同是"它藏在函数里,外面看不见"。
30 秒实验:把 Score 从函数里搬到函数外面(变成普通类),改掉相应写法。你会发现代码更好读、而且能复用——这正是"局部类较少被选择"的直观理由:把它藏起来省的那点事,往往不值当。
六、常见误区
- "局部类和嵌套类是一回事"——不是。嵌套类定义在类里(10.4.5),外面通过
Outer::Inner还能用;局部类定义在函数里,外面彻底用不了。 - "局部类的成员函数可以在类外定义"——不行。因为函数外引用不到这个类名。必须就地写完。
- "局部类可以有静态成员"——不行(静态数据成员、静态成员函数都不许)。唯一例外是
static const整型常量和枚举。 - "局部类能随便访问函数里的局部变量"——不能。要用的值必须当参数传进去(理由见第三节)。
- "不学它就会漏掉重要知识"——不会。它是"知道有这回事"级别的知识;真正要花力气的是构造函数、析构函数、复制构造函数、
this、静态成员这些天天要用的东西。把力气花在常用的地方。
自测三问(点开对答案)
1. 局部类定义在哪儿?作用域有多大?——**定义在函数体(或语句块)里**;**作用域只限于那个函数/块内部**,出了就没人认识了。 2. 局部类有哪两条硬限制?——① **成员函数必须在类里就地写完**(外面没法写 `类名::函数名`,因为引用不到类名);② **不能有静态数据成员**(也没法写类外定义)。 3. 局部类能直接访问函数里的局部变量吗?——**不能**。**必须把值当参数传进来**。原因是局部对象可能活过那个变量,直接引用会出悬空问题。- ☐ 10.5 友元(下一节)
相关笔记
- 嵌套类 —— 上一节:定义在类里的类
- 友元 —— 下一节:给外人一把钥匙
- 变量作用域 —— 全局→类→函数→块,作用域的层层嵌套
- 枚举类型 —— 局部类里少数允许的"常量"手段
- 🔷 C++基础 · 索引 —— 全课路线图