自学内容网 自学内容网

C++ 面试模拟02

第一部分:基础知识

  1. 什么是拷贝构造函数和赋值运算符?它们之间有什么区别?
  2. 在 C++ 中,const 关键字的作用是什么?有哪些常见用法?
  3. C++ 中的内存管理机制是怎样的?如何避免内存泄漏?
  4. 虚函数(virtual function)的作用是什么?虚函数表(vtable)是如何工作的?

第二部分:面向对象编程

  1. 什么是多态性?C++ 中如何实现运行时多态?
  2. 请解释什么是继承以及继承的优缺点。
  3. C++ 中的“菱形继承”是什么?如何通过虚继承来解决它带来的问题?
  4. 什么是抽象类?抽象类与接口类有何不同?

第三部分:STL(标准模板库)

  1. std::vectorstd::list 有什么区别?它们各自的优点和缺点是什么?
  2. 什么是迭代器?有哪些类型的迭代器?
  3. std::mapstd::unordered_map 的底层实现分别是什么?性能有何差异?
  4. C++ 中的容器适配器有哪些?请举例说明。

第四部分:并发与多线程

  1. C++ 中如何创建线程?有哪些常见的线程管理方式?
  2. 什么是互斥锁(mutex)?如何避免死锁?
  3. 解释一下条件变量(condition variable)的作用,并给出一个简单的使用例子。
  4. 什么是线程局部存储(Thread Local Storage)?在 C++ 中如何使用?

第五部分:C++ 高级特性

  1. 什么是移动语义?C++11 中如何实现移动构造函数与移动赋值运算符?
  2. C++20 中的协程(coroutine)是什么?它们是如何工作的?
  3. 模板元编程(Template Metaprogramming)是什么?在 C++ 中有哪些实际应用?
  4. 什么是 SFINAE?在模板中如何利用 SFINAE 进行类型选择?

第六部分:现场编程

题目:

请你实现一个线程安全的计数器类 ThreadSafeCounter,它支持以下功能:

  • increment():计数器加 1
  • decrement():计数器减 1
  • get():返回当前计数器的值

要求:

  • 使用 std::mutex 保证线程安全。
  • 实现拷贝构造和赋值运算符重载。

答案:

C++知识点/面试问题指南-CSDN博客

第一部分:基础知识

  1. 拷贝构造函数和赋值运算符的区别

    • 拷贝构造函数:当用一个对象初始化另一个新对象时调用,用于对象的创建。
    • 赋值运算符:在已存在对象的基础上,将另一个对象的内容赋值给它。
    • 区别:拷贝构造函数是在对象初始化时调用的,赋值运算符则在对象已经创建后进行赋值。
  2. const 关键字的作用

    • 修饰变量:表示该变量的值不能修改。
    • 修饰指针:区分指针本身是否可变和指向的对象是否可变。
    • 修饰成员函数:表示该成员函数不会修改类的成员变量。
  3. 内存管理

    • C++ 使用动态分配(new/delete)、栈分配以及 RAII(资源获取即初始化)来管理内存。
    • 避免内存泄漏的方法包括使用智能指针(如 std::shared_ptrstd::unique_ptr)。
  4. 虚函数和 vtable

    • 虚函数:用于实现运行时多态,基类中标记为 virtual 的函数可以在派生类中被重写。
    • 虚函数表(vtable):编译器为包含虚函数的类创建一个表,指向虚函数的地址,保证在运行时能够调用正确的函数。

第二部分:面向对象编程

  1. 多态性

    • C++ 中通过虚函数实现运行时多态。基类的虚函数可以在派生类中重写,通过基类指针或引用调用时,动态地选择合适的派生类实现。
  2. 继承

    • 优点:允许代码重用和扩展基类功能。
    • 缺点:不当的使用可能导致强耦合,难以维护。
  3. 菱形继承和虚继承

    • 菱形继承:如果一个类从多个基类继承,而这些基类又继承自同一祖先类,会导致多个基类有相同的祖先成员。
    • 虚继承:通过 virtual 继承,确保继承链中的祖先类只会有一份副本。
  4. 抽象类

    • 抽象类:至少包含一个纯虚函数(= 0 的函数)。不能实例化,只能被继承。
    • 接口类:所有成员函数都为纯虚函数。

第三部分:STL

  1. std::vectorstd::list 区别

    • std::vector:基于动态数组,支持随机访问,插入/删除代价高(中间位置)。
    • std::list:基于双向链表,不支持随机访问,插入/删除较快。
    • 优缺点vector 适合频繁随机访问,list 适合频繁插入/删除。
  2. 迭代器的类型

    • 输入迭代器、输出迭代器、前向迭代器、双向迭代器、随机访问迭代器。
  3. std::map vs std::unordered_map

    • std::map:基于红黑树(有序),O(log n) 查找。
    • std::unordered_map:基于哈希表(无序),O(1) 平均查找,但最坏情况 O(n)。
  4. 容器适配器

    • 包括 std::stackstd::queuestd::priority_queue,它们通过封装其他容器(如 dequelist)实现特定的行为。

第四部分:并发与多线程

  1. 线程的创建

    • 使用 std::thread 创建线程:
std::thread t([] { std::cout << "Thread is running"; });
t.join();  // 等待线程结束
  • 互斥锁(mutex)与死锁

    • 互斥锁std::mutex 用于保护共享资源避免数据竞争。通过 std::lock_guardstd::unique_lock 自动管理锁。
    • 避免死锁:使用相同顺序加锁,或者使用 std::lock() 同时加锁多个资源。
  • 条件变量

    • 用于线程间同步,等待特定条件:
std::condition_variable cv;
std::mutex mtx;
bool ready = false;
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, [&ready] { return ready; });

线程局部存储

  • 使用 thread_local 关键字为每个线程定义独立的存储空间:
thread_local int x = 0;  // 每个线程都有自己独立的 x 变量

第五部分:C++ 高级特性

  1. 移动语义

    • 移动语义通过 移动构造函数移动赋值运算符 避免不必要的深拷贝,使用 std::move 来将对象转化为右值引用,从而实现资源的转移而不是复制。
  2. C++20 协程

    • 协程通过 co_awaitco_yieldco_return 实现协程的控制流,用来处理异步任务和生成器。
    • 使用例:
std::future<int> asyncAdd(int a, int b) {
    co_return a + b;
}
  • 模板元编程

    • 通过模板递归和模板特化实现编译期计算。常见应用包括类型萃取(type traits)和元函数。
  • SFINAE

    • 在模板中,"Substitution Failure Is Not An Error" 规则允许在匹配失败时尝试其他模板。常用于限制模板特化。
template<typename T>
std::enable_if_t<std::is_integral<T>::value, T> add(T a, T b) { return a + b; }

第六部分:现场编程

#include <iostream>
#include <mutex>

class ThreadSafeCounter {
private:
    int counter;
    std::mutex mtx;

public:
    ThreadSafeCounter() : counter(0) {}

    // 增加计数
    void increment() {
        std::lock_guard<std::mutex> lock(mtx);
        ++counter;
    }

    // 减少计数
    void decrement() {
        std::lock_guard<std::mutex> lock(mtx);
        --counter;
    }

    // 获取当前计数
    int get() const {
        std::lock_guard<std::mutex> lock(mtx);
        return counter;
    }

    // 拷贝构造函数
    ThreadSafeCounter(const ThreadSafeCounter& other) {
        std::lock_guard<std::mutex> lock(other.mtx);
        counter = other.counter;
    }

    // 赋值运算符
    ThreadSafeCounter& operator=(const ThreadSafeCounter& other) {
        if (this != &other) {
            std::lock_guard<std::mutex> lockThis(mtx);
            std::lock_guard<std::mutex> lockOther(other.mtx);
            counter = other.counter;
        }
        return *this;
    }
};


原文地址:https://blog.csdn.net/weixin_44965579/article/details/142356907

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!