Skip to content

C++ 面试问题

基础概念

1. 什么是指针和引用?它们的区别是什么?

指针:

  • 存储另一个变量的内存地址
  • 可以被重新赋值指向不同的对象
  • 可以为空(nullptr)
  • 需要解引用操作符(*)访问值
cpp
int x = 10;
int* ptr = &x;  // 指针
*ptr = 20;      // 通过指针修改值
ptr = nullptr;  // 可以重新赋值

引用:

  • 现有变量的别名
  • 一旦初始化就不能改变指向的对象
  • 不能为空
  • 直接使用,无需解引用
cpp
int x = 10;
int& ref = x;   // 引用
ref = 20;       // 直接修改
// ref = y;     // 错误:不能重新赋值

主要区别:

  • 指针占用内存,引用只是别名
  • 指针可以为空,引用不能
  • 指针可以重新赋值,引用不能
  • 指针需要解引用,引用直接使用

2. 什么是虚函数?如何实现多态?

cpp
class Shape {
public:
    virtual double area() = 0;  // 纯虚函数
    virtual ~Shape() = default;
};

class Circle : public Shape {
private:
    double radius;
public:
    Circle(double r) : radius(r) {}
    double area() override {
        return 3.14159 * radius * radius;
    }
};

class Rectangle : public Shape {
private:
    double width, height;
public:
    Rectangle(double w, double h) : width(w), height(h) {}
    double area() override {
        return width * height;
    }
};

// 使用多态
void printArea(Shape* shape) {
    std::cout << "Area: " << shape->area() << std::endl;
}

虚函数表(vtable):

  • 每个含有虚函数的类都有一个虚函数表
  • 对象包含指向虚函数表的指针
  • 调用虚函数时通过虚函数表查找

3. const 关键字的用途

cpp
// 1. const 变量
const int MAX_SIZE = 100;

// 2. const 指针
const int* ptr1 = &x;      // 指向常量的指针,值不能修改
int* const ptr2 = &x;      // 常量指针,地址不能修改
const int* const ptr3 = &x; // 指向常量的常量指针

// 3. const 引用
void printValue(const int& value) {
    // value = 10;  // 错误:不能修改
    std::cout << value << std::endl;
}

// 4. const 成员函数
class MyClass {
public:
    int getValue() const {  // 不修改对象状态
        return value;
    }
private:
    int value;
};

// 5. const 返回值
const std::string& getName() {
    return name;
}

4. static 关键字的用途

cpp
// 1. 静态局部变量
void counter() {
    static int count = 0;  // 只初始化一次
    count++;
    std::cout << count << std::endl;
}

// 2. 静态成员变量
class Counter {
public:
    static int count;  // 所有对象共享
    
    Counter() {
        count++;
    }
};
int Counter::count = 0;  // 必须在类外初始化

// 3. 静态成员函数
class Math {
public:
    static int add(int a, int b) {  // 不需要对象调用
        return a + b;
    }
};
// Math::add(2, 3);

// 4. 静态全局变量/函数(文件作用域)
static int globalVar = 10;  // 只在本文件可见

面向对象

5. 什么是封装?如何实现?

cpp
class BankAccount {
private:  // 私有成员,外部无法直接访问
    double balance;
    
public:
    BankAccount(double initial) : balance(initial) {}
    
    // 公共接口
    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }
    
    bool withdraw(double amount) {
        if (amount > 0 && balance >= amount) {
            balance -= amount;
            return true;
        }
        return false;
    }
    
    double getBalance() const {
        return balance;
    }
};

6. 继承类型及其区别

cpp
// 1. 公有继承
class Base {};
class Derived : public Base {};  // 基类的 public 变为 public,protected 变为 protected

// 2. 保护继承
class Derived : protected Base {};  // 基类的 public 和 protected 都变为 protected

// 3. 私有继承
class Derived : private Base {};  // 基类的 public 和 protected 都变为 private

用途:

  • 公有继承:is-a 关系
  • 保护继承:在派生类中实现细节
  • 私有继承:has-a 关系

7. 抽象类和接口的区别

cpp
// 抽象类
class Animal {
protected:
    std::string name;
    
public:
    Animal(const std::string& n) : name(n) {}
    
    virtual void makeSound() = 0;  // 纯虚函数
    virtual void eat() {  // 可以有实现
        std::cout << name << " is eating" << std::endl;
    }
    virtual ~Animal() = default;
};

// 接口(C++ 中通过纯抽象类实现)
class IShape {
public:
    virtual double area() const = 0;
    virtual double perimeter() const = 0;
    virtual ~IShape() = default;
};

区别:

  • 抽象类可以有成员变量和非纯虚函数
  • 接口通常只包含纯虚函数
  • 一个类可以继承多个接口(通过多重继承)

内存管理

8. 栈和堆的区别

特性
分配方式自动手动
分配速度
大小限制
内存释放自动手动
用途局部变量动态分配
cpp
// 栈分配
int x = 10;  // 自动释放

// 堆分配
int* ptr = new int(10);  // 需要手动释放
delete ptr;

// 使用智能指针(推荐)
#include <memory>
std::unique_ptr<int> ptr2 = std::make_unique<int>(10);
// 自动释放

9. 内存泄漏及其预防

cpp
// 内存泄漏示例
void memoryLeak() {
    int* ptr = new int[100];  // 分配内存
    // 忘记释放
    // delete[] ptr;
}

// 预防方法

// 1. 使用智能指针
void noLeak() {
    std::unique_ptr<int[]> ptr = std::make_unique<int[]>(100);
    // 自动释放
}

// 2. RAII 模式
class Resource {
private:
    int* data;
public:
    Resource(size_t size) : data(new int[size]) {}
    ~Resource() { delete[] data; }
};

// 3. 遵循 new/delete 配对
void correctUsage() {
    int* ptr = new int;
    *ptr = 10;
    delete ptr;  // 必须释放
}

10. 深拷贝与浅拷贝

cpp
class ShallowCopy {
public:
    int* data;
    
    ShallowCopy(int value) {
        data = new int(value);
    }
    
    // 默认拷贝构造函数 - 浅拷贝
    ~ShallowCopy() {
        delete data;  // 两次释放!
    }
};

class DeepCopy {
public:
    int* data;
    
    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;
    }
};

模板和泛型

11. 什么是模板?如何使用?

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

// 使用
int maxInt = maximum(5, 10);
double maxDouble = maximum(3.14, 2.71);

// 类模板
template<typename T>
class Stack {
private:
    std::vector<T> elements;
    
public:
    void push(const T& element) {
        elements.push_back(element);
    }
    
    T pop() {
        T top = elements.back();
        elements.pop_back();
        return top;
    }
    
    bool empty() const {
        return elements.empty();
    }
};

// 使用
Stack<int> intStack;
Stack<std::string> stringStack;

12. 模板特化

cpp
// 通用模板
template<typename T>
class Container {
public:
    void print(T value) {
        std::cout << "Generic: " << value << std::endl;
    }
};

// 显式特化(针对特定类型)
template<>
class Container<bool> {
public:
    void print(bool value) {
        std::cout << "Boolean: " << (value ? "true" : "false") << std::endl;
    }
};

// 使用
Container<int> intContainer;
intContainer.print(42);  // Generic: 42

Container<bool> boolContainer;
boolContainer.print(true);  // Boolean: true

STL 容器

13. vector 与 list 的选择

cpp
// vector - 连续内存,随机访问快
std::vector<int> vec = {1, 2, 3, 4, 5};
vec.push_back(6);
int third = vec[2];  // O(1)

// list - 双向链表,插入删除快
std::list<int> lst = {1, 2, 3, 4, 5};
lst.push_back(6);
lst.insert(lst.begin(), 0);  // O(1)

选择标准:

  • vector:需要随机访问,插入/删除主要在末尾
  • list:频繁在中间插入/删除

14. map 和 unordered_map 的区别

特性mapunordered_map
实现红黑树哈希表
查找O(log n)平均 O(1)
有序
内存较少较多
cpp
// map - 有序
std::map<std::string, int> orderedMap;
orderedMap["banana"] = 2;
orderedMap["apple"] = 1;
// 输出:apple, banana(按字母顺序)

// unordered_map - 无序
std::unordered_map<std::string, int> hashMap;
hashMap["banana"] = 2;
hashMap["apple"] = 1;
// 输出顺序不确定

并发和线程

15. 如何创建和管理线程?

cpp
#include <thread>
#include <mutex>
#include <atomic>

std::mutex mtx;
int sharedCounter = 0;

void increment() {
    std::lock_guard<std::mutex> lock(mtx);
    sharedCounter++;
}

std::atomic<int> atomicCounter(0);

void atomicIncrement() {
    atomicCounter++;
}

int main() {
    // 创建线程
    std::thread t1(increment);
    std::thread t2(increment);
    
    // 等待线程完成
    t1.join();
    t2.join();
    
    std::cout << "Counter: " << sharedCounter << std::endl;
    
    // 使用原子操作
    std::thread t3(atomicIncrement);
    std::thread t4(atomicIncrement);
    
    t3.join();
    t4.join();
    
    std::cout << "Atomic Counter: " << atomicCounter << std::endl;
    
    return 0;
}

16. 竞态条件和死锁

cpp
// 竞态条件
int counter = 0;

void raceCondition() {
    counter++;  // 非原子操作,可能导致错误结果
}

// 解决方法
std::mutex mtx;
int safeCounter = 0;

void safeIncrement() {
    std::lock_guard<std::mutex> lock(mtx);
    safeCounter++;
}

// 死锁示例
std::mutex m1, m2;

void thread1() {
    std::lock(m1, m2);  // 锁定多个互斥量
    std::lock_guard<std::mutex> lock1(m1, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(m2, std::adopt_lock);
    // 执行操作
}

void thread2() {
    std::lock(m1, m2);  // 相同的锁定顺序
    std::lock_guard<std::mutex> lock1(m1, std::adopt_lock);
    std::lock_guard<std::mutex> lock2(m2, std::adopt_lock);
    // 执行操作
}

高级主题

17. 移动语义和右值引用

cpp
#include <utility>

class Resource {
private:
    int* data;
    size_t size;
    
public:
    // 构造函数
    Resource(size_t s) : size(s), data(new int[s]) {}
    
    // 拷贝构造函数(深拷贝)
    Resource(const Resource& other) : size(other.size), data(new int[other.size]) {
        std::copy(other.data, other.data + size, data);
    }
    
    // 移动构造函数
    Resource(Resource&& other) noexcept : size(other.size), data(other.data) {
        other.data = nullptr;  // 清空原对象
        other.size = 0;
    }
    
    ~Resource() {
        delete[] data;
    }
};

// 使用移动语义
Resource createResource() {
    return Resource(100);  // 返回值优化 + 移动语义
}

Resource r1 = createResource();  // 移动构造
Resource r2 = std::move(r1);     // 显式移动

18. lambda 表达式

cpp
#include <algorithm>
#include <vector>

void lambdaExamples() {
    std::vector<int> numbers = {1, 2, 3, 4, 5};
    
    // 基本 lambda
    auto square = [](int x) { return x * x; };
    int result = square(5);  // 25
    
    // 带捕获的 lambda
    int multiplier = 2;
    auto multiply = [multiplier](int x) { return x * multiplier; };
    result = multiply(3);  // 6
    
    // 可变捕获
    int counter = 0;
    auto increment = [counter]() mutable {
        counter++;
        return counter;
    };
    
    // 在算法中使用
    std::vector<int> doubled(5);
    std::transform(numbers.begin(), numbers.end(), doubled.begin(),
                   [](int x) { return x * 2; });
    
    // 带谓词的查找
    auto it = std::find_if(numbers.begin(), numbers.end(),
                          [](int x) { return x > 3; });
}

19. 异常处理

cpp
#include <stdexcept>

class CustomException : public std::exception {
public:
    const char* what() const noexcept override {
        return "Custom exception occurred";
    }
};

void riskyOperation(bool shouldFail) {
    if (shouldFail) {
        throw std::runtime_error("Operation failed!");
    }
}

void exceptionExample() {
    try {
        riskyOperation(true);
    } catch (const std::runtime_error& e) {
        std::cerr << "Runtime error: " << e.what() << std::endl;
    } catch (const std::exception& e) {
        std::cerr << "Exception: " << e.what() << std::endl;
    } catch (...) {
        std::cerr << "Unknown exception" << std::endl;
    }
    
    // noexcept 指定
    void safeFunction() noexcept {
        // 保证不会抛出异常
    }
}

设计模式

20. 单例模式

cpp
class Singleton {
private:
    static Singleton* instance;
    Singleton() {}  // 私有构造函数
    
public:
    // 禁止拷贝和赋值
    Singleton(const Singleton&) = delete;
    Singleton& operator=(const Singleton&) = delete;
    
    static Singleton& getInstance() {
        static Singleton instance;  // 线程安全(C++11)
        return instance;
    }
    
    void doSomething() {
        std::cout << "Singleton method called" << std::endl;
    }
};

// 使用
Singleton& s = Singleton::getInstance();
s.doSomething();

21. 工厂模式

cpp
#include <memory>

enum class ShapeType { CIRCLE, RECTANGLE };

class Shape {
public:
    virtual ~Shape() = default;
    virtual void draw() const = 0;
};

class Circle : public Shape {
public:
    void draw() const override {
        std::cout << "Drawing Circle" << std::endl;
    }
};

class Rectangle : public Shape {
public:
    void draw() const override {
        std::cout << "Drawing Rectangle" << std::endl;
    }
};

class ShapeFactory {
public:
    static std::unique_ptr<Shape> createShape(ShapeType type) {
        switch (type) {
            case ShapeType::CIRCLE:
                return std::make_unique<Circle>();
            case ShapeType::RECTANGLE:
                return std::make_unique<Rectangle>();
            default:
                throw std::invalid_argument("Invalid shape type");
        }
    }
};

// 使用
auto shape = ShapeFactory::createShape(ShapeType::CIRCLE);
shape->draw();

性能优化

22. 优化技巧

cpp
// 1. 避免不必要的拷贝
void processString(const std::string& str) {  // 使用引用
    // 处理字符串
}

// 2. 使用移动语义
std::string createLargeString() {
    std::string result = "large string...";
    return result;  // 移动而非拷贝
}

// 3. 预分配容器大小
std::vector<int> vec;
vec.reserve(1000);  // 预分配

// 4. 使用 emplace_back 代替 push_back
std::vector<std::pair<int, int>> pairs;
pairs.emplace_back(1, 2);  // 就地构造
// pairs.push_back(std::make_pair(1, 2));  // 先构造再移动

// 5. 使用 constexpr
constexpr int factorial(int n) {
    return (n <= 1) ? 1 : n * factorial(n - 1);
}
constexpr int result = factorial(5);  // 编译时计算

// 6. 避免虚函数调用(如果性能关键)
class FastClass {
public:
    void fastMethod() {  // 非虚函数
        // 更快
    }
};

23. 内存对齐

cpp
#include <iostream>

// 结构体填充
struct Padded {
    char a;       // 1 byte + 3 padding
    int b;        // 4 bytes
    double c;     // 8 bytes
};  // 总共 16 bytes

struct Packed {
    char a;       // 1 byte
    int b;        // 4 bytes
    double c;     // 8 bytes
} __attribute__((packed));  // 总共 13 bytes

// 对齐指定
alignas(16) struct AlignedStruct {
    int data[4];
};

// 检查对齐
void checkAlignment() {
    std::cout << "Alignment of int: " << alignof(int) << std::endl;
    std::cout << "Size of Padded: " << sizeof(Padded) << std::endl;
    std::cout << "Size of Packed: " << sizeof(Packed) << std::endl;
}

实际问题

24. 实现 strcpy 函数

cpp
char* myStrcpy(char* dest, const char* src) {
    if (dest == nullptr || src == nullptr) {
        return nullptr;
    }
    
    char* original = dest;
    while ((*dest++ = *src++) != '\0') {
        // 空循环
    }
    
    return original;
}

25. 检测链表中的环

cpp
struct ListNode {
    int val;
    ListNode* next;
    
    ListNode(int x) : val(x), next(nullptr) {}
};

bool hasCycle(ListNode* head) {
    if (head == nullptr || head->next == nullptr) {
        return false;
    }
    
    ListNode* slow = head;
    ListNode* fast = head->next;
    
    while (slow != fast) {
        if (fast == nullptr || fast->next == nullptr) {
            return false;
        }
        slow = slow->next;
        fast = fast->next->next;
    }
    
    return true;
}

准备建议

理论准备

  1. 深入理解基础概念:指针、引用、内存管理
  2. 掌握面向对象原则:封装、继承、多态
  3. 熟悉 STL:容器、算法、迭代器
  4. 了解现代 C++ 特性:C++11/14/17/20

实践准备

  1. 刷题:LeetCode、HackerRank
  2. 项目经验:准备 2-3 个代表性项目
  3. 代码规范:遵循良好的编码风格
  4. 性能分析:了解性能优化技术

常见问题类型

  • 代码分析题:理解并解释代码
  • 编程题:实现特定功能
  • 设计题:设计类或系统架构
  • 调试题:找出代码中的错误
  • 性能题:优化代码性能

相关资源

Content is for learning and research only.