一、说明
这篇笔记基于《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 阻止 int 到 Human 的隐式转换。
知识点拓展: 但显式写出构造过程依然是允许的。
题 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::string、std::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
解析: 对象实例大小通常由其类型决定,因此二者常相等。
知识点拓展: 这点在书里的 MyString 和 Human 示例中也有体现。
题 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++ 中,class 与 struct 的一个典型默认差别是:
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-20,把类、对象、
.、->的区别彻底理顺。 - 再做 21-40,把封装、构造函数、默认构造、
explicit、析构函数吃透。 - 再做 41-60,把浅复制、深复制、复制构造、移动构造的逻辑完全打通。
- 再做 61-80,把
this、sizeof、struct、friend、union的边界条件记牢。 - 最后做 81-100,把聚合初始化、
constexpr和对象设计模式联系起来。
如果你做完整套题之后能做到:
- 看见一个类时先想到“状态、接口、生命周期”
- 看到原始指针成员时立刻警惕复制语义
- 知道什么时候该用
private - 知道为什么单参数构造函数要考虑
explicit - 能分清复制构造、复制赋值、移动构造不是一回事
那第九章就算真正过关了。