Skip to content

C++ 面试题解

概述

C++面试涵盖语言基础、高级特性、算法数据结构、系统设计等多个方面。本章精选常见C++面试题目,提供详细解答和代码示例,帮助准备技术面试。

📚 语言基础题

指针和引用

cpp
// Q1: 指针和引用的区别
/*
面试官:请解释指针和引用的区别

答案要点:
1. 初始化:引用必须在声明时初始化,指针可以后初始化
2. 重新赋值:引用不能重新指向其他对象,指针可以
3. 空值:引用不能为空,指针可以为nullptr
4. 内存:引用不占用额外内存,指针占用内存
5. 运算:指针支持算术运算,引用不支持
*/

void demonstratePointerVsReference() {
    int a = 10, b = 20;
    
    // 引用必须初始化
    int& ref = a;          // 正确
    // int& ref2;          // 错误:引用必须初始化
    
    // 指针可以不初始化
    int* ptr;              // 正确但危险
    ptr = &a;              // 后续赋值
    
    // 引用不能重新指向
    // ref = b;            // 这是赋值,不是重新指向
    
    // 指针可以重新指向
    ptr = &b;              // 正确
    
    // 引用不能为空
    // int& nullRef = nullptr;  // 错误
    
    // 指针可以为空
    int* nullPtr = nullptr;    // 正确
    
    // 指针算术
    int arr[] = {1, 2, 3};
    int* p = arr;
    p++;                   // 指向下一个元素
    // ref++;              // 错误:引用不支持算术运算
}

// Q2: 野指针、悬挂指针、内存泄漏
void demonstratePointerProblems() {
    // 野指针:未初始化的指针
    int* wildPtr;          // 野指针,指向未知位置
    // *wildPtr = 10;      // 危险:可能导致程序崩溃
    
    // 悬挂指针:指向已释放内存的指针
    int* danglingPtr = new int(42);
    delete danglingPtr;
    // *danglingPtr = 10;  // 危险:悬挂指针
    danglingPtr = nullptr; // 应该置空
    
    // 内存泄漏:分配的内存未释放
    for (int i = 0; i < 1000; ++i) {
        int* leak = new int(i);
        // 忘记delete leak; // 内存泄漏
    }
    
    // 正确做法:使用智能指针
    std::unique_ptr<int> smartPtr = std::make_unique<int>(42);
    // 自动释放,无需手动delete
}

类和对象

cpp
// Q3: 构造函数和析构函数的调用顺序
class Base {
public:
    Base() { std::cout << "Base构造" << std::endl; }
    virtual ~Base() { std::cout << "Base析构" << std::endl; }
};

class Derived : public Base {
public:
    Derived() { std::cout << "Derived构造" << std::endl; }
    ~Derived() override { std::cout << "Derived析构" << std::endl; }
};

void demonstrateConstructorOrder() {
    std::cout << "创建对象:" << std::endl;
    Derived obj;  // 输出:Base构造 -> Derived构造
    
    std::cout << "销毁对象:" << std::endl;
    // 自动析构:Derived析构 -> Base析构
}

// Q4: 深拷贝vs浅拷贝
class ShallowCopy {
private:
    int* data_;
    
public:
    ShallowCopy(int value) : data_(new int(value)) {}
    
    // 默认拷贝构造函数(浅拷贝)
    // ShallowCopy(const ShallowCopy& other) : data_(other.data_) {}
    
    ~ShallowCopy() { delete data_; }  // 可能导致双重删除
};

class DeepCopy {
private:
    int* data_;
    
public:
    DeepCopy(int value) : data_(new int(value)) {}
    
    // 深拷贝构造函数
    DeepCopy(const DeepCopy& other) : data_(new int(*other.data_)) {}
    
    // 深拷贝赋值运算符
    DeepCopy& operator=(const DeepCopy& other) {
        if (this != &other) {
            delete data_;
            data_ = new int(*other.data_);
        }
        return *this;
    }
    
    ~DeepCopy() { delete data_; }
    
    int getValue() const { return *data_; }
};

// Q5: 虚函数和纯虚函数
class Shape {
public:
    // 虚函数:有默认实现,可被重写
    virtual double area() const {
        return 0.0;
    }
    
    // 纯虚函数:没有实现,必须被重写
    virtual void draw() const = 0;
    
    // 虚析构函数:确保派生类析构函数被调用
    virtual ~Shape() = default;
};

class Circle : public Shape {
private:
    double radius_;
    
public:
    Circle(double radius) : radius_(radius) {}
    
    double area() const override {
        return 3.14159 * radius_ * radius_;
    }
    
    void draw() const override {
        std::cout << "Drawing circle" << std::endl;
    }
};

// Q6: 多态实现原理
void demonstratePolymorphism() {
    std::unique_ptr<Shape> shape = std::make_unique<Circle>(5.0);
    
    // 运行时多态:通过虚函数表实现
    std::cout << "Area: " << shape->area() << std::endl;  // 调用Circle::area()
    shape->draw();  // 调用Circle::draw()
    
    // 虚函数表解释:
    // 1. 每个有虚函数的类都有一个虚函数表
    // 2. 对象的前几个字节存储指向虚函数表的指针
    // 3. 调用虚函数时通过虚函数表查找实际函数地址
}

🔧 高级特性题

模板编程

cpp
// Q7: 函数模板vs类模板
template<typename T>
T max(T a, T b) {
    return (a > b) ? a : b;
}

template<typename T>
class Stack {
private:
    std::vector<T> data_;
    
public:
    void push(const T& item) { data_.push_back(item); }
    
    T pop() {
        if (data_.empty()) {
            throw std::runtime_error("Stack is empty");
        }
        T item = data_.back();
        data_.pop_back();
        return item;
    }
    
    bool empty() const { return data_.empty(); }
    size_t size() const { return data_.size(); }
};

// Q8: 模板特化
template<typename T>
class TypeInfo {
public:
    static std::string getName() { return "unknown"; }
};

// 全特化
template<>
class TypeInfo<int> {
public:
    static std::string getName() { return "integer"; }
};

template<>
class TypeInfo<std::string> {
public:
    static std::string getName() { return "string"; }
};

// 偏特化(只对类模板有效)
template<typename T>
class TypeInfo<T*> {
public:
    static std::string getName() { return "pointer to " + TypeInfo<T>::getName(); }
};

void demonstrateTemplateSpecialization() {
    std::cout << TypeInfo<int>::getName() << std::endl;        // integer
    std::cout << TypeInfo<double>::getName() << std::endl;     // unknown
    std::cout << TypeInfo<int*>::getName() << std::endl;       // pointer to integer
}

// Q9: SFINAE (Substitution Failure Is Not An Error)
template<typename T>
typename std::enable_if<std::is_integral<T>::value, bool>::type
isEven(T value) {
    return value % 2 == 0;
}

template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, bool>::type
isEven(T value) {
    return false;  // 浮点数没有偶数概念
}

// C++17 if constexpr版本
template<typename T>
bool isEvenModern(T value) {
    if constexpr (std::is_integral_v<T>) {
        return value % 2 == 0;
    } else {
        return false;
    }
}

内存管理

cpp
// Q10: 智能指针的区别和使用场景
void demonstrateSmartPointers() {
    // unique_ptr:独占所有权
    std::unique_ptr<int> uniquePtr = std::make_unique<int>(42);
    // std::unique_ptr<int> copy = uniquePtr;  // 错误:不能拷贝
    std::unique_ptr<int> moved = std::move(uniquePtr);  // 可以移动
    
    // shared_ptr:共享所有权
    std::shared_ptr<int> sharedPtr1 = std::make_shared<int>(100);
    std::shared_ptr<int> sharedPtr2 = sharedPtr1;  // 可以拷贝
    std::cout << "引用计数: " << sharedPtr1.use_count() << std::endl;  // 2
    
    // weak_ptr:避免循环引用
    std::weak_ptr<int> weakPtr = sharedPtr1;
    if (auto locked = weakPtr.lock()) {  // 尝试获取shared_ptr
        std::cout << "对象仍然存在: " << *locked << std::endl;
    }
}

// Q11: 循环引用问题
class Parent;
class Child;

class Parent {
public:
    std::shared_ptr<Child> child;
    ~Parent() { std::cout << "Parent destroyed" << std::endl; }
};

class Child {
public:
    std::weak_ptr<Parent> parent;  // 使用weak_ptr避免循环引用
    ~Child() { std::cout << "Child destroyed" << std::endl; }
};

void demonstrateCircularReference() {
    auto parent = std::make_shared<Parent>();
    auto child = std::make_shared<Child>();
    
    parent->child = child;
    child->parent = parent;  // weak_ptr不增加引用计数
    
    // 当函数结束时,parent和child都会被正确销毁
}

// Q12: 内存对齐
struct UnalignedStruct {
    char a;     // 1 byte
    int b;      // 4 bytes
    char c;     // 1 byte
    // 实际大小可能是12字节(而不是6字节)由于内存对齐
};

struct AlignedStruct {
    int b;      // 4 bytes
    char a;     // 1 byte
    char c;     // 1 byte
    // 实际大小可能是8字节
};

void demonstrateAlignment() {
    std::cout << "UnalignedStruct size: " << sizeof(UnalignedStruct) << std::endl;
    std::cout << "AlignedStruct size: " << sizeof(AlignedStruct) << std::endl;
    
    // 使用alignof查看对齐要求
    std::cout << "int alignment: " << alignof(int) << std::endl;
    std::cout << "char alignment: " << alignof(char) << std::endl;
}

🧮 算法和数据结构题

经典算法实现

cpp
// Q13: 实现快速排序
template<typename Iterator>
void quickSort(Iterator begin, Iterator end) {
    if (std::distance(begin, end) <= 1) {
        return;
    }
    
    auto pivot = std::prev(end);  // 选择最后一个元素作为基准
    auto middle = std::partition(begin, pivot, [pivot](const auto& elem) {
        return elem < *pivot;
    });
    
    std::iter_swap(middle, pivot);
    
    quickSort(begin, middle);
    quickSort(std::next(middle), end);
}

// Q14: 实现LRU缓存
template<typename Key, typename Value>
class LRUCache {
private:
    using ListIter = typename std::list<std::pair<Key, Value>>::iterator;
    
    size_t capacity_;
    std::list<std::pair<Key, Value>> cache_list_;
    std::unordered_map<Key, ListIter> cache_map_;
    
public:
    explicit LRUCache(size_t capacity) : capacity_(capacity) {}
    
    Value get(const Key& key) {
        auto it = cache_map_.find(key);
        if (it == cache_map_.end()) {
            throw std::runtime_error("Key not found");
        }
        
        // 移动到前面(最近使用)
        cache_list_.splice(cache_list_.begin(), cache_list_, it->second);
        return it->second->second;
    }
    
    void put(const Key& key, const Value& value) {
        auto it = cache_map_.find(key);
        
        if (it != cache_map_.end()) {
            // 更新现有键值
            it->second->second = value;
            cache_list_.splice(cache_list_.begin(), cache_list_, it->second);
        } else {
            // 添加新键值
            if (cache_list_.size() >= capacity_) {
                // 删除最旧的元素
                auto last = cache_list_.back();
                cache_map_.erase(last.first);
                cache_list_.pop_back();
            }
            
            cache_list_.emplace_front(key, value);
            cache_map_[key] = cache_list_.begin();
        }
    }
};

// Q15: 实现线程安全的单例模式
class ThreadSafeSingleton {
private:
    static std::unique_ptr<ThreadSafeSingleton> instance_;
    static std::mutex mutex_;
    
    ThreadSafeSingleton() = default;
    
public:
    static ThreadSafeSingleton* getInstance() {
        std::lock_guard<std::mutex> lock(mutex_);
        if (instance_ == nullptr) {
            instance_ = std::unique_ptr<ThreadSafeSingleton>(new ThreadSafeSingleton());
        }
        return instance_.get();
    }
    
    // 更简单的C++11版本(推荐)
    static ThreadSafeSingleton& getInstanceStatic() {
        static ThreadSafeSingleton instance;  // C++11保证线程安全
        return instance;
    }
    
    ThreadSafeSingleton(const ThreadSafeSingleton&) = delete;
    ThreadSafeSingleton& operator=(const ThreadSafeSingleton&) = delete;
};

std::unique_ptr<ThreadSafeSingleton> ThreadSafeSingleton::instance_ = nullptr;
std::mutex ThreadSafeSingleton::mutex_;

复杂数据结构

cpp
// Q16: 实现红黑树或AVL树的基本操作
enum class Color { RED, BLACK };

template<typename T>
struct RBTreeNode {
    T data;
    Color color;
    std::shared_ptr<RBTreeNode> left;
    std::shared_ptr<RBTreeNode> right;
    std::weak_ptr<RBTreeNode> parent;
    
    RBTreeNode(const T& value) : data(value), color(Color::RED) {}
};

template<typename T>
class RedBlackTree {
private:
    using NodePtr = std::shared_ptr<RBTreeNode<T>>;
    NodePtr root_;
    
    void rotateLeft(NodePtr node) {
        auto right = node->right;
        node->right = right->left;
        
        if (right->left) {
            right->left->parent = node;
        }
        
        right->parent = node->parent;
        
        if (auto parent = node->parent.lock()) {
            if (node == parent->left) {
                parent->left = right;
            } else {
                parent->right = right;
            }
        } else {
            root_ = right;
        }
        
        right->left = node;
        node->parent = right;
    }
    
    void fixInsert(NodePtr node) {
        while (node != root_ && node->parent.lock()->color == Color::RED) {
            auto parent = node->parent.lock();
            auto grandparent = parent->parent.lock();
            
            if (parent == grandparent->left) {
                auto uncle = grandparent->right;
                
                if (uncle && uncle->color == Color::RED) {
                    parent->color = Color::BLACK;
                    uncle->color = Color::BLACK;
                    grandparent->color = Color::RED;
                    node = grandparent;
                } else {
                    if (node == parent->right) {
                        node = parent;
                        rotateLeft(node);
                        parent = node->parent.lock();
                        grandparent = parent->parent.lock();
                    }
                    
                    parent->color = Color::BLACK;
                    grandparent->color = Color::RED;
                    rotateRight(grandparent);
                }
            } else {
                // 对称情况
                // ...类似实现
            }
        }
        root_->color = Color::BLACK;
    }
    
public:
    void insert(const T& value) {
        auto newNode = std::make_shared<RBTreeNode<T>>(value);
        
        if (!root_) {
            root_ = newNode;
            root_->color = Color::BLACK;
            return;
        }
        
        // BST插入逻辑
        NodePtr current = root_;
        NodePtr parent = nullptr;
        
        while (current) {
            parent = current;
            if (value < current->data) {
                current = current->left;
            } else {
                current = current->right;
            }
        }
        
        newNode->parent = parent;
        if (value < parent->data) {
            parent->left = newNode;
        } else {
            parent->right = newNode;
        }
        
        fixInsert(newNode);
    }
};

🧪 系统设计题

设计模式应用

cpp
// Q17: 设计一个线程池
class ThreadPool {
private:
    std::vector<std::thread> workers_;
    std::queue<std::function<void()>> tasks_;
    std::mutex mutex_;
    std::condition_variable condition_;
    bool stop_;
    
public:
    explicit ThreadPool(size_t num_threads) : stop_(false) {
        for (size_t i = 0; i < num_threads; ++i) {
            workers_.emplace_back([this] {
                while (true) {
                    std::function<void()> task;
                    
                    {
                        std::unique_lock<std::mutex> lock(mutex_);
                        condition_.wait(lock, [this] { return stop_ || !tasks_.empty(); });
                        
                        if (stop_ && tasks_.empty()) {
                            return;
                        }
                        
                        task = std::move(tasks_.front());
                        tasks_.pop();
                    }
                    
                    task();
                }
            });
        }
    }
    
    template<typename F, typename... Args>
    auto enqueue(F&& f, Args&&... args) 
        -> std::future<typename std::result_of<F(Args...)>::type> {
        
        using ReturnType = typename std::result_of<F(Args...)>::type;
        
        auto task = std::make_shared<std::packaged_task<ReturnType()>>(
            std::bind(std::forward<F>(f), std::forward<Args>(args)...)
        );
        
        std::future<ReturnType> result = task->get_future();
        
        {
            std::unique_lock<std::mutex> lock(mutex_);
            
            if (stop_) {
                throw std::runtime_error("enqueue on stopped ThreadPool");
            }
            
            tasks_.emplace([task] { (*task)(); });
        }
        
        condition_.notify_one();
        return result;
    }
    
    ~ThreadPool() {
        {
            std::unique_lock<std::mutex> lock(mutex_);
            stop_ = true;
        }
        
        condition_.notify_all();
        
        for (std::thread& worker : workers_) {
            worker.join();
        }
    }
};

// Q18: 观察者模式实现
template<typename EventType>
class Observer {
public:
    virtual ~Observer() = default;
    virtual void onEvent(const EventType& event) = 0;
};

template<typename EventType>
class Subject {
private:
    std::vector<std::weak_ptr<Observer<EventType>>> observers_;
    
public:
    void subscribe(std::shared_ptr<Observer<EventType>> observer) {
        observers_.push_back(observer);
    }
    
    void unsubscribe(std::shared_ptr<Observer<EventType>> observer) {
        observers_.erase(
            std::remove_if(observers_.begin(), observers_.end(),
                          [observer](const std::weak_ptr<Observer<EventType>>& weak_obs) {
                              return weak_obs.lock() == observer;
                          }),
            observers_.end());
    }
    
    void notify(const EventType& event) {
        // 清理失效的观察者
        observers_.erase(
            std::remove_if(observers_.begin(), observers_.end(),
                          [](const std::weak_ptr<Observer<EventType>>& weak_obs) {
                              return weak_obs.expired();
                          }),
            observers_.end());
        
        // 通知所有观察者
        for (auto& weak_obs : observers_) {
            if (auto obs = weak_obs.lock()) {
                obs->onEvent(event);
            }
        }
    }
};

总结

面试准备策略

  • 基础扎实: 指针、引用、类、继承、多态
  • 现代特性: 智能指针、lambda、移动语义
  • 算法功底: 排序、搜索、动态规划
  • 系统设计: 设计模式、并发编程
  • 实际经验: 项目经历、问题解决

高频考点

类别重点内容难度
语言基础指针引用、类设计、虚函数中等
内存管理智能指针、RAII、内存泄漏中等
模板编程模板特化、SFINAE困难
并发编程线程安全、锁机制困难
算法实现排序、树、图算法中等

回答技巧

  • 结构清晰: 先答要点,再举例说明
  • 代码演示: 用代码证明理解深度
  • 对比分析: 比较不同方案优劣
  • 实际应用: 结合项目经验说明
  • 扩展思考: 主动讨论相关问题

面试建议

  • 准备充分: 复习基础概念和高级特性
  • 练习编码: 手写常见算法和数据结构
  • 模拟面试: 找同事或朋友练习
  • 项目准备: 深入理解自己的项目
  • 持续学习: 关注C++标准发展

C++面试不仅考察语言知识,更重要的是解决问题的能力和工程实践经验。扎实的基础加上丰富的实践是成功的关键。

本站内容仅供学习和研究使用。