【C++类与对象高频面试问题总结2】
一、类与面向对象基础
问题1:C++中class和struct的主要区别是什么?
核心区别:
- 默认访问权限:
class
默认成员为private
,struct
默认为public
(继承方式默认也不同,class
默认私有继承,struct
默认公有继承)。 - 设计初衷:
class
强调封装(数据+方法),struct
兼容C语言结构体(纯数据聚合),但C++中struct
可完全替代class
功能。
选择建议:需要访问控制时用class
,纯数据集合可用struct
,依团队规范统一。
问题2:什么是封装?C++如何实现封装?
封装:将数据与操作结合,隐藏内部细节,仅暴露必要接口。
实现:通过访问限定符:
public
:类外可直接访问(接口函数)。private
:仅类内访问,需通过公有函数操作(数据校验)。protected
:派生类可访问(继承时使用)。
示例:将成员变量设为private
,提供public
的Init()
和Print()
函数管理数据,避免外部直接篡改。
二、类的作用域与内存模型
问题1:类对象的内存大小如何计算?成员函数是否占内存?
计算规则:
- 仅计算成员变量,遵循内存对齐(对齐数取成员大小与编译器默认值的较小值,总大小为最大对齐数的倍数)。
- 成员函数、静态成员不占对象内存(存于代码段,共享)。
特殊情况:
- 空类对象大小为1字节(编译器插入占位符,确保地址唯一)。
- 包含
private
成员的对象,大小仅与成员变量有关(访问权限不影响内存布局)。
问题2:this指针的作用和特性是什么?
作用:编译器为非静态成员函数添加的隐藏参数,指向当前调用对象,用于访问成员变量。
特性:
- 类型:
类类型* const
(指针常量,指向不可变;const
成员函数中为const 类类型* const
)。 - 作用域:仅在成员函数内可见,调用时自动传递(无需显式声明)。
- 可空性:允许
nullptr
调用不访问成员的函数(如ptr->Func()
,Func
无成员访问)。
三、默认成员函数
问题1:默认构造函数的形式及适用场景?
三种形式:
- 无参构造函数:
Date()
。 - 全缺省构造函数:
Date(int year = 1970, int month = 1, int day = 1)
。 - 编译器生成:用户未定义时自动生成(仅初始化自定义类型成员,内置类型为随机值)。
建议使用全缺省:支持无参/有参调用,避免二义性(与无参构造函数共存会报错)。
问题2:拷贝构造函数的参数为何必须是引用?
原因:值传递会递归调用拷贝构造函数,导致栈溢出(创建实参副本时需调用自身)。
正确写法:const T&
引用传递(避免拷贝,支持常量对象和临时对象)。
示例:
Date::Date(const Date& d) { /* 深拷贝逻辑 */ } // 正确
Date::Date(Date d) { /* 错误:递归调用 */ }
问题3:浅拷贝与深拷贝的区别?
对比项 | 浅拷贝 | 深拷贝 |
---|---|---|
定义 | 复制指针地址(共享内存) | 复制指针指向的内容(独立内存) |
风险 | 析构时重复释放,野指针 | 安全释放独立资源 |
实现 | 编译器默认生成 | 需手动编写拷贝构造/赋值重载 |
场景 | 无动态资源的类 | 含指针/动态内存的类(如String 、Stack ) |
四、运算符重载与const成员
问题1:哪些运算符不能重载?重载时的注意事项?
不能重载的运算符(5个):
.
、::
、.*
、?:
、sizeof
。
注意事项:
- 不能创建新运算符,需作用于自定义类型(避免修改内置类型行为)。
- 成员函数左操作数为
this
,全局函数常用友元(如operator<<
需访问私有成员)。
问题2:const成员函数的作用是什么?
作用:
- 修饰
this
指针为const T* const
,确保函数不修改成员变量(只读接口)。 - 允许
const
对象调用(如const Date d; d.Print();
)。
示例:
void Print() const { /* 仅读取成员,不能修改 */ }
五、友元与初始化列表
问题1:友元函数和友元类的区别?
友元函数:类外函数,用friend
声明,可访问类的私有/保护成员(常用于运算符重载,如operator<<
)。
友元类:整个类获得访问权限,友元关系单向且不可传递(A是B的友元,B不一定是A的友元)。
注意:友元破坏封装性,需谨慎使用,仅开放必要访问权限。
问题2:为什么必须用初始化列表初始化const成员?
原因:
const
成员/引用成员必须在定义时初始化,构造函数体中只能赋值(无法初始化常量)。- 自定义类型成员若没有默认构造函数,需在初始化列表显式调用其带参构造函数。
示例:
class A {const int _N;
public:A(int n) : _N(n) {} // 必须在初始化列表初始化
};
六、静态成员与内部类
问题1:静态成员变量的特性?
特性:
- 属于类,所有对象共享,需在类外初始化(C++11允许类内给
const static
成员赋默认值)。 - 静态成员函数无
this
指针,只能访问静态成员(用于与类相关但无对象依赖的功能,如计数器)。
示例:
class Counter {static int s_count; // 类内声明static int GetCount() { return s_count; }
};
int Counter::s_count = 0; // 类外初始化
问题2:内部类的特性?
特性:
- 独立类,通过外部类域访问(如
Outer::Inner
)。 - 内部类默认是外部类的友元,可访问其私有成员,但外部类不是内部类的友元(需显式声明)。
回答技巧总结
- 概念对比:清晰区分易混点(如
class
vsstruct
、浅拷贝 vs 深拷贝)。 - 内存模型:强调对象大小仅含成员变量(对齐规则),成员函数不占对象内存。
- this指针:理解其隐藏参数性质,及
const
对this
的修饰效果。 - 封装实践:通过示例说明访问限定符的使用,避免过度暴露私有成员。
- 资源管理:掌握“三巨头”(构造、析构、拷贝构造)的协作,处理动态资源时需深拷贝。
通过以上高频问题的梳理,可系统掌握C++类与对象的核心知识点,应对面试中的深度提问。