1、资料清单
| 资料 |
内容 |
入口 |
| 教材 |
《C++语言程序设计(第4版)》Chap4,原书页码 114-161 |
抽取 Markdown 下载 |
| 课件 |
Chap4 课件,位于本地郑莉 C++ 资料目录 |
本地资料夹 |
| 例题源代码 |
C++V5 源代码 |
下载 |
| VS2019 工程 |
C++V5 VS2019 solution |
下载 |
本章开始真正进入 C++ 的面向对象部分。主线是对象抽象 → 类的定义 → 访问控制 → 构造/析构 → 复制构造 → 类的组合。
2、颜色标注
| 颜色 |
含义 |
使用位置 |
| 蓝色 |
核心术语、定义 |
类、对象、封装、构造函数 |
| 绿色 |
推荐写法、主线 |
私有数据、公有接口、初始化列表 |
| 黄色 |
易错点、边界条件 |
复制构造、赋值、构造顺序 |
| 红色 |
不建议做法 |
直接暴露数据、用析构函数做普通输出 |
3、学习目标
| 目标 |
要会到什么程度 |
| 类和对象 |
会从现实问题中抽象属性和行为 |
| 访问控制 |
分清 public、private、protected |
| 封装 |
会把数据隐藏起来,只暴露必要接口 |
| 构造函数 |
会用构造函数和初始化列表创建有效对象 |
| 析构函数 |
理解对象销毁时的清理动作 |
| 复制构造函数 |
知道什么时候调用,能和赋值运算区分 |
| 类的组合 |
会在一个类中嵌入另一个类对象 |
| 结构体和联合体 |
知道它们和类的关系,以及现代写法选择 |
4、章节目录
| 小节 |
内容 |
本节关键词 |
| 4.1 |
面向对象程序设计基本特点 |
抽象、封装、继承、多态 |
| 4.2 |
类和对象 |
类定义、对象、成员变量、成员函数 |
| 4.3 |
构造函数和析构函数 |
默认构造、带参构造、复制构造、析构 |
| 4.4 |
类的组合 |
内嵌对象、初始化顺序 |
| 4.5 |
UML 类图 |
类名、属性、操作、访问权限 |
| 4.6 |
结构体和联合体 |
struct、union、数据组织 |
| 4.7 |
个人银行账户管理程序 |
类设计的小型综合应用 |
5、从结构化程序到面向对象程序
Chap3 的函数让代码可以复用,Chap4 的类让数据和操作可以绑定在一起。
1 2 3 4 5 6
| struct PointData { double x; double y; };
double distanceFromOrigin(PointData p);
|
这仍然是“数据在一边,函数在一边”。类的做法是把对象的状态和行为放到同一个抽象中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class Point { public: Point(double xValue, double yValue) : x(xValue), y(yValue) {}
double getX() const { return x; }
double getY() const { return y; }
private: double x; double y; };
|
| 对比 |
结构化程序 |
面向对象程序 |
| 思考方式 |
函数如何处理数据 |
对象拥有什么状态、能做什么事 |
| 数据位置 |
常在函数外部传来传去 |
成为对象的内部状态 |
| 访问方式 |
外部直接操作数据较常见 |
通过对象接口访问 |
| 维护重点 |
函数流程 |
类的边界与职责 |
类不是为了把代码写复杂,而是为了让数据有边界,让状态修改有入口。也就是:对象管理自己的状态,外部只通过接口和它交互。
6、面向对象的四个基本特点
6.1 抽象
抽象是从具体对象中提取共同特征。
例如描述一只狗,不需要把所有现实细节都放入程序。根据任务需要,可能只保留:
| 属性 |
行为 |
| 年龄 |
设置年龄 |
| 体重 |
获取体重 |
| 名字 |
输出信息 |
程序设计中的抽象不是“越多越好”,而是“刚好能解决问题”。
6.2 封装
封装把数据和操作数据的函数绑定在一起,并通过访问控制隐藏实现细节。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class Dog { public: void setAge(int value) { if (value >= 0) { age = value; } }
int getAge() const { return age; }
private: int age = 0; };
|
这里 age 是私有数据,外部不能随便写入负数,只能通过 setAge() 进入校验逻辑。
6.3 继承
继承用于表达“一类对象是另一类对象的特殊形式”。比如 Student 可以继承 Person。这一章先知道概念即可,后面 Chap7 会详细整理。
6.4 多态
多态表示同一消息作用在不同对象上,可以表现出不同的行为。比如不同图形都有 area(),圆和矩形的面积计算方式不同。后面 Chap8 会详细整理。
7、类和对象
7.1 类的基本写法
1 2 3 4 5 6 7
| class 类名 { public: 公有成员;
private: 私有成员; };
|
例子:
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Rectangle { public: Rectangle(double lengthValue, double widthValue) : length(lengthValue), width(widthValue) {}
double area() const { return length * width; }
private: double length; double width; };
|
创建对象:
1 2
| Rectangle rect(5.0, 4.0); std::cout << rect.area() << std::endl;
|
| 概念 |
说明 |
| 类 |
对同一类对象的抽象描述,是类型 |
| 对象 |
类的具体实例,占有实际存储空间 |
| 数据成员 |
对象保存的状态 |
| 成员函数 |
对象能执行的操作 |
类定义最后的右花括号后面必须有分号:};。这是初学阶段非常常见的小错误。
7.2 public、private、protected
| 访问控制 |
类外是否可访问 |
派生类是否可访问 |
常见用途 |
public |
可以 |
可以 |
类的外部接口 |
private |
不可以 |
不可以直接访问 |
对象内部数据和实现细节 |
protected |
不可以 |
可以 |
留给继承体系使用 |
本章重点先记住:数据成员优先设为 private,成员函数作为 public 接口。
7.3 成员函数的类内定义与类外定义
类内定义:
1 2 3 4 5 6 7 8 9
| class Counter { public: int getValue() const { return value; }
private: int value = 0; };
|
类外定义:
1 2 3 4 5 6 7 8 9 10 11
| class Counter { public: int getValue() const;
private: int value = 0; };
int Counter::getValue() const { return value; }
|
Counter::getValue 中的 :: 是作用域限定符,表示这个函数属于 Counter 类。
8、构造函数和析构函数
8.1 构造函数
构造函数在对象创建时自动调用,用来把对象初始化到可用状态。
1 2 3 4 5 6 7 8 9
| class Student { public: Student(int numberValue, std::string nameValue) : number(numberValue), name(nameValue) {}
private: int number; std::string name; };
|
构造函数特点:
| 特点 |
说明 |
| 名字与类名相同 |
Student(...) |
| 没有返回类型 |
不能写 void |
| 创建对象时自动调用 |
Student s(1, "Rui"); |
| 可以重载 |
不同参数列表对应不同初始化方式 |
8.2 初始化列表
推荐使用初始化列表初始化数据成员。
1 2 3 4 5 6 7 8 9
| class Date { public: Date(int y, int m, int d) : year(y), month(m), day(d) {}
private: int year; int month; int day; };
|
和在构造函数体内赋值相比,初始化列表更直接,也能初始化 const 成员、引用成员和成员对象。
成员实际初始化顺序不是初始化列表里写的顺序,而是数据成员在类中声明的顺序。
8.3 默认构造函数
默认构造函数是不需要实参就能调用的构造函数。
1 2 3 4 5 6 7 8
| class Dog { public: Dog() : age(0), weight(5) {}
private: int age; int weight; };
|
也可以通过默认参数实现:
1 2 3 4 5 6 7 8 9
| class Dog { public: Dog(int initialAge = 0, int initialWeight = 5) : age(initialAge), weight(initialWeight) {}
private: int age; int weight; };
|
8.4 复制构造函数
复制构造函数用一个已有对象初始化一个新对象。
1 2 3 4 5 6 7 8 9 10
| class Point { public: Point(int xValue, int yValue) : x(xValue), y(yValue) {}
Point(const Point& other) : x(other.x), y(other.y) {}
private: int x; int y; };
|
常见触发场景:
| 场景 |
例子 |
| 用对象初始化同类新对象 |
Point b = a; |
| 对象按值传参 |
void f(Point p); |
| 对象按值返回 |
return p; |
复制构造函数的参数通常写成 const 类名&。如果写成按值传参,会为了传参再次调用复制构造函数,形成循环调用。
8.5 析构函数
析构函数在对象生命周期结束时自动调用,负责释放对象持有的资源。
1 2 3 4 5 6
| class FileWrapper { public: ~FileWrapper() { } };
|
析构函数特点:
| 特点 |
说明 |
名字是 ~类名 |
如 ~Dog() |
| 没有返回类型 |
也不能有参数 |
| 不能重载 |
一个类只能有一个析构函数 |
| 自动调用 |
对象离开作用域或被释放时调用 |
本章程序题里有些析构函数只是空函数,保留即可;如果没有资源需要释放,现代 C++ 中可以不显式写析构函数。
9、复制构造和赋值运算的区别
这两个概念非常容易混。
1 2 3 4 5
| Rectangle a(5, 4); Rectangle b = a;
Rectangle c(1, 1); c = a;
|
| 对比 |
复制构造函数 |
赋值运算符 |
| 发生时机 |
新对象创建时 |
已有对象被重新赋值 |
| 是否创建对象 |
是 |
否 |
| 典型形式 |
T b = a; |
b = a; |
| 函数形式 |
T(const T& other) |
T& operator=(const T& other) |
本章只要求先能区分两者,运算符重载后面会继续整理。
10、类的组合
类的组合指一个类中包含另一个类的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Date { public: Date(int y, int m, int d) : year(y), month(m), day(d) {}
private: int year; int month; int day; };
class People { public: People(int numberValue, char sexValue, Date birthdayValue, std::string idValue) : number(numberValue), sex(sexValue), birthday(birthdayValue), id(idValue) {}
private: int number; char sex; Date birthday; std::string id; };
|
这里 People 由一个 Date 对象组合而成。
构造顺序:
| 顺序 |
内容 |
| 1 |
先构造成员对象 |
| 2 |
再执行当前类构造函数体 |
| 3 |
析构时顺序相反 |
11、UML 类图速记
UML 类图常用三层结构:
1 2 3 4 5 6 7 8 9
| +----------------------+ | 类名 | +----------------------+ | - 私有属性 | | + 公有属性 | +----------------------+ | + 公有操作() | | - 私有操作() | +----------------------+
|
| 符号 |
含义 |
+ |
public |
- |
private |
# |
protected |
例如 Dog 类可以表示为:
1 2 3 4 5 6 7 8 9 10 11
| +----------------------+ | Dog | +----------------------+ | - age: int | | - weight: int | +----------------------+ | + getAge(): int | | + setAge(int): void | | + getWeight(): int | | + setWeight(int): void | +----------------------+
|
12、结构体和联合体
12.1 struct
在 C++ 中,struct 和 class 都可以有成员变量、成员函数和构造函数。主要默认访问权限不同:
| 类型 |
默认访问权限 |
class |
private |
struct |
public |
一般可以这样取舍:
| 场景 |
建议 |
| 只是简单数据聚合 |
struct |
| 需要封装和不变式 |
class |
12.2 union
联合体的多个成员共享同一段内存,同一时刻通常只保存其中一种值。
1 2 3 4 5
| union Data { char c; int i; float f; };
|
传统 union 常需要额外记录当前保存的是哪一种类型。现代 C++ 中,更推荐用 std::variant 表达安全的多类型数据。
1 2 3
| #include <variant>
std::variant<char, int, float> data;
|
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
| #include <iostream> #include <string>
class Course { public: Course(std::string nameValue, int hourValue) : name(nameValue), hours(hourValue) { std::cout << "Construct: " << name << '\n'; }
Course(const Course& other) : name(other.name), hours(other.hours) { std::cout << "Copy construct: " << name << '\n'; }
~Course() { std::cout << "Destruct: " << name << '\n'; }
void print() const { std::cout << name << ": " << hours << " hours\n"; }
private: std::string name; int hours; };
void showCourse(Course course) { course.print(); }
int main() { Course cpp("C++", 64); cpp.print();
Course copy = cpp; showCourse(copy);
return 0; }
|
观察点:
| 观察点 |
说明 |
Course cpp(...) |
调用普通构造函数 |
Course copy = cpp |
调用复制构造函数 |
showCourse(copy) |
按值传参,再次调用复制构造函数 |
| 函数结束、对象离开作用域 |
调用析构函数 |
如果类里自己管理动态内存、文件句柄、网络连接等资源,就必须认真处理复制、赋值和析构。这个问题后续会和“深拷贝/浅拷贝”“RAII”一起复盘。
14、课后习题答案
以下答案按现代 C++ 重新整理,程序题均使用 #include <iostream> 和 int main()。
4-1 解释 public 和 private 的作用,公有成员与私有成员有什么区别
public 用来声明公有成员,公有成员构成类的外部接口,类外可以通过对象访问。
private 用来声明私有成员,私有成员只允许本类的成员函数或友元访问,类外不能直接访问。
| 对比 |
public |
private |
| 访问位置 |
类内和类外都可访问 |
通常只能类内访问 |
| 作用 |
提供外部接口 |
隐藏数据和实现细节 |
| 常见内容 |
构造函数、getter、setter、业务操作 |
数据成员、内部辅助函数 |
4-2 protected 关键字有何作用
protected 用来声明保护成员。保护成员在类外不可直接访问,但在派生类成员函数中可以访问。
本章先记住它介于 public 和 private 之间,继承部分到 Chap7 再展开。
4-3 构造函数和析构函数有什么作用
构造函数在对象创建时自动调用,用来初始化对象,让对象一出生就是有效状态。
析构函数在对象销毁前自动调用,用来完成清理工作,例如释放资源、关闭文件等。
| 函数 |
调用时机 |
主要作用 |
| 构造函数 |
对象创建时 |
初始化对象 |
| 析构函数 |
对象生命周期结束时 |
清理对象持有的资源 |
4-4 数据成员可以为公有的吗?成员函数可以为私有的吗
都可以,语法上合法。
但一般设计中,数据成员建议设为 private,通过公有成员函数访问和修改。成员函数可以是 private,常用于类内部的辅助逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| class Account { public: void deposit(double amount) { if (isValidAmount(amount)) { balance += amount; } }
private: bool isValidAmount(double amount) const { return amount > 0; }
double balance = 0.0; };
|
4-5 两个对象的数据成员值可以不同吗
可以。类只是类型模板,每个对象都有自己的一份普通数据成员。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class A { public: explicit A(int value) : a(value) {}
int getA() const { return a; }
private: int a; };
int main() { A a1(10); A a2(20); }
|
4-6 什么叫复制构造函数?何时被调用
复制构造函数是一种特殊构造函数,用已有对象初始化新的同类对象。
1
| ClassName(const ClassName& other);
|
常见调用时机:
| 场景 |
例子 |
| 用一个对象初始化另一个对象 |
A a2 = a1; |
| 函数形参是类对象且按值传参 |
void f(A a); |
| 函数返回值是类对象 |
return a; |
4-7 复制构造函数与赋值运算符有什么不同
复制构造函数用于创建新对象,赋值运算符用于已经存在的对象。
1 2 3 4 5
| A a1; A a2 = a1;
A a3; a3 = a1;
|
核心区别是:复制构造发生在对象创建时,赋值发生在对象已经存在之后。
4-8 定义一个 Dog 类,包含 age、weight 等属性,以及操作方法
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
| #include <iostream>
class Dog { public: Dog(int initialAge = 0, int initialWeight = 5) : age(initialAge), weight(initialWeight) {}
int getAge() const { return age; }
void setAge(int value) { if (value >= 0) { age = value; } }
int getWeight() const { return weight; }
void setWeight(int value) { if (value >= 0) { weight = value; } }
private: int age; int weight; };
int main() { Dog jack(2, 10);
std::cout << "Jack is a dog who is " << jack.getAge() << " years old and " << jack.getWeight() << " pounds weight.\n";
jack.setAge(7); jack.setWeight(20);
std::cout << "Now Jack is " << jack.getAge() << " years old and " << jack.getWeight() << " pounds weight.\n";
return 0; }
|
输出示例:
1 2
| Jack is a dog who is 2 years old and 10 pounds weight. Now Jack is 7 years old and 20 pounds weight.
|
原始抽取答案里 SetWeight 写成了修改 itsAge,这里已经修正为修改 weight。
4-9 设计并测试 Rectangle 类,用左下角与右上角坐标计算面积
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 <cmath> #include <iostream>
class Rectangle { public: Rectangle(double leftValue, double bottomValue, double rightValue, double topValue) : left(leftValue), bottom(bottomValue), right(rightValue), top(topValue) {}
double getWidth() const { return std::abs(right - left); }
double getHeight() const { return std::abs(top - bottom); }
double getArea() const { return getWidth() * getHeight(); }
private: double left; double bottom; double right; double top; };
int main() { Rectangle rectangle(20, 50, 80, 100);
std::cout << "Width: " << rectangle.getWidth() << '\n'; std::cout << "Height: " << rectangle.getHeight() << '\n'; std::cout << "Area: " << rectangle.getArea() << '\n';
return 0; }
|
输出示例:
1 2 3
| Width: 60 Height: 50 Area: 3000
|
4-10 设计一个用于人事管理的 People 类
教材答案里标注此题作为实验选做题,没有直接给出答案。这里补一个完整版本,包含日期类组合、构造函数、析构函数、复制构造函数、内联成员函数和带默认值的成员函数。
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 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| #include <iostream> #include <string>
class Date { public: Date(int yearValue = 2000, int monthValue = 1, int dayValue = 1) : year(yearValue), month(monthValue), day(dayValue) {}
void print() const { std::cout << year << '-' << month << '-' << day; }
private: int year; int month; int day; };
class People { public: People(int numberValue = 0, char sexValue = 'M', Date birthdayValue = Date(), std::string idValue = "") : number(numberValue), sex(sexValue), birthday(birthdayValue), id(idValue) {}
People(const People& other) : number(other.number), sex(other.sex), birthday(other.birthday), id(other.id) {}
~People() = default;
int getNumber() const { return number; }
void setNumber(int value) { number = value; }
void input() { int year; int month; int day;
std::cout << "Number: "; std::cin >> number; std::cout << "Sex(M/F): "; std::cin >> sex; std::cout << "Birthday(year month day): "; std::cin >> year >> month >> day; birthday = Date(year, month, day); std::cout << "ID: "; std::cin >> id; }
void display() const { std::cout << "Number: " << number << '\n'; std::cout << "Sex: " << sex << '\n'; std::cout << "Birthday: "; birthday.print(); std::cout << '\n'; std::cout << "ID: " << id << '\n'; }
private: int number; char sex; Date birthday; std::string id; };
int main() { People p1(1001, 'M', Date(2000, 9, 1), "123456200009010000"); p1.display();
People p2 = p1; std::cout << "\nCopied people:\n"; p2.display();
return 0; }
|
说明:
| 要求 |
对应代码 |
| 构造函数 |
People(...) |
| 析构函数 |
~People() = default; |
| 复制构造函数 |
People(const People& other) |
| 内联成员函数 |
类内定义的 getNumber()、setNumber() |
| 带默认形参 |
People(int numberValue = 0, ...) |
| 聚集/组合 |
People 中包含 Date birthday |
4-11 定义矩形类,有长、宽两个属性,计算面积
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
| #include <iostream>
class Rectangle { public: Rectangle(double lengthValue, double widthValue) : length(lengthValue), width(widthValue) {}
double getArea() const { return length * width; }
double getLength() const { return length; }
double getWidth() const { return width; }
private: double length; double width; };
int main() { double length; double width;
std::cout << "请输入矩形的长度:"; std::cin >> length; std::cout << "请输入矩形的宽度:"; std::cin >> width;
Rectangle rectangle(length, width);
std::cout << "长为 " << rectangle.getLength() << ",宽为 " << rectangle.getWidth() << " 的矩形面积为:" << rectangle.getArea() << '\n';
return 0; }
|
示例:
1 2 3
| 请输入矩形的长度:5 请输入矩形的宽度:4 长为 5,宽为 4 的矩形面积为:20
|
4-12 定义一个 DataType 类,能处理字符型、整型、浮点型三种数据
传统写法可以用 enum 标记当前类型,再用 union 保存数据。
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
| #include <iostream>
class DataType { public: explicit DataType(char value) : type(Character) { data.c = value; }
explicit DataType(int value) : type(Integer) { data.i = value; }
explicit DataType(float value) : type(FloatingPoint) { data.f = value; }
void print() const { switch (type) { case Character: std::cout << "字符型: " << data.c << '\n'; break; case Integer: std::cout << "整型: " << data.i << '\n'; break; case FloatingPoint: std::cout << "浮点型: " << data.f << '\n'; break; } }
private: enum ValueType { Character, Integer, FloatingPoint };
union Value { char c; int i; float f; };
ValueType type; Value data; };
int main() { DataType a('c'); DataType b(12); DataType c(1.44f);
a.print(); b.print(); c.print();
return 0; }
|
输出:
现代 C++ 也可以用 std::variant 简化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| #include <iostream> #include <variant>
class DataType { public: explicit DataType(char value) : data(value) {} explicit DataType(int value) : data(value) {} explicit DataType(float value) : data(value) {}
void print() const { std::visit([](const auto& value) { std::cout << value << '\n'; }, data); }
private: std::variant<char, int, float> data; };
|
教材阶段先掌握 enum + union 的写法即可。
4-13 定义 Circle 类,计算圆面积
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
| #include <iostream>
class Circle { public: explicit Circle(double radiusValue) : radius(radiusValue) {}
double getArea() const { constexpr double pi = 3.14159265358979323846; return pi * radius * radius; }
private: double radius; };
int main() { double radius;
std::cout << "请输入圆的半径:"; std::cin >> radius;
Circle circle(radius);
std::cout << "半径为 " << radius << " 的圆面积为:" << circle.getArea() << '\n';
return 0; }
|
示例:
1 2
| 请输入圆的半径:5 半径为 5 的圆面积为:78.5398
|
4-14 定义 Tree 类,grow(int years) 增加树龄,age() 显示树龄
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
| #include <iostream>
class Tree { public: explicit Tree(int initialAge = 0) : ages(initialAge) {}
void grow(int years) { if (years > 0) { ages += years; } }
void age() const { std::cout << "这棵树的年龄为 " << ages << '\n'; }
private: int ages; };
int main() { Tree tree(12);
tree.age(); tree.grow(4); tree.age();
return 0; }
|
输出:
原答案把第二次输出放在析构函数里,程序结束时虽然能看到结果,但不适合作为正常业务输出。这里改成主动调用 age(),逻辑更清楚。
15、本章复盘
| 复盘问题 |
自查答案 |
| 类和对象有什么区别 |
类是类型,对象是实例 |
| 为什么数据成员通常设为私有 |
避免外部绕过校验直接修改状态 |
| 构造函数什么时候调用 |
对象创建时自动调用 |
| 析构函数什么时候调用 |
对象生命周期结束时自动调用 |
| 复制构造和赋值有什么区别 |
一个创建新对象,一个修改已有对象 |
| 类的组合是什么 |
一个类中包含另一个类的对象 |
struct 和 class 默认访问权限有什么不同 |
struct 默认 public,class 默认 private |
下一章 Chap5 会继续整理作用域、可见性、生存期、静态成员、友元和共享数据保护。