海康威视C++软件开发 一面总结

1. 虚函数的实现原理是什么?虚函数表存储在哪里?

答案:

  • 虚函数通过虚函数表(vtable)和虚函数指针(vptr)实现
  • 每个包含虚函数的类都有一个虚函数表,存储虚函数的地址
  • 每个对象内部有一个vptr指针,指向该类的虚函数表
  • vptr通常位于对象内存布局的开头
  • 虚函数表存储在只读数据段(.rodata)
  • 调用虚函数时:通过对象的vptr找到vtable,再通过偏移量找到具体函数地址

示例:

class Base {
    virtual void func() { }
};
// 对象内存布局:[vptr][成员变量...]

2. 智能指针的实现原理?shared_ptr是线程安全的吗?(保留八股文)

答案:

  • unique_ptr:独占所有权,内部只保存裸指针,析构时delete
  • shared_ptr:引用计数,内部有控制块(control block)存储引用计数和删除器
  • weak_ptr:不增加引用计数,通过lock()转换为shared_ptr使用

shared_ptr的线程安全性:

  • 引用计数的增减是原子操作,线程安全
  • 但指向的对象本身不是线程安全的
  • 多个线程同时修改同一个shared_ptr对象也不安全
  • 结论:引用计数安全,对象访问和指针修改需要额外同步

3. 说说你对RAII的理解,在项目中如何应用?

答案:RAII(Resource Acquisition Is Initialization)资源获取即初始化:

  • 核心思想:资源的生命周期与对象生命周期绑定
  • 构造函数获取资源,析构函数释放资源
  • 利用C++的栈展开机制自动管理资源

典型应用:

  • 智能指针管理内存
  • lock_guard/unique_lock管理互斥锁
  • fstream管理文件句柄
  • 自定义资源管理类(如数据库连接、网络socket)

示例:

class FileHandler {
    FILE* fp;
public:
    FileHandler(const char* name) { fp = fopen(name, "r"); }
    ~FileHandler() { if(fp) fclose(fp); }
};

4. 什么是内存对齐?为什么需要内存对齐?如何控制对齐方式?

答案:内存对齐:数据在内存中的起始地址必须是某个值的整数倍

原因:

  • CPU访问对齐的数据效率更高,一次读取即可
  • 某些CPU架构访问未对齐数据会崩溃
  • 提高缓存命中率

对齐规则:

  • 结构体成员按其类型大小对齐
  • 结构体总大小是最大成员对齐值的整数倍
  • 可用#pragma pack(n)alignas控制

示例:

struct A {
    char c;   // 1字节
    int i;    // 4字节对齐,前面填充3字节
    short s;  // 2字节
};  // 总大小12字节(需要填充到4的倍数)

// 使用alignas
struct alignas(16) B {
    int x;
};  // 强制16字节对齐

5. 左值、右值、左值引用、右值引用分别是什么?移动语义的作用?

答案:

  • 左值(lvalue):有名字、可取地址的表达式,如变量
  • 右值(rvalue):临时对象、字面量,不可取地址
  • 左值引用:T&,只能绑定左值
  • 右值引用:T&&,可以绑定右值,用于移动语义

移动语义:

  • 避免深拷贝,转移资源所有权
  • 通过移动构造函数和移动赋值运算符实现
  • 使用std::move将左值转换为右值引用

示例:

class String {
    char* data;
public:
    // 移动构造
    String(String&& s) noexcept : data(s.data) {
        s.data = nullptr;  // 转移所有权
    }
};

String s1("hello");
String s2 = std::move(s1);  // 移动而非拷贝

6. 进程和线程的区别?多线程通信方式有哪些?

答案:区别:

  • 进程是资源分配的基本单位,线程是调度的基本单位
  • 进程有独立地址空间,线程共享进程地址空间
  • 进程切换开销大,线程切换开销小
  • 进程间通信复杂,线程间通信简单

多线程通信方式:

  • 共享内存:全局变量、堆内存(需要同步)
  • 互斥锁(mutex):保护临界区
  • 条件变量(condition_variable):线程间同步
  • 信号量(semaphore):控制访问数量
  • 原子操作(atomic):无锁编程
  • 消息队列:生产者-消费者模型

7. 说说C++的四种类型转换,各自的使用场景?(保留八股文)

答案:

  • static_cast:编译期类型转换,用于基本类型转换、向上转型(子类到父类)
  • dynamic_cast:运行时类型检查,用于向下转型(父类到子类),失败返回nullptr
  • const_cast:去除const属性,用于修改const对象(需谨慎)
  • reinterpret_cast:重新解释内存,用于指针类型转换(如int到char

示例:

// static_cast
double d = 3.14;
int i = static_cast<int>(d);

// dynamic_cast
Base* b = new Derived();
Derived* d = dynamic_cast<Derived*>(b);

// const_cast
const int* cp = &i;
int* p = const_cast<int*>(cp);

// reinterpret_cast
int* ip = &i;
char* cp = reinterpret_cast<char*>(ip);

8. 什么是死锁?如何避免死锁?

答案:死锁:多个线程互相等待对方持有的资源,导致永久阻塞

死锁的四个必要条件:

  • 互斥:资源不能共享
  • 持有并等待:持有资源的同时等待其他资源
  • 不可剥夺:资源不能被强制释放
  • 循环等待:存在资源等待环路

避免死锁的方法:

  • 按固定顺序获取锁
  • 使用std::lock同时获取多个锁
  • 使用超时机制(try_lock_for)
  • 使用层次锁(hierarchical mutex)
  • 避免嵌套锁
  • 使用RAII管理锁(lock_guard)

示例:

// 错误:可能死锁
void thread1() {
    lock(mutex1);
    lock(mutex2);
}
void thread2() {
    lock(mutex2);
    lock(mutex1);
}

// 正确:固定顺序
void thread1() {
    std::lock(mutex1, mutex2);  // 同时获取
    lock_g

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

C++八股文全集 文章被收录于专栏

本专栏系统梳理C++技术面试核心考点,涵盖语言基础、面向对象、内存管理、STL容器、模板编程及经典算法。从引用指针、虚函数表、智能指针等底层原理,到继承多态、运算符重载等OOP特性从const、static、inline等关键字辨析,到动态规划、KMP算法、并查集等手写实现。每个知识点以面试答题形式呈现,注重原理阐述而非冗长代码,帮助你快速构建完整知识体系,从容应对面试官提问,顺利拿下offer。

全部评论

相关推荐

26想校招上岸的菜鸟:但是不可否认的是 leader想要3个 结果投了1000个 领导筛了100个面试 当leader找到了满意的3个 让他们继续二面 但是同时 其他的97个也会让你一面 因为已经约面了总不能毁面吧 因为leader也不确定那3个能不能过后面的面试,以及来不来 因此 这97个有一部分作为那3个的后续 剩下的 就是走个过场 面完就挂 这就是我个人认为的kpi面
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务