一、说明

这篇笔记基于《21天学通 C++(第8版)》第九章“类和对象”的核心内容整理而成,但题目、解析和拓展说明均为重新组织后的训练材料。

使用方式建议如下:

  • 先独立做题
  • 每题立刻对答案
  • 重点看解析中的判断逻辑
  • 再用“知识点拓展”补全关联概念

这一套题做完,基本就能把第九章最核心的对象模型、构造析构、复制语义和访问控制理顺。


二、类与对象基础题(1-20)

题 1

题目: 下面哪一项最准确地描述了 C++ 中的类?

A. 一个已经存在于内存中的具体对象
B. 一种定义数据和行为的自定义类型
C. 一种只能保存整数的数据结构
D. 一种专门替代函数的语法

正确答案: B

解析: 类本质上是程序员定义的数据类型,它把数据成员和成员函数组织在一起。

知识点拓展: 类是蓝图,对象才是运行时真正存在的实例。

题 2

题目: 对象与类的关系,最合适的理解是:

A. 对象是类的关键字
B. 类是对象的数组
C. 对象是类在运行时的实例
D. 类和对象完全没有区别

正确答案: C

解析: 类负责定义结构,对象是根据这种定义创建出来的具体实体。

知识点拓展: “实例化”就是从类生成对象的过程。

题 3

题目: 仅仅声明一个类本身,会直接带来什么结果?

A. 自动创建很多对象
B. 直接执行类中的成员函数
C. 告诉编译器这种类型的结构
D. 自动在堆中分配内存

正确答案: C

解析: 类声明主要是类型定义,不会自动执行,也不会自动创建对象。

知识点拓展: 真正占用对象内存的是对象实例,而不是类声明本身。

题 4

题目: 类把数据和操作数据的函数组织在一起,这种思想最接近:

A. 封装
B. 宏替换
C. 强制类型转换
D. 递归

正确答案: A

解析: 封装强调把相关数据和相关行为收拢到同一个逻辑单元中。

知识点拓展: 面向对象里常说的封装、抽象、继承、多态,封装通常是最早接触的一层。

题 5

题目: 下面哪一行代码表示创建了一个 Human 类的对象?

A. class Human;
B. Human firstMan;
C. Human::IntroduceSelf();
D. delete Human;

正确答案: B

解析: Human firstMan; 表示声明了一个类型为 Human 的变量,也就是对象。

知识点拓展: 对象名本质上和普通变量名一样,是一个可被访问的实体名。

题 6

题目: 如果你手里拿的是对象本身,访问其成员应该使用:

A. ->
B. ::
C. .
D. &

正确答案: C

解析: 句点运算符用于通过对象本体访问成员。

知识点拓展: :: 是作用域解析运算符,不用于访问普通对象成员。

题 7

题目: 如果你手里拿的是“指向对象的指针”,访问成员通常使用:

A. .
B. ->
C. ::
D. #

正确答案: B

解析: -> 用于通过对象指针直接访问成员。

知识点拓展: ptr->member 等价于 (*ptr).member

题 8

题目: 表达式 (*firstWoman).IntroduceSelf(); 与下面哪项等价?

A. firstWoman.IntroduceSelf();
B. firstWoman::IntroduceSelf();
C. firstWoman->IntroduceSelf();
D. &firstWoman.IntroduceSelf();

正确答案: C

解析: 先解引用得到对象,再用 . 访问成员,效果与 -> 相同。

知识点拓展: 这背后本质还是“你拿到的是地址还是对象”。

题 9

题目: new Human() 表达式返回的通常是:

A. 一个 Human 对象本体
B. 一个 Human 的引用
C. 指向新建 Human 对象的指针
D. 一个布尔值

正确答案: C

解析: new 返回动态分配对象的地址,因此需要指针接收。

知识点拓展: 对应的资源释放通常要用 delete

题 10

题目: 同一个类创建出来的不同对象,最合理的说法是:

A. 必须保存完全相同的数据
B. 可以拥有相同结构但不同状态
C. 只能存在一个对象
D. 只能在堆上创建

正确答案: B

解析: 类规定结构,对象各自保存自己的成员值,因此状态可以不同。

知识点拓展: 这也是“同一个类可以化身为多个不同对象”的含义。

题 11

题目: 一个类通常可以包含:

A. 只有数据成员
B. 只有成员函数
C. 数据成员和成员函数
D. 只有构造函数

正确答案: C

解析: 类的典型结构就是把属性和操作这些属性的方法放在一起。

知识点拓展: 成员函数也常被称为方法。

题 12

题目: 对于 Human* p = new Human();,使用完后最合理的后续动作是:

A. free(p);
B. delete p;
C. remove p;
D. 什么都不做

正确答案: B

解析: new 分配的对象应使用 delete 释放。

知识点拓展: free 是 C 风格内存管理函数,不和 new 配对。

题 13

题目: 一个完整的类声明结尾通常必须有:

A. 冒号 :
B. 分号 ;
C. 逗号 ,
D. 星号 *

正确答案: B

解析: 类声明和结构声明一样,右大括号后要跟分号。

知识点拓展: 这是很多初学者最常见的语法遗漏之一。

题 14

题目: 对于类中的成员变量,最贴近书中语义的理解是:

A. 类声明时它们就对每个对象都实际存在
B. 只有对象实例化后,这些成员才成为该对象的实际组成部分
C. 它们只在构造函数里存在
D. 它们和对象无关

正确答案: B

解析: 类只是定义模板,对象实例化后才拥有自己的成员数据。

知识点拓展: 每个对象都有自己的成员副本,除非成员是静态的。

题 15

题目: 在面向对象语境里,“方法”通常指的是:

A. 全局变量
B. 成员函数
C. 枚举值
D. 头文件

正确答案: B

解析: 方法就是属于类的函数,也就是成员函数。

知识点拓展: “函数”和“方法”常在不同语境下交替出现,但这里本质是同一类东西。

题 16

题目: 下面关于 Human firstMan;Human* firstWoman = new Human(); 的说法,哪项更正确?

A. 两者都一定在同一块内存区域创建对象
B. 前者通常是局部对象,后者是动态分配对象
C. 前者返回地址,后者返回对象
D. 两者都不需要销毁

正确答案: B

解析: 前者通常是自动对象,后者是通过 new 分配的动态对象。

知识点拓展: 它们的访问方式和生命周期管理方式也会因此不同。

题 17

题目: 如果 Human* p 指向一个有效对象,下面哪项写法正确?

A. p.age = 18;
B. p::age = 18;
C. p->age = 18;
D. &p.age = 18;

正确答案: C

解析: 通过对象指针访问成员,应该使用 ->

知识点拓展: p.age 是把指针当对象用,会出错。

题 18

题目: 下面哪种理解最符合“使用类作为类型”?

A. 类只能当关键字,不能像类型那样用
B. 类可以像 int 一样作为变量类型使用
C. 类只能用于数组
D. 类只能用于函数返回值

正确答案: B

解析: 自定义类本质上也是一种类型,因此可以声明对象变量。

知识点拓展: 这是“自定义类型”最重要的含义之一。

题 19

题目: .-> 的区别,本质上取决于:

A. 是否在头文件里声明
B. 是否使用了 namespace
C. 当前拿到的是对象还是对象指针
D. 成员是公有还是私有

正确答案: C

解析: 这两个运算符的选择取决于访问主体的形态。

知识点拓展: 一旦把这个判断逻辑理顺,相关语法就不会乱。

题 20

题目: 对“类”最恰当的比喻是:

A. 一次函数调用
B. 一条已经执行完的语句
C. 一份对象蓝图
D. 一块固定内存

正确答案: C

解析: 类描述对象的结构和能力,很像设计蓝图。

知识点拓展: 蓝图不会自己跑起来,实例才会。


三、封装与构造函数题(21-40)

题 21

题目:class 中,如果不写访问限定符,成员默认是:

A. public
B. private
C. protected
D. static

正确答案: B

解析: class 的默认访问权限是私有。

知识点拓展: struct 默认则是公有,这是两者最常见差别之一。

题 22

题目: 把成员声明为 private 的直接目的通常是:

A. 让它更快
B. 让它自动变成常量
C. 阻止类外直接访问
D. 让它只能保存整数

正确答案: C

解析: private 的核心作用是限制类外直接读取和修改。

知识点拓展: 真正目标不是“藏”,而是让访问受控。

题 23

题目: 如果成员 age 是私有的,类外通常应通过什么访问它?

A. 随便用 . 读写
B. 友元宏
C. 公有成员函数
D. 只能永远不能访问

正确答案: C

解析: 常规做法是通过公有接口如 GetAge()SetAge() 访问。

知识点拓展: 这让类能够决定“暴露什么”和“怎么暴露”。

题 24

题目: 下列哪项最贴近“数据抽象”的含义?

A. 把所有成员都写成公有
B. 只暴露必要接口,隐藏内部细节
C. 完全不允许类有成员函数
D. 所有对象必须放在堆中

正确答案: B

解析: 抽象的核心是对外隐藏实现,只保留必要的使用入口。

知识点拓展: 封装和抽象密切相关,但抽象更强调“边界”。

题 25

题目:SetAge() 这类函数最合理的价值描述是:

A. 只是为了让代码变长
B. 可以在赋值前做校验
C. 让成员自动变成静态
D. 让对象无法构造

正确答案: B

解析: setter 的重要价值是把非法输入挡在外面。

知识点拓展: 合法状态控制,是封装最实际的工程意义之一。

题 26

题目: 关于构造函数,下面哪项说法正确?

A. 必须有返回值
B. 名字必须与类名相同
C. 只能声明,不能定义
D. 只能有一个

正确答案: B

解析: 构造函数与类同名,且没有返回类型。

知识点拓展: 如果写了返回类型,那它就不再是构造函数了。

题 27

题目: 构造函数通常在什么时候自动被调用?

A. 成员函数返回后
B. 对象创建时
C. delete
D. 编译完成时

正确答案: B

解析: 对象一创建,构造函数就会被调用。

知识点拓展: 这也是它最适合做初始化的原因。

题 28

题目: 什么样的构造函数通常被称为默认构造函数?

A. 返回 void 的构造函数
B. 只能在堆上调用的构造函数
C. 能在不传参数时调用的构造函数
D. 第一个写出来的构造函数

正确答案: C

解析: 只要对象可在无参数情况下创建,就可视为存在默认构造能力。

知识点拓展: 带默认参数的构造函数也可能承担默认构造角色。

题 29

题目: 若类只提供了 Human(int age);,那么下面哪项通常会失败?

A. Human h(20);
B. Human h = Human(20);
C. Human h;
D. Human* p = new Human(20);

正确答案: C

解析: 只有带参构造函数时,默认无参构造通常不可用。

知识点拓展: 这是“没有默认构造函数的类”的典型表现。

题 30

题目: 构造函数重载指的是:

A. 不同类使用同名构造函数
B. 同一个类有多个参数列表不同的构造函数
C. 构造函数和析构函数同名
D. 构造函数可以返回不同类型

正确答案: B

解析: 构造函数可以重载,前提是参数列表不同。

知识点拓展: 这让对象可以有多种创建方式。

题 31

题目: 对成员变量初始化来说,构造函数最适合做的事是:

A. 销毁所有对象
B. 建立对象初始合法状态
C. 复制其他对象
D. 替代 main()

正确答案: B

解析: 构造函数负责让对象一出生就处于合理状态。

知识点拓展: 未初始化的基础类型成员很容易出现垃圾值问题。

题 32

题目: 关于初始化列表,下面哪项理解更正确?

A. 它发生在对象已经完全构造之后
B. 它只是普通赋值语法的另一种写法
C. 它是成员真正初始化的重要机制
D. 它只能用于数组

正确答案: C

解析: 初始化列表用于成员初始化,语义上早于函数体中的赋值。

知识点拓展: 对常量成员、引用成员和成员对象尤其重要。

题 33

题目: 如果构造函数写成 Human(int age = 1);,下列哪项说法正确?

A. 这个类不能无参创建
B. 这个类具备无参创建能力
C. 这个构造函数不是构造函数
D. 这种写法只在 C 中成立

正确答案: B

解析: 参数有默认值,因此 Human h; 也是合法的。

知识点拓展: 一个类本质上只能有一种“默认构造入口”。

题 34

题目: 单参数构造函数如果不是 explicit,可能带来什么现象?

A. 无法构造对象
B. 自动生成析构函数
C. 编译器进行隐式类型转换
D. 所有成员都变公有

正确答案: C

解析: 单参数构造函数常被编译器当作隐式转换通道。

知识点拓展: 这就是 Human h = 10; 这类写法可能成立的原因。

题 35

题目: explicit 关键字最直接的作用是:

A. 禁止析构
B. 阻止隐式转换
C. 强制深复制
D. 让对象变成常量

正确答案: B

解析: explicit 用于让构造行为必须显式写出,避免意外转换。

知识点拓展: 这是单参数构造函数的高频配套关键字。

题 36

题目: 已知 class Human { public: explicit Human(int age); };,那么下面哪项通常非法?

A. Human kid(10);
B. Human kid = Human(10);
C. DoSomething(kid);
D. Human anotherKid = 10;

正确答案: D

解析: explicit 阻止 intHuman 的隐式转换。

知识点拓展: 但显式写出构造过程依然是允许的。

题 37

题目: 析构函数名称的形式通常是:

A. Human()
B. ~Human()
C. delete Human()
D. destruct Human()

正确答案: B

解析: 析构函数以 ~ 加类名的形式命名。

知识点拓展: 析构函数没有参数,也没有返回类型。

题 38

题目: 关于析构函数,下面哪项说法正确?

A. 可以像构造函数一样随意重载
B. 一个类可以有多个析构函数
C. 一个类只能有一个析构函数
D. 析构函数必须手动调用

正确答案: C

解析: 析构函数不能重载,因此每个类最多一个。

知识点拓展: 对自动对象来说,离开作用域时会自动触发析构。

题 39

题目: 析构函数最典型的职责是:

A. 计算对象面积
B. 清理对象拥有的资源
C. 负责对象命名
D. 只用于输出日志

正确答案: B

解析: 析构函数最重要的用途是资源回收,例如动态内存。

知识点拓展: 这正是 RAII 思想的基础之一。

题 40

题目: 如果类中自己拥有动态内存,但没有正确编写析构函数,最大风险通常是:

A. 自动深复制
B. 内存泄漏
C. 构造函数消失
D. 所有成员变成私有

正确答案: B

解析: 默认空析构不会帮你释放手动申请的资源。

知识点拓展: 资源管理不完整,后面复制时问题还会进一步放大。


四、析构、复制与移动题(41-60)

题 41

题目: 当函数形参按值接收一个对象时,最容易触发的是:

A. 析构函数
B. 复制构造函数
C. 友元函数
D. 聚合初始化

正确答案: B

解析: 按值传参意味着实参会被复制到形参对象中。

知识点拓展: 这也是资源类最怕“无意识复制”的地方。

题 42

题目: 一个函数按值返回对象时,典型会涉及:

A. 复制或移动语义
B. 宏展开
C. 继承
D. 虚函数表

正确答案: A

解析: 按值返回对象通常会触发复制构造或移动构造。

知识点拓展: 现代编译器还会做返回值优化,但语义基础仍然重要。

题 43

题目: 对含有原始指针成员的类执行默认复制时,所谓“浅复制”通常复制的是:

A. 指针指向的数据内容
B. 指针保存的地址值
C. 所有动态资源的独立副本
D. 只有成员函数

正确答案: B

解析: 浅复制只是把指针值拷过去,两个对象会指向同一块内存。

知识点拓展: 真正危险的是“共享所有权却各自以为自己独占”。

题 44

题目: 浅复制最典型的后果之一是:

A. 自动提升性能
B. 自动完成深复制
C. 重复释放同一资源
D. 所有对象都变常量

正确答案: C

解析: 两个对象共享同一地址时,析构阶段容易出现双重释放。

知识点拓展: 除了 double delete,还会带来悬空指针。

题 45

题目: 深复制与浅复制的核心区别在于:

A. 深复制会新建独立资源副本
B. 深复制只复制对象名
C. 深复制不需要构造函数
D. 深复制不能用于字符串

正确答案: A

解析: 深复制会重新申请资源并复制内容,而不是复制地址。

知识点拓展: 对拥有资源的类来说,深复制才更符合值语义。

题 46

题目: 复制构造函数最典型的声明形式是:

A. MyString(MyString copySource)
B. MyString(const MyString& copySource)
C. void MyString(const MyString&)
D. ~MyString(const MyString&)

正确答案: B

解析: 复制构造函数参数通常应是当前类对象的常量引用。

知识点拓展: 这是语法要求和性能要求共同决定的常见形式。

题 47

题目: 复制构造函数参数为什么不能按值传递?

A. 因为这样会导致复制构造函数不断递归调用自己
B. 因为这样对象会自动变成单例
C. 因为编译器不允许引用存在
D. 因为这样对象会进入全局区

正确答案: A

解析: 按值传参本身又需要复制对象,于是会再次调用复制构造函数。

知识点拓展: 所以复制构造函数参数几乎总是引用。

题 48

题目: 下面哪条语句最典型地触发复制构造函数?

A. MyString b = a;
B. b = a;
C. delete a;
D. a.GetString();

正确答案: A

解析: 用已有对象初始化新对象,是复制构造的典型触发场景。

知识点拓展: 创建阶段的复制通常归复制构造管,已存在对象之间赋值归复制赋值管。

题 49

题目: 下面哪条语句更典型地使用复制赋值运算符,而不是复制构造函数?

A. MyString b(a);
B. MyString b = a;
C. b = a;
D. return a;

正确答案: C

解析: b 已经存在时,再用 a 赋值给它,走的是赋值语义。

知识点拓展: 第九章重点在复制构造,但已经提醒你后面还要处理复制赋值。

题 50

题目: 如果一个类包含原始指针成员,下面哪项通常是更稳妥的做法?

A. 完全依赖默认复制构造函数
B. 提供合适的复制控制逻辑
C. 把析构函数删掉
D. 强制所有对象都成为友元

正确答案: B

解析: 只依赖默认复制往往会带来浅复制风险。

知识点拓展: 这就是“拥有资源的类必须认真对待复制语义”。

题 51

题目: 编译器自动生成的默认复制构造函数,通常做的是:

A. 深复制所有动态内存
B. 成员逐个复制
C. 自动移动资源
D. 自动禁用复制

正确答案: B

解析: 默认复制构造函数通常按成员逐个复制,也就是 memberwise copy。

知识点拓展: 对原始指针成员来说,这通常不够安全。

题 52

题目: 移动构造函数是哪一版 C++ 引入的重要特性?

A. C++98
B. C++03
C. C++11
D. C++17

正确答案: C

解析: 移动语义是 C++11 的标志性改进之一。

知识点拓展: 它对性能敏感型类型尤其重要。

题 53

题目: 移动构造函数的典型形参形式是:

A. ClassName(const ClassName&)
B. ClassName(ClassName&&)
C. ClassName(ClassName*)
D. ClassName(ClassName[])

正确答案: B

解析: && 表示右值引用,是移动语义的关键语法。

知识点拓展: 右值通常代表即将结束生命周期的临时对象。

题 54

题目: 移动构造函数的核心动作更接近:

A. 创建完整深复制副本
B. 接管资源所有权
C. 禁止对象构造
D. 让对象变成友元

正确答案: B

解析: 移动的重点是“拿走资源”,而不是“复制资源”。

知识点拓展: 这就是它比深复制更高效的原因。

题 55

题目: 一个对象被移动之后,更合理的设计通常是:

A. 让源对象指针保持原值不变
B. 让源对象进入可析构的安全状态
C. 让源对象自动深复制回来
D. 让源对象立即成为静态对象

正确答案: B

解析: 常见做法是把源对象资源指针置空,避免析构时重复释放。

知识点拓展: “被移动后可析构但语义未指定”是常见约定。

题 56

题目: 移动构造函数主要改善的是哪类场景的性能?

A. 处理临时对象时避免昂贵深复制
B. 让 private 变成 public
C. 让所有对象都在栈上创建
D. 让对象无法返回

正确答案: A

解析: 临时对象很快会被销毁,深复制它们往往浪费。

知识点拓展: 这在大字符串、容器和资源包装类里价值尤其大。

题 57

题目: 如果类成员主要是 std::stringstd::vector 这类设计良好的类型,通常:

A. 默认复制机制往往更可靠
B. 一定不能复制
C. 析构函数会失效
D. 只能用 C 风格数组

正确答案: A

解析: 这些标准库类型自身已经实现了合理的资源管理和复制语义。

知识点拓展: 这也是现代 C++ 尽量少直接使用原始资源指针的原因。

题 58

题目: 如果一个类自己写了析构函数去释放原始指针资源,但没处理复制问题,通常意味着:

A. 资源管理依然是安全的
B. 复制后仍可能出严重问题
C. 所有复制都会自动被禁用
D. sizeof 会变大

正确答案: B

解析: 析构解决的是释放问题,不自动解决复制所有权问题。

知识点拓展: 资源类的“创建、复制、销毁”必须整体考虑。

题 59

题目: 关于复制构造函数和析构函数的关系,哪项更正确?

A. 两者互不相关
B. 只要有析构函数,就绝不需要复制构造函数
C. 当类拥有资源时,两者往往需要协同设计
D. 只需要复制构造函数,不需要析构函数

正确答案: C

解析: 一个负责正确复制资源,一个负责正确释放资源,两者缺一不可。

知识点拓展: 这正是“资源拥有者类型”最难的地方。

题 60

题目: 如果一个函数参数从 MyString str 改成 const MyString& str,最直接的收益通常是:

A. 自动启用移动构造
B. 避免不必要的对象复制
C. 强制深复制
D. 对象自动变成单例

正确答案: B

解析: 常量引用传参不需要复制实参对象。

知识点拓展: 这也是大型对象常用的高效传参方式。


五、this、sizeof、struct、friend 与 union 题(61-80)

题 61

题目: 在非静态成员函数中,this 最准确表示的是:

A. 当前类名
B. 当前对象的地址
C. 当前对象的副本
D. 当前函数的返回值

正确答案: B

解析: this 指向当前正在调用该成员函数的对象。

知识点拓展: 可以把它理解成一个隐式传入的对象指针。

题 62

题目: 编译器在调用非静态成员函数时,通常会隐式传入:

A. main 指针
B. this 指针
C. nullptr
D. 复制构造函数

正确答案: B

解析: 成员函数之所以知道“当前对象是谁”,就是因为隐式有 this

知识点拓展: 这也是成员函数能直接访问成员变量的根源。

题 63

题目: 为什么静态成员函数中没有 this

A. 因为静态成员函数不属于任何具体对象
B. 因为静态成员函数一定是私有的
C. 因为静态成员函数不能访问任何数据
D. 因为静态成员函数会自动复制对象

正确答案: A

解析: 静态成员函数属于类层面,不绑定某个对象实例。

知识点拓展: 因而它不能直接访问非静态成员变量。

题 64

题目: 在成员函数里写 this->age = humansAge;,其中 this->age 指的是:

A. 局部变量 age
B. 当前对象的成员 age
C. 全局变量 age
D. 类型名 age

正确答案: B

解析: this-> 明确表示访问当前对象的成员。

知识点拓展: 当形参和成员同名时,这种写法尤其清晰。

题 65

题目: sizeof(ClassName) 主要反映的是:

A. 类成员函数代码总长度
B. 类数据布局所占用的对象大小
C. 类所在文件大小
D. 类创建过多少个对象

正确答案: B

解析: sizeof 用在类上关注的是对象实例的内存大小。

知识点拓展: 成员函数代码通常不按对象逐份存储,因此不算进对象大小。

题 66

题目: 通常情况下,sizeof(obj)sizeof(ClassName) 的关系是:

A. 一定前者更大
B. 一定前者更小
C. 通常相同
D. 完全无关

正确答案: C

解析: 对象实例大小通常由其类型决定,因此二者常相等。

知识点拓展: 这点在书里的 MyStringHuman 示例中也有体现。

题 67

题目: 如果类里有一个 int* 成员,它指向一个动态数组,那么 sizeof(该类对象)

A. 取决于动态数组元素个数
B. 通常不取决于动态数组元素个数
C. 一定等于数组总字节数
D. 一定无法编译

正确答案: B

解析: sizeof 看到的是指针成员本身,不会把指向的堆内存算进去。

知识点拓展: 这也是“对象大小”和“对象管理的资源大小”常被混淆的地方。

题 68

题目: 类对象大小与成员简单求和不一致时,常见原因之一是:

A. 复制构造函数
B. 继承
C. 内存对齐和填充
D. 友元函数

正确答案: C

解析: 编译器可能为满足对齐要求而插入 padding。

知识点拓展: 所以 sizeof 经常不是“肉眼相加”的结果。

题 69

题目: 在 C++ 中,struct 未显式写访问限定符时,成员默认是:

A. private
B. public
C. protected
D. static

正确答案: B

解析: struct 的默认访问权限是公有。

知识点拓展: 这是它和 class 最常见的区别之一。

题 70

题目: 在 C++ 中,classstruct 的一个典型默认差别是:

A. class 不能有成员函数
B. struct 不能实例化
C. class 默认私有,struct 默认公有
D. class 不能有构造函数

正确答案: C

解析: 在 C++ 编译器看来,两者非常像,但默认访问权限不同。

知识点拓展: 默认继承方式也不同,不过这一点在后续继承章节更重要。

题 71

题目: 下面关于 friend 函数的说法,正确的是:

A. 它会让类的所有私有成员变成公有
B. 它能被授权访问类的私有成员
C. 它一定是类成员函数
D. 它会禁用封装

正确答案: B

解析: 友元函数是由类明确授权的外部访问例外。

知识点拓展: 它不是类成员,但能访问私有成员。

题 72

题目: 如果一个外部类被声明为某类的 friend class,通常意味着:

A. 只有它的构造函数能访问私有成员
B. 这个外部类的所有成员都自动变成静态
C. 这个外部类的成员可访问该类私有成员
D. 两个类必须写在同一个文件里

正确答案: C

解析: 友元类获得的是整个类级别的访问授权。

知识点拓展: 友元权限是由被访问类授予的,不是外部类自己拿到的。

题 73

题目: 共用体 union 最本质的特点是:

A. 每个成员各自独立占内存
B. 每次通常只有一个非静态成员处于活动状态
C. 只能放整数
D. 不能定义任何函数

正确答案: B

解析: 共用体成员共享同一块内存,因此通常只应把其中一个视为当前有效值。

知识点拓展: 正因如此,它节省空间但更容易被误用。

题 74

题目: sizeof(union) 的结果通常接近于:

A. 所有成员大小之和
B. 最小成员大小
C. 最大成员大小
D. 成员个数

正确答案: C

解析: 编译器需要为最大成员预留足够空间。

知识点拓展: 这和类/结构“各成员都要占一份空间”不同。

题 75

题目: 在 C++ 中,不显式写访问限定符时,共用体成员默认是:

A. private
B. public
C. protected
D. virtual

正确答案: B

解析: 共用体和结构类似,成员默认公有。

知识点拓展: 但和结构不同的是,共用体成员共享内存。

题 76

题目: 关于共用体,下面哪项说法更正确?

A. 共用体不能节省内存
B. 共用体适合同时保存多个独立有效值
C. 共用体常和额外标签字段一起使用
D. 共用体完全等价于 std::string

正确答案: C

解析: 实际工程里,常用枚举等标签记录当前共用体有效成员是哪一个。

知识点拓展: 书中的 ComplexType 就是这种典型组合。

题 77

题目: 表达式 (*ptr).IntroduceSelf()ptr->IntroduceSelf() 的关系是:

A. 前者错误,后者正确
B. 前者正确,后者错误
C. 两者等价
D. 只有在 ptr 为常量时才等价

正确答案: C

解析: 两者都是通过指针访问对象成员,只是写法不同。

知识点拓展: -> 本质上就是为这种常见模式提供的简写。

题 78

题目:friend 最合理的态度通常是:

A. 能用就全用,代替所有接口
B. 把所有类互相都设为友元
C. 作为受控例外谨慎使用
D. 任何情况下都绝不能用

正确答案: C

解析: 友元是有用的,但如果滥用,会削弱接口边界。

知识点拓展: 设计上应该先考虑正常接口,确有必要再开友元口子。

题 79

题目: 如果把类的析构函数声明为私有,直接效果之一通常是:

A. 更容易在栈上创建对象
B. 可能阻止在栈上直接实例化对象
C. 自动启用单例
D. 自动禁用构造函数

正确答案: B

解析: 栈对象离开作用域时必须析构,而私有析构使这件事在类外不可行。

知识点拓展: 书中用这个技巧演示了“禁止在栈中实例化”的模式。

题 80

题目: 如果一个类只能在堆上创建,且析构函数是私有的,那么释放对象的合理办法通常是:

A. 让外部直接 delete
B. 提供类内可访问析构的公有接口
C. 永远不释放
D. 强制转成 void*

正确答案: B

解析: 类内部成员函数能够访问私有析构,因此可以封装释放逻辑。

知识点拓展: 这本质上是在把销毁权限也收回到类自己手里。


六、聚合初始化、constexpr 与综合应用题(81-100)

题 81

题目: 下面哪种写法体现了聚合初始化风格?

A. Aggregate1 a1{2017, 3.14};
B. Aggregate1 a1->2017;
C. Aggregate1 = a1(2017, 3.14);
D. Aggregate1 a1 = new Aggregate1;

正确答案: A

解析: 花括号整体给聚合类型赋初值,就是聚合初始化的典型写法。

知识点拓展: C++11 之后这种写法更常见也更自然。

题 82

题目: 一个类要成为聚合类型,通常不应包含:

A. 公有非静态数据成员
B. 用户定义的构造函数
C. 简单数组成员
D. 基本类型成员

正确答案: B

解析: 书中明确指出,用户定义的构造函数会破坏聚合类型条件。

知识点拓展: 私有/受保护成员、虚函数等也会影响是否为聚合类型。

题 83

题目: 如果一个类含有私有数据成员,那么按第九章给出的标准,它通常:

A. 更容易成为聚合类型
B. 仍然一定是聚合类型
C. 通常不再是聚合类型
D. 一定变成共用体

正确答案: C

解析: 聚合类型通常要求只包含公有非静态数据成员。

知识点拓展: 一旦你开始做封装,往往就会失去“整体聚合初始化”的条件。

题 84

题目: 如果类中已经定义了用户自己的构造函数,按照本章语境,下列哪项更可能成立?

A. 它通常不能再按简单聚合类型整体初始化
B. 它一定变成 union
C. 它无法实例化
D. 它自动拥有移动构造函数

正确答案: A

解析: 用户定义构造函数会让聚合初始化条件发生变化。

知识点拓展: 这也是“封装能力增强”和“简单聚合能力”之间的一种取舍。

题 85

题目:Aggregate1 a1{2017, 3.14}; 的理解,下面哪项更正确?

A. 它是在调用友元函数
B. 它是在整体初始化聚合对象
C. 它一定会触发复制构造
D. 它是在做指针运算

正确答案: B

解析: 这正是书中展示的聚合初始化类/结构的典型例子。

知识点拓展: 这类语法在结构简单、公开数据成员场景里很直接。

题 86

题目: constexpr 用于类构造函数和成员函数时,核心目标之一是:

A. 禁止类实例化
B. 让符合条件的计算可在编译期完成
C. 替代析构函数
D. 禁止对象复制

正确答案: B

解析: constexpr 让编译器在可能时直接计算出结果。

知识点拓展: 这能减少运行期计算成本。

题 87

题目: 如果某个构造函数被声明为 constexpr,但对象本身不是常量表达式使用场景,那么:

A. 编译器一定报错
B. 该关键字完全非法
C. 编译器可能不在编译期求值
D. 对象会自动变成单例

正确答案: C

解析: constexpr 提供的是“可在编译期求值”的能力,不是强制一切都编译期完成。

知识点拓展: 是否真正发生编译期计算,还取决于使用方式。

题 88

题目: 书中单例类 President 的核心访问入口是:

A. 公有默认构造函数
B. President::GetInstance()
C. 全局变量 President
D. 外部友元函数

正确答案: B

解析: 单例通过静态访问函数返回同一个实例。

知识点拓展: 关键不在“名字”,而在“类内只保留一个实例入口”。

题 89

题目: 传统 C++ 中,要禁止类对象被复制,书中给出的典型做法是:

A. 把所有成员都设为 public
B. 把复制构造函数和复制赋值声明为私有
C. 只写析构函数
D. 把对象都改成 struct

正确答案: B

解析: 这是旧式 C++ 中常见的“不可复制类”实现方式。

知识点拓展: 现代 C++ 更常用 = delete 表达这种意图。

题 90

题目: 单例模式的直接目标是:

A. 让类永远不能构造
B. 确保全局只存在一个实例
C. 提高 sizeof 结果
D. 禁止类有私有成员

正确答案: B

解析: 单例要解决的是“类只能有一个化身”的问题。

知识点拓展: 但这也可能带来扩展性瓶颈,因此不能滥用。

题 91

题目: 对单参数构造函数来说,下面哪项通常是更稳妥的工程实践?

A. 默认允许所有隐式转换
B. 优先考虑 explicit
C. 强制写成友元
D. 一律写成 private

正确答案: B

解析: explicit 能减少“编译器偷偷帮你转类型”的意外。

知识点拓展: 这会让接口语义更清楚。

题 92

题目: 如果一个类的所有成员都是私有的,且没有友元,那么谁能访问这些成员?

A. 任何函数都可以
B. 只有全局函数可以
C. 只有该类自己的成员函数
D. 只有 main()

正确答案: C

解析: 没有友元授权时,私有成员只对类内部开放。

知识点拓展: 这正是封装边界最严格的一种情况。

题 93

题目:friend 的最佳理解更接近:

A. 一个完全替代公有接口的机制
B. 一个受控的访问例外
C. 一个强制对象复制的关键字
D. 一个初始化语法糖

正确答案: B

解析: 友元是类作者主动打开的一条例外通道,而不是默认设计手段。

知识点拓展: 把所有访问都交给友元,通常说明接口设计本身有问题。

题 94

题目: 下列哪种说法更符合“只能在堆上创建对象”的设计?

A. 把析构函数私有化,并提供类内销毁入口
B. 把所有成员都公有
C. 删除 new
D. 把构造函数写成全局函数

正确答案: A

解析: 书中用私有析构配合静态销毁函数来展示这种模式。

知识点拓展: 这类技巧本质是通过生命周期规则约束实例存在方式。

题 95

题目: 构造函数最适合承担的另一个高级职责是:

A. 保证对象一创建就满足基本不变量
B. 自动生成所有友元
C. 强制对象大小固定为 4 字节
D. 自动禁用拷贝

正确答案: A

解析: 对象设计的关键之一,就是让它“出生即合法”。

知识点拓展: 这比“创建后再慢慢补齐状态”更可靠。

题 96

题目: 对于普通局部对象,析构函数通常在什么时候调用?

A. 对象离开作用域时
B. 编译开始时
C. 调用构造函数前
D. 写了 friend 之后

正确答案: A

解析: 自动对象在作用域结束时会自动析构。

知识点拓展: 这就是栈对象资源管理常常更省心的原因。

题 97

题目: 对共用体执行聚合初始化时,最值得警惕的点是:

A. 它会自动初始化所有成员
B. 它只会初始化第一个非静态成员
C. 它会自动深复制所有数据
D. 它会把所有成员设成私有

正确答案: B

解析: 书中特别提醒,共用体聚合初始化只会落到第一个非静态成员上。

知识点拓展: 因此对共用体而言,显式指定活动成员通常更清晰。

题 98

题目: 在静态成员函数里,下面哪项是不存在的?

A. 类名
B. 作用域解析
C. this 指针
D. 返回值

正确答案: C

解析: 静态成员函数不依附具体对象,因此没有 this

知识点拓展: 这也是它不能直接访问实例成员的原因。

题 99

题目: 第九章里“带原始指针的类最容易出问题”的核心原因是:

A. 语法太长
B. 拷贝、析构和所有权边界容易混乱
C. 指针一定比对象慢
D. 类不能包含指针

正确答案: B

解析: 一旦类自己拥有原始资源,复制和销毁就不再是“随便默认一下”能解决的。

知识点拓展: 真正麻烦的不是指针语法,而是资源所有权语义。

题 100

题目: 如果做完整章后只能记住一句话,哪句最值得记住?

A. 类只是把变量写得更长
B. 只要有构造函数,类设计就一定正确
C. 类设计的核心是控制对象的状态、访问方式、复制方式和生命周期
D. struct 一定比 class 更简单

正确答案: C

解析: 第九章表面讲语法,深层训练的是对象设计与资源语义。

知识点拓展: 真正理解这一点,后面学继承、运算符重载、STL 和 RAII 才不会散。


七、刷题建议

如果你想把第九章真正吃透,可以按这个顺序复习:

  1. 先做 1-20,把类、对象、.-> 的区别彻底理顺。
  2. 再做 21-40,把封装、构造函数、默认构造、explicit、析构函数吃透。
  3. 再做 41-60,把浅复制、深复制、复制构造、移动构造的逻辑完全打通。
  4. 再做 61-80,把 thissizeofstructfriendunion 的边界条件记牢。
  5. 最后做 81-100,把聚合初始化、constexpr 和对象设计模式联系起来。

如果你做完整套题之后能做到:

  • 看见一个类时先想到“状态、接口、生命周期”
  • 看到原始指针成员时立刻警惕复制语义
  • 知道什么时候该用 private
  • 知道为什么单参数构造函数要考虑 explicit
  • 能分清复制构造、复制赋值、移动构造不是一回事

那第九章就算真正过关了。