1.10 C++ 面向对象(重头戏)
一、面向对象和面向过程区别
面向过程:强调过程的抽象化和模块化,以过程为中心处理客观世界问题。
面向对象:强调把解决问题的方法直接绑定到对象身上。
二、 面向对象的基本特征有哪些?
抽象(抽象出共同的、本质的特征)、继承(基类与派生类)、封装(将数据和方法封装在一个类中,只提供一些公有接口来访问)、多态(相同方法在不同对象上的表现不同)。
三、什么是深拷贝和浅拷贝
主要是针对指针成员变量来说的,深拷贝要求重新申请一段新的内存,将源对象指针所指向的内存内容拷贝到新对象中。浅拷贝就会简单的进行指针赋值。
四、什么是友元
一些类会给成员以外的函数或类(即友元函数、友元类)提供访问、操作自身成员变量(包括私有变量和保护变量)的机会,但是这种赋权是单向的(友元类、友元方法可以操作该类的私有成员,但反过来不行)。
#include <iostream>
using namespace std;
class BankAccount {
private:
double balance; // 私有成员,外部无法直接访问
public:
BankAccount(double b) : balance(b) {}
// 声明审计函数为友元(允许访问私有成员)
friend void audit(const BankAccount& account);
};
// 友元函数定义(可以访问BankAccount的私有成员)
void audit(const BankAccount& account) {
cout << "[审计] 当前余额: " << account.balance << endl; // 直接访问私有成员
}
int main() {
BankAccount acc(1000.0);
audit(acc); // 输出: [审计] 当前余额: 1000
return 0;
}
特性 |
友元函数 |
友元类 |
访问权限 |
可以访问目标类的私有( |
该类的所有成员函数均可访问目标类的私有和保护成员 |
定义方式 |
在类内使用 |
在类内使用 |
访问方式 |
需通过对象实例或指针/引用访问成员(如 |
可直接访问目标类成员(无需通过实例) |
关系方向 |
单向:目标类成员函数不能访问友元函数的局部变量 |
单向:目标类成员函数不能访问友元类的私有成员(除非友元类反向声明) |
典型应用场景 |
1. 运算符重载( |
1. 紧密协作的类(如 |
五、基类的构造函数、析构函数能否被派生类继承?
不能。首先二者名字就不一样,其次基类与派生类的资源管理需求也不一样,构造函数、析构函数有其自身的调用机制。
六、初始化列表和构造函数初始化的区别?
初始化列表会先于构造函数执行,也就是在定义阶段执行。对const或引用类型成员初始化时,只能用初始化列表的方式;以及派生类对基类传递值进行初始化的时候;以及没有默认构造函数的成员类(因为如果不进行显示初始化,编译器将自动调用其默认构造函数,但是没有默认构造函数,然后就会报错)。
七、类的成员变量的初始化顺序
按在类中声明的顺序依次初始化,与初始化列表的顺序无关。
若使用构造函数初始化,则与构造函数中的初始化顺序有关。
在 c++11 以前,普通变量成员不能进行类内初始化;c++11 之后可以,类内初始化会被编译器合并到构造函数的初始化列表中,若同时存在类内初始化和构造函数初始化列表,后者会覆盖前者
在 c++17 以前,静态成员必须在类外单独初始化;c++17 引入 内联静态成员,允许在类内初始化:
class MyClass {
static int count; // 声明
static constexpr float PI = 3.14f; // 声明+初始化
};
int MyClass::count = 0; // 必须类外定义
class MyClass {
static inline int count = 0; // 声明+定义+初始化
static constexpr float PI = 3.14f; // 同C++11
};
// 无需类外定义!
底层原理:
inline告诉编译器:- 该变量的定义可以出现在多个翻译单元(.cpp文件)中。
- 链接器会合并所有重复定义,最终只保留一个实例,在程序全局数据区有唯一地址。
八、public 继承、protected 继承、private 继承的区别?
公有继承会向下传递,公有部分在其子孙派生类中将一直是公有部分。
保护继承会使得只能通过其最近派生类访问被继承的类。基类的公有部分和保护部分全都将变为派生类的保护部分。
私有继承使基类的一切都将变为私有,在第三代的时候会导致完全屏蔽,第三代只能通过第二代的公有接口访问基类的公有接口,进而访问基类的私有成员。
九、虚继承
解决棱形继承的问题。避免对爷爷成员变量访问的二义性。会由编译器选择一个中间类的虚基类指针找到基类的数据。
内存布局:
普通继承:
D的内存布局:[B::A::data] [C::A::data] [D的成员](两份 data)。
虚继承:
D的内存布局:[虚基类指针] [B的成员] [C的成员] [A::data] [D的成员](只有一份 data)。
十、C++ 类内可以定义引用数据成员吗?
可以,必须通过成员函数初始化列表初始化。
十一、泛型编程是什么?
让类型参数化,是程序可以从逻辑功能上抽象,把被处理对象的类型作为参数传递。c++ 里面主要通过模板实现(函数模板、类模板)。类模板可以进行 类型参数化 和 值参数化 。
template <typename T, int size> // 使用值参数 size
class Array {
private:
T arr[size];
public:
T& operator[](int index) {
return arr[index];
}
}
模板特化(模板函数、模板类):
template <typename T>
void print(T value) {
cout << "Generic: " << value << endl;
}
//针对 int类型的特化
template
void print<int>(int value) {
cout << "Specialized for int: "<< value << en
剩余60%内容,订阅专栏后可继续查看/也可单篇购买
一名985硕,在25年秋招中斩获多个C++/嵌入式开发Offer。本专栏将分享我的面经,涵盖C/C++、操作系统、计算机网络、ARM体系与架构、Linux应用/驱动开发、Qt、通信协议及开发工具链等核心内容。