1、资料清单

资料 内容 入口
教材 《C++语言程序设计(第4版)》Chap5,原书页码 162-203 抽取 Markdown 下载
课件 Chap5 课件,位于本地郑莉 C++ 资料目录 本地资料夹
例题源代码 C++V5 源代码 下载
VS2019 工程 C++V5 VS2019 solution 下载

本章的主线:Chap4 解决“类怎样封装对象”,Chap5 解决“数据在什么范围内可见、能活多久、怎样共享、怎样保护”。核心顺序是作用域 → 可见性 → 生存期 → 静态成员 → 友元 → const 保护 → 多文件结构

2、颜色标注

颜色 含义 使用位置
蓝色 核心术语、定义 作用域、可见性、生存期、静态成员
绿色 推荐写法、主线 const、初始化、头文件保护
黄色 易错点、边界条件 隐藏外层变量、静态成员定义、友元关系
红色 不建议做法 滥用全局变量、滥用友元、宏替代常量

3、学习目标

目标 要会到什么程度
作用域 分清函数原型、块、类、文件/命名空间作用域
可见性 知道同名标识符如何遮蔽外层标识符
生存期 分清自动、静态、动态对象的生命周期
静态成员 会定义类共享的数据成员和成员函数
友元 知道友元函数、友元类的用途和限制
const 会写常对象、常成员函数、常引用和常指针
多文件结构 会把声明放头文件,把实现放源文件
编译预处理 理解 #include、头文件保护、宏和条件编译

4、章节目录

小节 内容 本节关键词
5.1 标识符的作用域与可见性 作用域、可见性、名称遮蔽
5.2 对象的生存期 自动对象、静态对象、动态对象
5.3 类的静态成员 static 数据成员、static 成员函数
5.4 类的友元 友元函数、友元类
5.5 共享数据的保护 const、常对象、常成员函数
5.6 多文件结构和编译预处理 头文件、源文件、#include、宏

5、作用域、可见性、生存期

这三个词很像,但讨论的问题不同。

概念 讨论的问题 例子
作用域 名字在哪段代码里有效 局部变量只在块内有效
可见性 当前这个位置能不能引用到某个名字 内层同名变量会遮蔽外层变量
生存期 对象从什么时候创建,到什么时候销毁 局部变量离开块后销毁

可以这样记:作用域管“名字能不能用”,生存期管“对象还在不在”。名字不可见时,对象不一定已经消失;对象消失后,名字即使写出来也没有意义。

6、作用域

6.1 函数原型作用域

函数原型中形参名的有效范围只在原型内部。

1
2
3
4
int maxValue(int a, int b);
int maxValue(int x, int y) {
return x > y ? x : y;
}

上面原型中的 ab 和定义中的 xy 不需要一致。函数匹配看类型、顺序和个数,不看原型里的参数名。

6.2 块作用域

花括号内部声明的标识符通常具有块作用域。

1
2
3
4
5
6
7
8
9
10
int main() {
int x = 10;

if (x > 0) {
int y = 20;
std::cout << y << '\n';
}

// std::cout << y; // 错误:y 离开 if 块后不可见
}

6.3 类作用域

类的成员名具有类作用域,可以通过对象、指针、引用或作用域限定符访问。

1
2
3
4
5
6
7
8
9
10
11
12
13
class Counter {
public:
void increment() {
++value;
}

int getValue() const {
return value;
}

private:
int value = 0;
};

成员函数内部可以直接访问同一个类的成员 value

6.4 文件作用域和命名空间作用域

定义在所有函数和类之外的名字,具有较大的可见范围。

1
2
3
4
5
int globalCount = 0;

void addOne() {
++globalCount;
}

实际写项目时,不建议随意使用可修改的全局变量。它会让数据来源和修改位置变得很难追踪。

更推荐用命名空间管理一组相关名字:

1
2
3
namespace config {
constexpr int maxUserCount = 100;
}

7、可见性与名称遮蔽

可见性的一般规则:

规则 说明
先声明,后使用 标识符通常要先声明才能引用
同一作用域不可重复声明同名标识符 同一块内不能重复定义 int x
内层同名会遮蔽外层同名 当前优先使用内层名字

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>

int x = 5;
int y = 7;

void myFunction() {
int y = 10;

std::cout << "x from myFunction: " << x << '\n';
std::cout << "y from myFunction: " << y << "\n\n";
}

int main() {
std::cout << "x from main: " << x << '\n';
std::cout << "y from main: " << y << "\n\n";

myFunction();

std::cout << "Back from myFunction!\n\n";
std::cout << "x from main: " << x << '\n';
std::cout << "y from main: " << y << '\n';

return 0;
}

输出:

1
2
3
4
5
6
7
8
9
10
x from main: 5
y from main: 7

x from myFunction: 5
y from myFunction: 10

Back from myFunction!

x from main: 5
y from main: 7

myFunction() 里的局部变量 y 遮蔽了全局变量 y,但没有改变全局变量 y

名称遮蔽不是错误,但容易让代码难读。复盘时看到同名变量,要先判断当前使用的是哪一层作用域里的名字。

8、对象的生存期

8.1 自动生存期

局部非静态对象通常具有自动生存期。进入块时创建,离开块时销毁。

1
2
3
void f() {
int x = 10;
} // x 在这里销毁

8.2 静态生存期

全局对象、命名空间作用域对象、static 局部对象、类的静态数据成员通常具有静态生存期。它们在程序运行期间一直存在。

1
2
3
4
5
void countCall() {
static int count = 0;
++count;
std::cout << count << '\n';
}

每次调用 countCall()count 都不会重新初始化。

8.3 动态生存期

动态对象由程序控制创建和销毁。

1
2
int* p = new int(10);
delete p;

现代 C++ 更推荐用标准库对象和智能指针管理资源,减少手动 new/delete

1
2
3
#include <memory>

auto p = std::make_unique<int>(10);

8.4 临时对象

表达式中可能产生临时对象,它们通常在完整表达式结束后销毁。

1
std::string name = std::string("Rui") + "qing";

临时对象的生命周期和引用绑定有关,后续学到右值引用、移动语义时会继续遇到。

9、类的静态成员

9.1 静态数据成员

普通数据成员是每个对象各有一份,静态数据成员是整个类共享一份。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Cat {
public:
explicit Cat(int ageValue) : age(ageValue) {
++howManyCats;
}

~Cat() {
--howManyCats;
}

static int getHowMany() {
return howManyCats;
}

private:
int age;
static int howManyCats;
};

int Cat::howManyCats = 0;
成员类型 拥有者 份数
普通数据成员 对象 每个对象一份
静态数据成员 全类共享一份

在 C++17 之前,类内声明的静态数据成员通常还需要在类外定义一次。否则可能出现链接错误。

C++17 之后可以使用 inline static

1
2
3
4
class Cat {
private:
inline static int howManyCats = 0;
};

教材阶段先掌握“类内声明、类外定义”的传统写法。

9.2 静态成员函数

静态成员函数属于类,不属于某个具体对象。

1
2
3
4
5
6
7
8
9
class Counter {
public:
static int getCount() {
return count;
}

private:
static int count;
};

调用方式:

1
Counter::getCount();

静态成员函数没有 this 指针,不能直接访问普通数据成员。

1
2
3
4
5
6
7
8
9
class Example {
public:
static void f() {
// value = 10; // 错误:static 函数没有具体对象
}

private:
int value = 0;
};

10、友元

友元是 C++ 给封装开的一个“受控入口”。被声明为友元的函数或类,可以访问该类的私有成员和保护成员。

10.1 友元函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Boat;

class Car {
public:
explicit Car(int weightValue) : weight(weightValue) {}

friend int totalWeight(const Car& car, const Boat& boat);

private:
int weight;
};

class Boat {
public:
explicit Boat(int weightValue) : weight(weightValue) {}

friend int totalWeight(const Car& car, const Boat& boat);

private:
int weight;
};

int totalWeight(const Car& car, const Boat& boat) {
return car.weight + boat.weight;
}

totalWeight() 不是 CarBoat 的成员函数,但它能访问二者私有成员。

10.2 友元类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Fuel;

class Engine {
friend class Fuel;

private:
int powerLevel = 0;
};

class Fuel {
public:
void boost(Engine& engine) {
engine.powerLevel += 10;
}
};

FuelEngine 的友元类,所以 Fuel 的成员函数可以访问 Engine 的私有成员。

10.3 友元关系的限制

特点 说明
不具有交换性 AB 的友元,不代表 BA 的友元
不具有传递性 A 友元 BB 友元 C,不代表 A 友元 C
不能被继承 基类的友元关系不会自动给派生类

友元能绕过访问控制。它适合表达两个类之间确实紧密协作的情况,不适合当作“嫌 getter/setter 麻烦”的捷径。

11、共享数据的保护

共享数据越多,越要控制谁能改、谁只能看。C++ 里最基础的保护工具就是 const

11.1 常对象

常对象创建后不能调用会修改对象状态的成员函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Date {
public:
Date(int y, int m, int d) : year(y), month(m), day(d) {}

void print() const {
std::cout << year << '-' << month << '-' << day << '\n';
}

private:
int year;
int month;
int day;
};

const Date birthday(2000, 9, 1);
birthday.print();

11.2 常成员函数

成员函数后面的 const 表示它不会修改对象的可观察状态。

1
2
3
int getValue() const {
return value;
}

常对象只能调用常成员函数。

11.3 常数据成员

常数据成员必须通过初始化列表初始化。

1
2
3
4
5
6
7
8
9
class Student {
public:
Student(int idValue, std::string nameValue)
: id(idValue), name(nameValue) {}

private:
const int id;
std::string name;
};

11.4 常引用和常指针

常引用常用于避免复制,同时保证函数不修改实参。

1
2
3
void printName(const std::string& name) {
std::cout << name << '\n';
}

指针里的 const 要看位置:

1
2
const int* p1 = &x; // 指向常量的指针,不能通过 p1 修改 x
int* const p2 = &x; // 指针常量,p2 不能改指向,但能改 *p2
写法 含义
const int* p 不能通过 p 改所指对象
int const* p const int* p 等价
int* const p p 自己不能改指向
const int* const p 指向和所指内容都不能通过 p

12、多文件结构和编译预处理

12.1 头文件和源文件

常见组织方式:

文件 放什么
.h / .hpp 类声明、函数声明、常量声明
.cpp 函数实现、类成员函数实现
main.cpp 程序入口和流程组织

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// counter.h
#ifndef COUNTER_H
#define COUNTER_H

class Counter {
public:
void add();
int getValue() const;

private:
int value = 0;
};

#endif
1
2
3
4
5
6
7
8
9
10
// counter.cpp
#include "counter.h"

void Counter::add() {
++value;
}

int Counter::getValue() const {
return value;
}
1
2
3
4
5
6
7
8
9
10
// main.cpp
#include <iostream>
#include "counter.h"

int main() {
Counter counter;
counter.add();
std::cout << counter.getValue() << '\n';
return 0;
}

12.2 头文件保护

头文件保护防止同一个头文件被重复包含。

1
2
3
4
5
6
#ifndef COUNTER_H
#define COUNTER_H

// declarations

#endif

也常见:

1
#pragma once

教材阶段优先掌握 #ifndef / #define / #endif

12.3 宏与常量

传统 C/C++ 常用宏定义常量:

1
#define MAX_COUNT 100

现代 C++ 更推荐:

1
constexpr int maxCount = 100;

constexpr 有类型检查,调试和作用域管理都更友好。

13、本章代码复盘

下面这个程序把静态成员、常成员函数、对象生存期和共享数据保护串起来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <iostream>
#include <string>

class BankAccount {
public:
BankAccount(std::string ownerValue, double balanceValue)
: owner(ownerValue), balance(balanceValue), accountId(nextId++) {
++accountCount;
}

BankAccount(const BankAccount& other)
: owner(other.owner), balance(other.balance), accountId(nextId++) {
++accountCount;
}

~BankAccount() {
--accountCount;
}

void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}

double getBalance() const {
return balance;
}

int getAccountId() const {
return accountId;
}

static int getAccountCount() {
return accountCount;
}

private:
std::string owner;
double balance;
const int accountId;
static int accountCount;
static int nextId;
};

int BankAccount::accountCount = 0;
int BankAccount::nextId = 1001;

int main() {
BankAccount a("Rui", 1000.0);
BankAccount b = a;

a.deposit(200.0);

std::cout << "a id: " << a.getAccountId()
<< ", balance: " << a.getBalance() << '\n';
std::cout << "b id: " << b.getAccountId()
<< ", balance: " << b.getBalance() << '\n';
std::cout << "accounts: "
<< BankAccount::getAccountCount() << '\n';

return 0;
}

观察点:

观察点 说明
accountCount 静态数据成员,所有对象共享
nextId 静态数据成员,用于生成编号
accountId 常数据成员,只能初始化,不能后续赋值
getBalance() const 常成员函数,只读访问对象
复制构造函数 复制账户时生成新的 accountId

14、课后习题答案

以下答案按现代 C++ 重新整理,原答案中的老式 iostream.hvoid main() 和明显笔误已经修正。

5-1 什么叫作用域?有哪几种类型的作用域

作用域是标识符在程序文本中有效的范围。

常见作用域:

作用域 说明
函数原型作用域 函数原型中参数名的有效范围
块作用域 {} 内声明的局部名字
类作用域 类成员名在类内有效
文件/命名空间作用域 函数和类外声明的名字

5-2 什么叫可见性?可见性的一般规则是什么

可见性讨论的是在程序某个位置能不能引用到某个标识符。

一般规则:

规则 说明
先声明,后引用 使用前要能看到声明
同一作用域不能重复声明同名标识符 避免名称冲突
内层同名标识符会遮蔽外层 当前作用域优先级更高

5-3 观察程序输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>

void myFunction();

int x = 5;
int y = 7;

int main() {
std::cout << "x from main: " << x << '\n';
std::cout << "y from main: " << y << "\n\n";

myFunction();

std::cout << "Back from myFunction!\n\n";
std::cout << "x from main: " << x << '\n';
std::cout << "y from main: " << y << '\n';

return 0;
}

void myFunction() {
int y = 10;

std::cout << "x from myFunction: " << x << '\n';
std::cout << "y from myFunction: " << y << "\n\n";
}

输出:

1
2
3
4
5
6
7
8
9
10
x from main: 5
y from main: 7

x from myFunction: 5
y from myFunction: 10

Back from myFunction!

x from main: 5
y from main: 7

解释:myFunction() 中定义了局部变量 y,它遮蔽了全局变量 yx 没有被局部定义遮蔽,所以访问的是全局 x

5-4 怎样允许 Fuel 成员访问 Engine 中的私有和保护成员

可以把 Fuel 声明为 Engine 的友元类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>

class Fuel;

class Engine {
friend class Fuel;

public:
Engine() : powerLevel(0) {}

int getPowerLevel() const {
return powerLevel;
}

private:
int powerLevel;
};

class Fuel {
public:
void boost(Engine& engine) {
engine.powerLevel += 10;
}
};

int main() {
Engine engine;
Fuel fuel;

fuel.boost(engine);

std::cout << engine.getPowerLevel() << '\n';

return 0;
}

输出:

1
10

5-5 什么叫静态数据成员?它有何特点

静态数据成员是用 static 声明的类成员,属于类本身,而不是某个对象。

特点:

特点 说明
全类共享一份 所有对象访问同一个静态数据成员
可用于对象间共享数据 例如统计对象数量
通常需要类外定义 int Cat::howManyCats = 0;
受访问控制影响 可以是 privatepublicprotected

5-6 什么叫静态函数成员?它有何特点

静态成员函数是用 static 声明的成员函数,属于类本身。

特点:

特点 说明
可通过类名调用 Cat::getHowMany()
没有 this 指针 不绑定具体对象
只能直接访问静态成员 不能直接访问普通数据成员
可作为类级别接口 例如查询对象总数

5-7 定义 Cat 类,用静态成员记录对象个数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <iostream>
#include <memory>
#include <vector>

class Cat {
public:
explicit Cat(int ageValue) : age(ageValue) {
++howManyCats;
}

~Cat() {
--howManyCats;
}

int getAge() const {
return age;
}

void setAge(int value) {
if (value >= 0) {
age = value;
}
}

static int getHowMany() {
return howManyCats;
}

private:
int age;
static int howManyCats;
};

int Cat::howManyCats = 0;

void printCatCount() {
std::cout << "There are " << Cat::getHowMany()
<< " cats alive!\n";
}

int main() {
constexpr int maxCats = 5;
std::vector<std::unique_ptr<Cat>> cats;

for (int i = 0; i < maxCats; ++i) {
cats.push_back(std::make_unique<Cat>(i));
printCatCount();
}

while (!cats.empty()) {
cats.pop_back();
printCatCount();
}

return 0;
}

输出:

1
2
3
4
5
6
7
8
9
10
There are 1 cats alive!
There are 2 cats alive!
There are 3 cats alive!
There are 4 cats alive!
There are 5 cats alive!
There are 4 cats alive!
There are 3 cats alive!
There are 2 cats alive!
There are 1 cats alive!
There are 0 cats alive!

5-8 什么叫友元函数?什么叫友元类

友元函数是用 friend 在类中声明的非成员函数,它可以访问该类的私有成员和保护成员。

友元类是用 friend class 声明的类,它的成员函数都可以访问相应类的私有成员和保护成员。

类型 说明
友元函数 某个外部函数获得访问权限
友元类 某个类整体获得访问权限

5-9 友元关系是否具有交换性、传递性、继承性

都不具有。

判断 答案 原因
A 是类 B 的友元,类 B 是类 A 的友元吗 不是 友元关系不具有交换性
B 是类 C 的友元,类 C 是类 A 的友元吗 不是 友元关系不具有传递性
D 是类 A 的派生类,类 D 是类 B 的友元吗 不是 友元关系不能被继承

更准确地说,友元关系只在声明它的那一个类中生效,不会自动扩散。

5-10 静态成员变量可以为私有的吗?声明一个私有静态整型成员变量

可以。

1
2
3
4
5
6
class Example {
private:
static int value;
};

int Example::value = 0;

静态成员同样受访问控制影响,设为 private 后只能通过类的成员函数或友元访问。

5-11 在一个文件中定义全局变量 n,另一个文件中定义函数 fn1()

可以用 extern 声明跨文件共享的全局变量。

1
2
3
4
5
6
7
// fn1.h
#ifndef FN1_H
#define FN1_H

void fn1();

#endif
1
2
3
4
5
6
7
8
// fn1.cpp
#include "fn1.h"

extern int n;

void fn1() {
n = 30;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// main.cpp
#include <iostream>
#include "fn1.h"

int n = 0;

int main() {
n = 20;
fn1();

std::cout << "n 的值为 " << n << '\n';

return 0;
}

输出:

1
n 的值为 30

这道题是为了理解 extern 和多文件共享变量。实际项目里可修改全局变量要谨慎使用。

5-12 在 fn1() 中定义静态变量 n,主函数调用十次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>

void fn1() {
static int n = 0;
++n;

std::cout << "n 的值为 " << n << '\n';
}

int main() {
for (int i = 0; i < 10; ++i) {
fn1();
}

return 0;
}

输出:

1
2
3
4
5
6
7
8
9
10
n 的值为 1
n 的值为 2
n 的值为 3
n 的值为 4
n 的值为 5
n 的值为 6
n 的值为 7
n 的值为 8
n 的值为 9
n 的值为 10

解释:static int n = 0; 只初始化一次,函数调用结束后 n 不销毁,下一次调用继续使用上一次的值。

5-13 定义类 XYZ 和函数 h(X*),练习友元函数与友元类

一个更完整的多文件写法如下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// my_x_y_z.h
#ifndef MY_X_Y_Z_H
#define MY_X_Y_Z_H

class X;

class Y {
public:
void g(X* x);
};

class Z;

class X {
public:
X() : i(0) {}

int getValue() const {
return i;
}

friend void h(X* x);
friend void Y::g(X* x);
friend class Z;

private:
int i;
};

class Z {
public:
void f(X* x) {
x->i += 5;
}
};

void h(X* x);

#endif
1
2
3
4
5
6
7
8
9
10
// my_x_y_z.cpp
#include "my_x_y_z.h"

void Y::g(X* x) {
++x->i;
}

void h(X* x) {
x->i += 10;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// main.cpp
#include <iostream>
#include "my_x_y_z.h"

int main() {
X x;
Y y;
Z z;

y.g(&x);
z.f(&x);
h(&x);

std::cout << x.getValue() << '\n';

return 0;
}

输出:

1
16

这里 Y::g()i1Z::f()i5h()i10

原答案中的 x->i =+10; 实际是把 i 赋值为正 10,不是加 10。这里已经改成 x->i += 10;

5-14 定义 BoatCar,用友元函数计算总重量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>

class Boat;

class Car {
public:
explicit Car(int weightValue) : weight(weightValue) {}

friend int totalWeight(const Car& car, const Boat& boat);

private:
int weight;
};

class Boat {
public:
explicit Boat(int weightValue) : weight(weightValue) {}

friend int totalWeight(const Car& car, const Boat& boat);

private:
int weight;
};

int totalWeight(const Car& car, const Boat& boat) {
return car.weight + boat.weight;
}

int main() {
Car car(4);
Boat boat(5);

std::cout << totalWeight(car, boat) << '\n';

return 0;
}

输出:

1
9

5-15 类模板中有静态数据成员,运行中会产生多少个静态变量

类模板的每一个实例化类型都会产生一份对应的静态数据成员。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <iostream>

template <typename T>
class Counter {
public:
Counter() {
++count;
}

static int getCount() {
return count;
}

private:
static int count;
};

template <typename T>
int Counter<T>::count = 0;

int main() {
Counter<int> a;
Counter<int> b;
Counter<double> c;

std::cout << Counter<int>::getCount() << '\n';
std::cout << Counter<double>::getCount() << '\n';

return 0;
}

输出:

1
2
2
1

解释:Counter<int>Counter<double> 是两个不同的实例化类,所以它们分别有自己的 count

15、本章复盘

复盘问题 自查答案
作用域和生存期有什么区别 作用域管名字,生存期管对象
局部静态变量什么时候初始化 第一次执行到定义处时初始化一次
静态成员函数能否直接访问普通成员 不能,因为没有 this 指针
友元关系是否可交换、传递、继承 都不可以
const 成员函数的意义是什么 承诺不修改对象状态,常对象只能调用它
头文件为什么要保护 防止重复包含导致重复声明/定义问题
extern 的作用是什么 声明一个在别处定义的外部链接变量或函数

下一章 Chap6 会进入数组、指针与字符串,也是 C++ 基础里最容易出错、但复盘价值很高的一章。