海康威视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面
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
点赞
1
分享

创作者周榜

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