字节二面
1. C++static你知道些什么?(说了作用域作用周期存放区域)
2. 现在有两个文件a.cpp、b.cpp,两个文件里都有static怎么确定谁先初始化
3. 静态类成员什么时候初始化?在b.cpp里include了a.h(里面有staticintx变量)b.cpp能用这个变量嘛?会不会出什么问题?(回答不知道,说作用域不对被面试官反问)
4. 你知道#include在什么阶段处理嘛?那你知道这个处理做了什么操作吗。
#include在预处理阶段处理,将指定的文件内容插入到当前源文件中。
如果是#include <> 回去系统目录找文件,如果是 #include " "会去自定义目录找文件,找不到再按标准库的方式查找。
5. .bss和.data有什么区别
(一个放未初始化的全局变量和静态变量,一个放已经初始化好了的)
6. C++有几种传参格式
(传值传指针传引用)
7. 传值传指针传引用区别是什么,为什么要有传引用?
(答的传值要生成局部变量传指针和引用可以直接对传的东西进行修改,传指针可以解引用,传引用就相当于传了地址上的值,但是没有解引用的说法
8. 模板函数会不会模板特化、偏特化知不知道
函数模板特化是指为函数模板的特定类型参数提供另外的实现。
模板偏特化(也称为部分特化)是针对类模板的一种特化方式。不是为所有模板参数都指定具体类型,而是对模板参数的一部分进行特化,用于处理模板参数在某些特定组合或者约束下的情况。
9. 说说多态?
多态是指在派生类中重写这些虚函数,当通过基类的指针或引用调用虚函数时会根据实际对象的类型来调用响应的函数。
派生类继承基类的虚函数,并根据需要重写。
运行时多态通过虚函数实现,运行时才确定调用哪个函数。
编译时多态通过函数重载和函数模板实现,通过传不同类型的参数可以调用不同的函数,参数匹配在编译时完成。
10.动态多态原理?
当一个类包含虚函数时,编译器会为该类创建一个虚函数表。
虚函数表存储了该类所有虚函数的地址。
每个包含虚函数的类对象都会有一个指向其类虚函数表的指针,称为虚函数指针vptr。
当对象被创建时,虚函数指针会被初始化一个指向其类的虚函数表。
当通过基类的指针或引用调用虚函数时,编译器会用该对象的虚函数指针来查找虚函数表中对应函数的地址。
11. 虚函数表在什么时候建立的?放在什么区?虚函数指针在什么时候建立的?
虚函数表是在编译期间由编译器创建的,存储于数据段中。
虚函数指针在对象构建时创建,存储在对象实例最前面的位置。
12. 虚继承你知道嘛?解决什么问题?
虚继承用于解决菱形继承问题。
在菱形继承结构中,一个类继承自两个具有共同基类的类时,会导致共同基类的成员在派生类中存在两份拷贝。
虚继承通过确保共同基类的单一实例存在于继承层次中,来解决菱形继承问题。在虚继承中,基类在派生类中只保留一份副本,而不是每个派生类保留一份副本。
class Base {
public:
int data;
};
class Derived1 : virtual public Base {
// Derived1 虚继承自 Base
};
class Derived2 : virtual public Base {
// Derived2 虚继承自 Base
};
class FinalDerived : public Derived1, public Derived2 {
// FinalDerived 类中只会有一份 Base 类的成员
};
13. 说说右值和右值引用吧
等号右边的通常就是右值,右值没有明确的地址且不可更改。
右值引用通过 && 符号声明,例如 int &&。
可实现 移动语义 和 完美转发。
移动语义:在对象赋值或函数返回时,使用右值引用,借助移动构造函数,直接把右值的所有权返回出去,避免不必要的拷贝。
完美转发:编写泛型代码时,使用右值引用可确保参数的值类别在传递过程中保持不变。
//完美转发示例
#include <iostream>
#include <utility> // for std::forward
// 一个简单的函数,用于打印传入的参数类型
void printType(int& x) {
std::cout << "Lvalue reference: " << x << std::endl;
}
void printType(int&& x) {
std::cout << "Rvalue reference: " << x << std::endl;
}
// 模板函数,完美转发参数给printType函数
template <typename T>
void forwardToPrintType(T&& arg) {
printType(std::forward<T>(arg));
}
int main() {
int a = 5;
// 传入左值
forwardToPrintType(a); // 输出: Lvalue reference: 5
// 传入右值
forwardToPrintType(10); // 输出: Rvalue reference: 10
return 0;
}
15. 友元知道嘛
类成员默认private,定义友元可以让其他类的函数访问自己的成员。
16. 多线程会嘛?
(没写过但学了一下)说说你知道的关于线程的一些关键字(答了互斥量和条件变量)
17. 生产者消费者模型知道嘛?
做一个队列
#include <iostream>
#include <queue>
#include <thread>
#include <mutex>
#include <condition_variable>
// 全局变量定义
std::queue<int> buffer; // 缓冲区队列
const unsigned int BUFFER_SIZE = 10; // 缓冲区大小
std::mutex mtx; // 互斥锁保护共享资源
std::condition_variable cv; // 条件变量用于线程同步
bool done = false; // 生产完成标志
// 生产者函数
void producer(int id) {
for (int i = 0; i < 20; ++i) {
std::unique_lock<std::mutex> lock(mtx); // 加锁
cv.wait(lock, [] { return buffer.size() < BUFFER_SIZE; }); // 等待缓冲区有空间
buffer.push(i); // 向缓冲区推送数据
std::cout << "Producer " << id << " produced " << i << std::endl; // 打印生产信息
lock.unlock(); // 解锁
cv.notify_all(); // 通知所有等待线程
}
}
// 消费者函数
void consumer(int id) {
while (true) {
std::unique_lock<std::mutex> lock(mtx); // 加锁
cv.wait(lock, [] { return !buffer.empty() || done; }); // 等待缓冲区有数据或生产完成
if (!buffer.empty()) {
int value = buffer.front(); // 获取缓冲区前端数据
buffer.pop(); // 从缓冲区弹出数据
std::cout << "Consumer " << id << " consumed " << value << std::endl; // 打印消费信息
}
lock.unlock(); // 解锁
cv.notify_all(); // 通知所有等待线程
if (done && buffer.empty()) break; // 如果生产完成且缓冲区为空,则退出循环
}
}
// 主函数
int main() {
std::thread producers[2]; // 生产者线程数组
for (int i = 0; i < 2; ++i) {
producers[i] = std::thread(producer, i + 1); // 创建生产者线程
}
std::thread consumers[2]; // 消费者线程数组
for (int i = 0; i < 2; ++i) {
consumers[i] = std::thread(consumer, i + 1); // 创建消费者线程
}
// 等待所有生产者线程完成
for (auto& p : producers) {
p.join();
}
{
std::unique_lock<std::mutex> lock(mtx); // 加锁以修改共享变量
done = true; // 设置生产完成标志
}
cv.notify_all(); // 通知所有等待线程生产已完成
// 等待所有消费者线程完成
for (auto& c : consumers) {
c.join();
}
return 0;
}
18.智能指针你知道嘛?知道share_ptr原理?
智能指针类型 | 描述 |
---|---|
unique_ptr | 独占所有权,不可拷贝,支持移动,超出作用域即释放内存 |
shared_ptr | 共享所有权,使用引用计数管理生命周期,最后一个shared_ptr 销毁时释放对象 |
weak_ptr | 弱引用,不增加引用计数,用于解决循环引用,指向的shared_ptr 销毁时自动变为空 |
独占所有权指任意时刻,只有一个unique_ptr实例可以拥有并管理一个特定的对象。
不可拷贝指拷贝构造函数、赋值构造函数被禁用,支持移动指允许通过移动构造函数和移动赋值运算符来将所有权给另一个指针。
NonCopyableMovable obj1 = createNonCopyableMovable();//创建实例
NonCopyableMovable obj2 = std::move(obj1); // 移动语义,obj1现在不再拥有资源
19. mmap()你知道嘛共享内存那个?你知道它是将进程里的哪个内存映射过去的嘛?
映射的是文件映射区,操作内存效果等同于直接操作文件。