C++ 入门指南 (Java开发者版)
目录
基础语法对比
1. 基本类型
Java | C++ | 说明 |
---|---|---|
boolean | bool | 布尔类型 |
byte | char | 8位整数 |
short | short | 16位整数 |
int | int | 32位整数 |
long | long long | 64位整数 |
float | float | 32位浮点数 |
double | double | 64位浮点数 |
String | std::string | 字符串类型 |
2. 变量声明与初始化
Java:
int x = 10;
final int y = 20; // 常量
C++:
int x = 10;
const int y = 20; // 常量
auto z = 30; // 类型推导
3. 引用与指针
Java中只有引用,而C++同时支持引用和指针:
// 引用
int x = 10;
int& ref = x; // 引用必须初始化
ref = 20; // x也变成20
// 指针
int* ptr = &x; // 获取x的地址
*ptr = 30; // 通过指针修改x的值
内存管理
1. 栈与堆
Java中所有对象都在堆上分配,而C++允许在栈上创建对象:
// 栈上分配
MyClass obj1; // 自动管理内存
// 堆上分配
MyClass* obj2 = new MyClass(); // 需要手动释放
delete obj2; // 手动释放内存
2. 智能指针
C++11引入了智能指针,类似于Java的垃圾回收:
#include <memory>
// 独占所有权
std::unique_ptr<MyClass> ptr1 = std::make_unique<MyClass>();
// 共享所有权
std::shared_ptr<MyClass> ptr2 = std::make_shared<MyClass>();
面向对象编程
1. 类定义
Java:
public class MyClass {
private int x;
public MyClass(int x) {
this.x = x;
}
}
C++:
class MyClass {
private:
int x;
public:
MyClass(int x) : x(x) {} // 初始化列表
};
2. 继承
Java:
public class Child extends Parent {
public Child() {
super();
}
}
C++:
class Child : public Parent {
public:
Child() : Parent() {} // 调用父类构造函数
};
3. 虚函数
Java中所有方法默认都是虚函数,C++需要显式声明:
class Base {
public:
virtual void foo() = 0; // 纯虚函数
virtual ~Base() {} // 虚析构函数
};
标准库
1. 容器
Java | C++ | 说明 |
---|---|---|
ArrayList | std::vector | 动态数组 |
LinkedList | std::list | 双向链表 |
HashMap | std::unordered_map | 哈希表 |
TreeMap | std::map | 有序映射 |
使用示例:
#include <vector>
#include <map>
std::vector<int> vec = {1, 2, 3};
std::map<std::string, int> map;
map["key"] = 42;
2. 算法
C++标准库提供了丰富的算法:
#include <algorithm>
std::vector<int> vec = {3, 1, 4, 1, 5};
std::sort(vec.begin(), vec.end());
auto it = std::find(vec.begin(), vec.end(), 4);
编译与构建
1. 编译过程
C++需要显式编译,而Java是解释执行:
# 编译
g++ -std=c++17 main.cpp -o program
# 运行
./program
2. 头文件
C++使用头文件声明接口:
// myclass.h
class MyClass {
public:
void foo();
};
// myclass.cpp
#include "myclass.h"
void MyClass::foo() {
// 实现
}
常见陷阱与最佳实践
1. 内存泄漏
避免使用原始指针,优先使用智能指针:
// 不推荐
MyClass* ptr = new MyClass();
// 可能忘记delete
// 推荐
auto ptr = std::make_unique<MyClass>();
// 自动管理内存
2. 字符串处理
使用std::string
而不是C风格字符串:
// 不推荐
char* str = "Hello";
// 推荐
std::string str = "Hello";
3. 现代C++特性
优先使用现代C++特性:
// 范围for循环
for (const auto& item : container) {
// 处理item
}
// lambda表达式
auto add = [](int a, int b) { return a + b; };
C++特有特性
1. 友元函数和友元类
C++允许非成员函数或类访问类的私有成员:
class MyClass {
private:
int secret;
// 声明友元函数
friend void friendFunction(MyClass& obj);
// 声明友元类
friend class FriendClass;
};
// 友元函数实现
void friendFunction(MyClass& obj) {
obj.secret = 42; // 可以直接访问私有成员
}
class FriendClass {
public:
void accessSecret(MyClass& obj) {
obj.secret = 100; // 可以直接访问私有成员
}
};
2. 运算符重载
C++允许重载运算符,使自定义类型支持运算符操作:
class Complex {
private:
double real, imag;
public:
Complex(double r, double i) : real(r), imag(i) {}
// 重载+运算符
Complex operator+(const Complex& other) const {
return Complex(real + other.real, imag + other.imag);
}
// 重载输出运算符
friend std::ostream& operator<<(std::ostream& os, const Complex& c) {
os << c.real << " + " << c.imag << "i";
return os;
}
};
// 使用示例
Complex c1(1, 2), c2(3, 4);
Complex c3 = c1 + c2; // 使用重载的+运算符
std::cout << c3; // 使用重载的<<运算符
3. 模板编程
C++的模板系统比Java的泛型更强大:
// 函数模板
template<typename T>
T max(T a, T b) {
return (a > b) ? a : b;
}
// 类模板
template<typename T>
class Stack {
private:
std::vector<T> elements;
public:
void push(T const& elem) {
elements.push_back(elem);
}
T pop() {
T elem = elements.back();
elements.pop_back();
return elem;
}
};
// 使用示例
int i = max(10, 20); // 使用int类型
double d = max(3.14, 2.72); // 使用double类型
Stack<int> intStack; // 整数栈
Stack<std::string> stringStack; // 字符串栈
4. 移动语义和右值引用
C++11引入的移动语义可以优化性能:
class Resource {
private:
int* data;
public:
// 移动构造函数
Resource(Resource&& other) noexcept : data(other.data) {
other.data = nullptr;
}
// 移动赋值运算符
Resource& operator=(Resource&& other) noexcept {
if (this != &other) {
delete data;
data = other.data;
other.data = nullptr;
}
return *this;
}
};
// 使用示例
Resource r1;
Resource r2 = std::move(r1); // 使用移动语义
5. 命名空间
C++使用命名空间来组织代码:
namespace MyLib {
class MyClass {
// ...
};
void myFunction() {
// ...
}
}
// 使用方式
MyLib::MyClass obj;
MyLib::myFunction();
// 或者使用using声明
using namespace MyLib;
MyClass obj;
myFunction();
6. 预处理器指令
C++的预处理器提供了强大的编译时功能:
// 条件编译
#ifdef DEBUG
#define LOG(x) std::cout << x << std::endl
#else
#define LOG(x)
#endif
// 防止头文件重复包含
#ifndef MY_HEADER_H
#define MY_HEADER_H
// 头文件内容
#endif
// 内联函数
inline int square(int x) {
return x * x;
}
7. 异常处理
C++的异常处理机制:
class MyException : public std::exception {
public:
const char* what() const noexcept override {
return "My custom exception";
}
};
void riskyFunction() {
throw MyException();
}
// 使用示例
try {
riskyFunction();
} catch (const MyException& e) {
std::cerr << e.what() << std::endl;
} catch (const std::exception& e) {
std::cerr << "Standard exception: " << e.what() << std::endl;
} catch (...) {
std::cerr << "Unknown exception" << std::endl;
}
8. 函数对象和Lambda表达式
C++支持函数式编程特性:
// 函数对象
class Adder {
private:
int value;
public:
Adder(int v) : value(v) {}
int operator()(int x) const {
return x + value;
}
};
// Lambda表达式
auto multiply = [](int x, int y) { return x * y; };
auto add = [value = 10](int x) { return x + value; };
// 使用示例
Adder add5(5);
int result1 = add5(10); // 15
int result2 = multiply(3, 4); // 12
int result3 = add(5); // 15
9. 多重继承和虚继承
C++支持多重继承,这是Java所没有的特性:
// 普通多重继承
class Animal {
public:
virtual void eat() = 0;
};
class Flying {
public:
virtual void fly() = 0;
};
class Bird : public Animal, public Flying {
public:
void eat() override { /* 实现 */ }
void fly() override { /* 实现 */ }
};
// 虚继承(解决菱形继承问题)
class Base {
public:
int value;
};
class Derived1 : virtual public Base {};
class Derived2 : virtual public Base {};
class Final : public Derived1, public Derived2 {
// 只有一个value成员
};
10. 类型转换
C++提供了多种类型转换方式:
// 1. C风格转换(不推荐)
int i = (int)3.14;
// 2. static_cast:编译时类型检查
double d = 3.14;
int i = static_cast<int>(d);
// 3. dynamic_cast:运行时类型检查(用于多态)
Base* base = new Derived();
Derived* derived = dynamic_cast<Derived*>(base);
// 4. const_cast:移除const属性
const int* p = &i;
int* q = const_cast<int*>(p);
// 5. reinterpret_cast:底层位模式转换
int* p = reinterpret_cast<int*>(0x1234);
11. 可变参数模板
C++支持可变参数模板,比Java的变长参数更强大:
// 可变参数函数模板
template<typename... Args>
void print(Args... args) {
(std::cout << ... << args) << std::endl;
}
// 可变参数类模板
template<typename... Types>
class Tuple;
template<typename Head, typename... Tail>
class Tuple<Head, Tail...> : public Tuple<Tail...> {
Head head;
public:
Tuple(Head h, Tail... t) : Tuple<Tail...>(t...), head(h) {}
};
// 使用示例
print(1, "hello", 3.14); // 打印多个参数
Tuple<int, string, double> t(1, "hello", 3.14);
12. 完美转发
C++11引入的完美转发可以保持参数的值类别:
template<typename T>
void wrapper(T&& arg) {
foo(std::forward<T>(arg)); // 完美转发
}
// 使用示例
wrapper(42); // 右值
int x = 42;
wrapper(x); // 左值
wrapper(std::move(x)); // 右值
13. constexpr和编译期计算
C++支持在编译期进行计算:
// constexpr函数
constexpr int factorial(int n) {
return (n <= 1) ? 1 : (n * factorial(n - 1));
}
// constexpr变量
constexpr int max_size = 100;
// 编译期计算
constexpr int result = factorial(10); // 在编译期计算
// if constexpr(C++17)
template<typename T>
auto getValue(T t) {
if constexpr (std::is_integral_v<T>) {
return t * 2;
} else {
return t;
}
}
14. 并发编程
C++11引入了标准线程库:
#include <thread>
#include <mutex>
#include <future>
// 线程
void threadFunction() {
// 线程代码
}
std::thread t(threadFunction);
t.join();
// 互斥锁
std::mutex mtx;
{
std::lock_guard<std::mutex> lock(mtx);
// 临界区
}
// 异步任务
auto future = std::async(std::launch::async, []() {
return 42;
});
int result = future.get();
15. 内存模型和原子操作
C++11引入了内存模型和原子操作:
#include <atomic>
std::atomic<int> counter{0};
void increment() {
counter.fetch_add(1, std::memory_order_relaxed);
}
// 内存序
std::atomic<int> x{0}, y{0};
void thread1() {
x.store(1, std::memory_order_release);
y.store(1, std::memory_order_release);
}
void thread2() {
while (y.load(std::memory_order_acquire) != 1);
assert(x.load(std::memory_order_acquire) == 1);
}
16. 概念(Concepts)和约束
C++20引入的概念系统提供了更强大的模板约束:
// 定义概念
template<typename T>
concept Number = std::is_arithmetic_v<T>;
// 使用概念约束
template<Number T>
T add(T a, T b) {
return a + b;
}
// 组合概念
template<typename T>
concept Sortable = requires(T a) {
{ a < a } -> std::convertible_to<bool>;
{ std::sort(&a, &a) };
};
// 使用示例
template<Sortable T>
void sortContainer(T& container) {
std::sort(container.begin(), container.end());
}
17. 协程(Coroutines)
C++20引入的协程支持异步编程:
#include <coroutine>
#include <future>
// 定义协程返回类型
struct Task {
struct promise_type {
Task get_return_object() { return {}; }
std::suspend_never initial_suspend() { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void return_void() {}
void unhandled_exception() {}
};
};
// 异步函数
Task asyncFunction() {
co_await std::suspend_never{};
// 异步操作
}
18. 模块(Modules)
C++20引入的模块系统提供了更好的代码组织方式:
// math.cppm
module;
#include <cmath>
export module math;
export namespace math {
double square(double x) { return x * x; }
double cube(double x) { return x * x * x; }
}
// main.cpp
import math;
import <iostream>;
int main() {
std::cout << math::square(5) << std::endl;
return 0;
}
19. 反射(Reflection)
C++23计划引入的反射特性:
// 类型反射
template<typename T>
void printTypeInfo() {
std::cout << "Type name: " << type_name<T>() << std::endl;
std::cout << "Size: " << sizeof(T) << std::endl;
std::cout << "Alignment: " << alignof(T) << std::endl;
}
// 成员反射
template<typename T>
void printMembers(const T& obj) {
for_each_member(obj, [](auto name, auto value) {
std::cout << name << ": " << value << std::endl;
});
}
20. 高级元编程技巧
// 1. SFINAE (Substitution Failure Is Not An Error)
template<typename T>
typename std::enable_if<std::is_integral<T>::value, T>::type
increment(T x) {
return x + 1;
}
// 2. 类型特征
template<typename T>
struct is_container {
static constexpr bool value =
requires(T t) {
t.begin();
t.end();
t.size();
};
};
// 3. 完美转发
template<typename... Args>
void wrapper(Args&&... args) {
foo(std::forward<Args>(args)...);
}
// 4. 变参模板展开
template<typename... Args>
void print(Args&&... args) {
(std::cout << ... << args) << std::endl;
}
21. 内存管理高级特性
// 1. 自定义分配器
template<typename T>
class MyAllocator {
public:
using value_type = T;
T* allocate(std::size_t n) {
return static_cast<T*>(::operator new(n * sizeof(T)));
}
void deallocate(T* p, std::size_t n) {
::operator delete(p);
}
};
// 2. 内存池
class MemoryPool {
std::vector<char> memory;
std::size_t used = 0;
public:
void* allocate(std::size_t size) {
if (used + size > memory.size()) {
throw std::bad_alloc();
}
void* ptr = &memory[used];
used += size;
return ptr;
}
};
// 3. 智能指针自定义删除器
auto deleter = [](int* p) {
std::cout << "Deleting " << *p << std::endl;
delete p;
};
std::unique_ptr<int, decltype(deleter)> ptr(new int(42), deleter);
22. 并发编程高级特性
// 1. 原子操作
std::atomic<int> counter{0};
counter.fetch_add(1, std::memory_order_relaxed);
// 2. 内存序
std::atomic<int> x{0}, y{0};
void thread1() {
x.store(1, std::memory_order_release);
y.store(1, std::memory_order_release);
}
// 3. 条件变量
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void waitForReady() {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; });
}
// 4. 读写锁
std::shared_mutex rwMutex;
void read() {
std::shared_lock lock(rwMutex);
// 读取操作
}
void write() {
std::unique_lock lock(rwMutex);
// 写入操作
}
实际开发中的最佳实践
1. 代码组织
// 头文件组织
// myproject/
// ├── include/ // 公共头文件
// │ └── myproject/
// │ ├── core.h
// │ └── utils.h
// ├── src/ // 源文件
// │ ├── core.cpp
// │ └── utils.cpp
// ├── tests/ // 测试文件
// └── CMakeLists.txt // 构建配置
// 头文件保护
#ifndef MYPROJECT_CORE_H
#define MYPROJECT_CORE_H
namespace myproject {
class Core {
// ...
};
}
#endif
2. 错误处理策略
// 1. 异常处理
class DatabaseError : public std::runtime_error {
public:
explicit DatabaseError(const std::string& msg)
: std::runtime_error(msg) {}
};
// 2. 错误码
enum class ErrorCode {
Success,
FileNotFound,
PermissionDenied,
// ...
};
// 3. 可选值(C++17)
std::optional<int> findValue(const std::vector<int>& vec, int target) {
auto it = std::find(vec.begin(), vec.end(), target);
if (it != vec.end()) {
return *it;
}
return std::nullopt;
}
// 4. 预期值(C++23)
std::expected<int, std::string> divide(int a, int b) {
if (b == 0) {
return std::unexpected("Division by zero");
}
return a / b;
}
3. 性能优化技巧
// 1. 移动语义优化
class Resource {
std::vector<int> data;
public:
// 移动构造函数
Resource(Resource&& other) noexcept
: data(std::move(other.data)) {}
// 移动赋值运算符
Resource& operator=(Resource&& other) noexcept {
if (this != &other) {
data = std::move(other.data);
}
return *this;
}
};
// 2. 内联函数
inline int square(int x) {
return x * x;
}
// 3. 编译期计算
constexpr int fibonacci(int n) {
return (n <= 1) ? n : fibonacci(n-1) + fibonacci(n-2);
}
// 4. 内存对齐
struct alignas(16) AlignedStruct {
double x;
double y;
};
4. 调试技巧
// 1. 断言
#include <cassert>
void process(int* ptr) {
assert(ptr != nullptr && "Pointer cannot be null");
// ...
}
// 2. 日志
#include <iostream>
#define LOG_LEVEL 1
#define LOG(level, msg) \
if (level <= LOG_LEVEL) \
std::cout << "[" << level << "] " << msg << std::endl
// 3. 调试宏
#ifdef DEBUG
#define DBG_PRINT(x) std::cout << #x << " = " << x << std::endl
#else
#define DBG_PRINT(x)
#endif
5. 测试框架使用
// 使用Google Test框架
#include <gtest/gtest.h>
TEST(MyTest, BasicTest) {
EXPECT_EQ(2 + 2, 4);
EXPECT_TRUE(true);
EXPECT_FALSE(false);
}
// 参数化测试
class MyTest : public ::testing::TestWithParam<int> {};
TEST_P(MyTest, ParamTest) {
int value = GetParam();
EXPECT_GT(value, 0);
}
INSTANTIATE_TEST_SUITE_P(
Values, MyTest,
::testing::Values(1, 2, 3, 4, 5)
);
6. 构建系统
# CMakeLists.txt 示例
cmake_minimum_required(VERSION 3.10)
project(MyProject)
# 设置C++标准
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# 添加可执行文件
add_executable(myapp src/main.cpp)
# 添加库
add_library(mylib src/lib.cpp)
# 链接库
target_link_libraries(myapp PRIVATE mylib)
# 添加测试
enable_testing()
add_subdirectory(tests)
7. 代码质量工具
# 1. 代码格式化 (clang-format)
clang-format -i *.cpp *.h
# 2. 静态分析 (clang-tidy)
clang-tidy *.cpp -checks=*
# 3. 内存检查 (valgrind)
valgrind --leak-check=full ./myapp
# 4. 性能分析 (gprof)
g++ -pg myapp.cpp -o myapp
./myapp
gprof myapp gmon.out > analysis.txt
学习资源
总结
从Java转向C++需要注意以下几点:
- 理解内存管理:从自动垃圾回收转向手动/智能指针管理
- 掌握指针和引用:这是C++的核心概念
- 熟悉标准库:特别是STL容器和算法
- 注意编译过程:理解头文件、链接等概念
- 采用现代C++:使用C++11及以后版本的新特性
- 学习C++特有特性:友元、运算符重载、模板等
- 理解移动语义:这是现代C++性能优化的关键
- 掌握预处理器:了解条件编译和宏定义的使用
- 理解多重继承:这是C++特有的面向对象特性
- 掌握类型转换:了解各种类型转换的适用场景
- 学习并发编程:理解C++的线程模型和内存模型
- 重视编译期计算:利用constexpr提高运行时性能
- 注重代码质量:使用静态分析工具和测试框架
- 掌握构建系统:理解CMake等构建工具的使用
- 重视性能优化:合理使用移动语义和编译期计算
- 了解新特性:掌握C++20/23引入的概念、协程、模块等
- 深入元编程:理解SFINAE、类型特征等高级特性
- 掌握内存管理:了解自定义分配器和内存池
- 精通并发:理解原子操作、内存序和同步原语
- 关注发展:跟踪C++标准的发展和新特性
记住:C++给予你更多的控制权,但也需要更多的责任。正确使用C++的特性可以写出高效且安全的代码。在实际开发中,要注意代码组织、错误处理、性能优化和调试技巧,这些都是成为优秀C++开发者的关键。同时,要持续关注C++标准的发展,及时掌握新特性,这样才能写出更现代、更高效的C++代码。
C++面试题精选
1. 基础概念题
1.1 内存管理
// 问题:以下代码有什么问题?
void func() {
int* ptr = new int[10];
// ... 使用ptr
return; // 内存泄漏
}
// 解答:应该使用智能指针或确保释放内存
void func() {
std::unique_ptr<int[]> ptr(new int[10]);
// ... 使用ptr
// 自动释放内存
}
1.2 指针和引用
// 问题:以下代码输出什么?
int a = 10;
int& ref = a;
int* ptr = &a;
ref = 20;
*ptr = 30;
std::cout << a << std::endl; // 输出:30
// 问题:以下代码有什么问题?
int& func() {
int x = 10;
return x; // 返回局部变量的引用
}
1.3 虚函数和多态
// 问题:以下代码输出什么?
class Base {
public:
virtual void foo() { std::cout << "Base" << std::endl; }
virtual ~Base() {}
};
class Derived : public Base {
public:
void foo() override { std::cout << "Derived" << std::endl; }
};
Base* ptr = new Derived();
ptr->foo(); // 输出:Derived
delete ptr;
2. 算法和数据结构题
2.1 实现智能指针
// 问题:实现一个简单的智能指针
template<typename T>
class SmartPtr {
T* ptr;
public:
SmartPtr(T* p = nullptr) : ptr(p) {}
~SmartPtr() { delete ptr; }
T& operator*() { return *ptr; }
T* operator->() { return ptr; }
// 禁用拷贝
SmartPtr(const SmartPtr&) = delete;
SmartPtr& operator=(const SmartPtr&) = delete;
// 允许移动
SmartPtr(SmartPtr&& other) : ptr(other.ptr) {
other.ptr = nullptr;
}
};
2.2 实现线程安全的单例
// 问题:实现线程安全的单例模式
class Singleton {
private:
static std::atomic<Singleton*> instance;
static std::mutex mutex;
Singleton() {}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
public:
static Singleton* getInstance() {
Singleton* p = instance.load(std::memory_order_acquire);
if (!p) {
std::lock_guard<std::mutex> lock(mutex);
p = instance.load(std::memory_order_relaxed);
if (!p) {
p = new Singleton();
instance.store(p, std::memory_order_release);
}
}
return p;
}
};
3. 系统设计题
3.1 实现线程池
// 问题:实现一个简单的线程池
class ThreadPool {
std::vector<std::thread> workers;
std::queue<std::function<void()>> tasks;
std::mutex queue_mutex;
std::condition_variable condition;
bool stop;
public:
ThreadPool(size_t threads) : stop(false) {
for(size_t i = 0; i < threads; ++i)
workers.emplace_back([this] {
while(true) {
std::function<void()> task;
{
std::unique_lock<std::mutex> lock(queue_mutex);
condition.wait(lock, [this] {
return stop || !tasks.empty();
});
if(stop && tasks.empty()) return;
task = std::move(tasks.front());
tasks.pop();
}
task();
}
});
}
template<class F>
void enqueue(F&& f) {
{
std::unique_lock<std::mutex> lock(queue_mutex);
tasks.emplace(std::forward<F>(f));
}
condition.notify_one();
}
~ThreadPool() {
{
std::unique_lock<std::mutex> lock(queue_mutex);
stop = true;
}
condition.notify_all();
for(std::thread &worker: workers)
worker.join();
}
};
3.2 实现内存池
// 问题:实现一个简单的内存池
class MemoryPool {
struct Block {
Block* next;
};
Block* freeList;
std::mutex mutex;
public:
MemoryPool() : freeList(nullptr) {}
void* allocate(size_t size) {
std::lock_guard<std::mutex> lock(mutex);
if (!freeList) {
// 分配新的内存块
freeList = static_cast<Block*>(::operator new(size));
freeList->next = nullptr;
}
Block* block = freeList;
freeList = freeList->next;
return block;
}
void deallocate(void* ptr, size_t size) {
std::lock_guard<std::mutex> lock(mutex);
Block* block = static_cast<Block*>(ptr);
block->next = freeList;
freeList = block;
}
};
4. 性能优化题
4.1 优化字符串处理
// 问题:优化字符串连接操作
// 不推荐
std::string result;
for (int i = 0; i < 1000; ++i) {
result += "hello"; // 频繁的内存分配
}
// 推荐
std::string result;
result.reserve(5000); // 预分配内存
for (int i = 0; i < 1000; ++i) {
result += "hello";
}
4.2 优化内存分配
// 问题:优化频繁的小对象分配
// 不推荐
std::vector<SmallObject> objects;
for (int i = 0; i < 1000; ++i) {
objects.push_back(SmallObject()); // 频繁的内存分配
}
// 推荐
std::vector<SmallObject> objects;
objects.reserve(1000); // 预分配内存
for (int i = 0; i < 1000; ++i) {
objects.emplace_back(); // 原地构造
}
5. 实际项目题
5.1 实现日志系统
// 问题:实现一个线程安全的日志系统
class Logger {
std::ofstream logFile;
std::mutex mutex;
public:
Logger(const std::string& filename) {
logFile.open(filename, std::ios::app);
}
template<typename... Args>
void log(Args&&... args) {
std::lock_guard<std::mutex> lock(mutex);
auto now = std::chrono::system_clock::now();
auto time = std::chrono::system_clock::to_time_t(now);
logFile << std::ctime(&time) << " ";
(logFile << ... << std::forward<Args>(args)) << std::endl;
}
};
5.2 实现配置系统
// 问题:实现一个配置系统
class Config {
std::unordered_map<std::string, std::string> settings;
std::mutex mutex;
public:
void set(const std::string& key, const std::string& value) {
std::lock_guard<std::mutex> lock(mutex);
settings[key] = value;
}
std::string get(const std::string& key) const {
std::lock_guard<std::mutex> lock(mutex);
auto it = settings.find(key);
return it != settings.end() ? it->second : "";
}
bool loadFromFile(const std::string& filename) {
std::ifstream file(filename);
if (!file) return false;
std::string line;
while (std::getline(file, line)) {
size_t pos = line.find('=');
if (pos != std::string::npos) {
std::string key = line.substr(0, pos);
std::string value = line.substr(pos + 1);
set(key, value);
}
}
return true;
}
};
6. 面试技巧
- 理解问题:确保完全理解面试官的问题,可以适当提问澄清
- 分析思路:先说明解题思路,再开始编码
- 代码质量:注意代码的健壮性、可读性和性能
- 边界情况:考虑异常情况、内存泄漏、线程安全等
- 优化改进:说明可能的优化方向
- 知识扩展:展示对C++新特性的了解
7. 常见问题类型
- 内存管理:智能指针、内存泄漏、内存池
- 多线程:线程安全、死锁、同步原语
- 设计模式:单例、工厂、观察者等
- 性能优化:内存分配、字符串处理、算法优化
- 系统设计:日志系统、配置系统、缓存系统
- 语言特性:移动语义、完美转发、模板元编程
记住:面试不仅是考察知识,更是考察解决问题的能力和思维方式。在回答问题时,要展示你的思考过程,说明你的设计决策,并考虑代码的可维护性和可扩展性。
8. C++语法和易错点
8.1 初始化相关
// 问题:以下初始化方式有什么区别?
int a = 10; // 拷贝初始化
int b(10); // 直接初始化
int c{10}; // 列表初始化
int d = {10}; // 拷贝列表初始化
// 问题:以下代码有什么问题?
class MyClass {
int x; // 未初始化
std::string s; // 默认初始化
public:
MyClass() { // 构造函数中未初始化x
// x未初始化
}
};
// 正确做法
class MyClass {
int x{0}; // 类内初始化
std::string s;
public:
MyClass() = default; // 使用默认构造函数
};
8.2 移动语义和完美转发
// 问题:以下代码有什么问题?
void process(std::string str) {
// 处理str
}
std::string getString() {
return "hello";
}
process(getString()); // 发生不必要的拷贝
// 正确做法
void process(std::string&& str) {
// 处理str
}
// 或者使用完美转发
template<typename T>
void process(T&& str) {
// 处理str
}
8.3 智能指针使用
// 问题:以下代码有什么问题?
std::shared_ptr<int> p1(new int(10));
std::shared_ptr<int> p2 = p1;
std::shared_ptr<int> p3 = p1; // 引用计数为3
// 问题:以下代码有什么问题?
void func(std::shared_ptr<int> p) {
// 使用p
}
func(new int(10)); // 错误:不能隐式转换
// 正确做法
func(std::make_shared<int>(10));
8.4 异常安全
// 问题:以下代码有什么问题?
class Resource {
int* ptr;
public:
Resource() : ptr(new int(10)) {}
~Resource() { delete ptr; }
void setValue(int value) {
delete ptr; // 可能抛出异常
ptr = new int(value); // 可能抛出异常
}
};
// 正确做法
class Resource {
std::unique_ptr<int> ptr;
public:
Resource() : ptr(std::make_unique<int>(10)) {}
void setValue(int value) {
ptr = std::make_unique<int>(value);
}
};
8.5 多线程安全
// 问题:以下代码有什么问题?
class Counter {
int count;
public:
void increment() { ++count; } // 非线程安全
int getCount() { return count; }
};
// 正确做法
class Counter {
std::atomic<int> count{0};
public:
void increment() { ++count; }
int getCount() { return count; }
};
9. C++代码规范
9.1 命名规范
// 类名:大驼峰
class MyClass {
// 成员变量:下划线后缀
int count_;
std::string name_;
// 私有成员函数:下划线前缀
void _privateMethod();
public:
// 公共成员函数:小驼峰
void publicMethod();
// 常量:全大写
static const int MAX_COUNT = 100;
};
// 命名空间:小写
namespace my_project {
// 函数:小驼峰
void processData();
// 变量:小驼峰
int totalCount = 0;
}
9.2 代码组织
// 头文件组织
#ifndef MY_PROJECT_MY_CLASS_H
#define MY_PROJECT_MY_CLASS_H
#include <string>
#include <vector>
namespace my_project {
class MyClass {
public:
// 1. 公共类型定义
using value_type = int;
// 2. 公共常量
static constexpr int DEFAULT_SIZE = 10;
// 3. 构造函数和析构函数
MyClass();
explicit MyClass(int size);
~MyClass() = default;
// 4. 公共成员函数
void process();
// 5. 运算符重载
MyClass& operator+=(const MyClass& other);
private:
// 6. 私有成员
std::vector<int> data_;
};
} // namespace my_project
#endif
9.3 注释规范
/**
* @brief 类的简要描述
* @details 类的详细描述
*/
class MyClass {
public:
/**
* @brief 函数的简要描述
* @param param1 参数1的描述
* @param param2 参数2的描述
* @return 返回值的描述
* @throws 可能抛出的异常
*/
int process(int param1, const std::string& param2);
private:
// 成员变量的说明
int count_; ///< 计数器
};
9.4 错误处理
// 1. 使用异常处理错误
class DatabaseError : public std::runtime_error {
public:
explicit DatabaseError(const std::string& msg)
: std::runtime_error(msg) {}
};
// 2. 使用错误码
enum class ErrorCode {
Success,
FileNotFound,
PermissionDenied
};
// 3. 使用可选值
std::optional<int> findValue(const std::vector<int>& vec, int target);
// 4. 使用预期值
std::expected<int, std::string> divide(int a, int b);
9.5 性能优化
// 1. 使用移动语义
class Resource {
std::vector<int> data;
public:
Resource(Resource&& other) noexcept
: data(std::move(other.data)) {}
};
// 2. 使用引用避免拷贝
void process(const std::string& str);
// 3. 使用emplace_back
std::vector<MyClass> vec;
vec.emplace_back(10, "hello");
// 4. 使用reserve预分配内存
std::vector<int> vec;
vec.reserve(1000);
10. 常见陷阱
10.1 对象生命周期
// 问题:以下代码有什么问题?
class MyClass {
std::string& str; // 引用成员
public:
MyClass(std::string& s) : str(s) {}
};
void func() {
std::string s = "hello";
MyClass obj(s); // 引用可能悬空
} // s被销毁,obj.str成为悬空引用
// 正确做法
class MyClass {
std::string str; // 值成员
public:
MyClass(const std::string& s) : str(s) {}
};
10.2 迭代器失效
// 问题:以下代码有什么问题?
std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto it = vec.begin(); it != vec.end(); ++it) {
if (*it == 3) {
vec.erase(it); // 迭代器失效
}
}
// 正确做法
std::vector<int> vec = {1, 2, 3, 4, 5};
vec.erase(
std::remove_if(vec.begin(), vec.end(),
[](int x) { return x == 3; }),
vec.end()
);
10.3 多线程同步
// 问题:以下代码有什么问题?
class Counter {
int count;
std::mutex mtx;
public:
void increment() {
std::lock_guard<std::mutex> lock(mtx);
++count;
}
int getCount() {
return count; // 未加锁
}
};
// 正确做法
class Counter {
std::atomic<int> count{0};
public:
void increment() {
++count;
}
int getCount() {
return count;
}
};
10.4 资源管理
// 问题:以下代码有什么问题?
void processFile() {
FILE* file = fopen("data.txt", "r");
// 处理文件
// 可能提前返回或抛出异常
fclose(file);
}
// 正确做法
void processFile() {
std::ifstream file("data.txt");
// 处理文件
// 自动关闭文件
}
记住:在C++编程中,要注意:
- 正确使用初始化语法
- 理解移动语义和完美转发
- 合理使用智能指针
- 确保异常安全
- 保证线程安全
- 遵循代码规范
- 避免常见陷阱
- 注意资源管理
- 重视性能优化
- 保持代码可维护性
11. 智能指针详解
11.1 智能指针类型
- unique_ptr
// 独占所有权的智能指针,不能复制,只能移动
std::unique_ptr<int> p1 = std::make_unique<int>(10);
// std::unique_ptr<int> p2 = p1; // 错误:不能复制
std::unique_ptr<int> p2 = std::move(p1); // 正确:可以移动
// 自定义删除器
auto deleter = [](int* p) {
std::cout << "Deleting " << *p << std::endl;
delete p;
};
std::unique_ptr<int, decltype(deleter)> p3(new int(20), deleter);
// 数组版本
std::unique_ptr<int[]> arr = std::make_unique<int[]>(10);
- shared_ptr
// 共享所有权的智能指针,使用引用计数
std::shared_ptr<int> p1 = std::make_shared<int>(10);
std::shared_ptr<int> p2 = p1; // 引用计数+1
std::shared_ptr<int> p3 = p1; // 引用计数+1
// p1, p2, p3都指向同一个对象,引用计数为3
// 自定义删除器
std::shared_ptr<FILE> file(
fopen("data.txt", "r"),
[](FILE* f) { fclose(f); }
);
// 循环引用问题
class Node {
public:
std::shared_ptr<Node> next;
// 可能导致循环引用
};
// 解决循环引用:使用weak_ptr
class Node {
public:
std::weak_ptr<Node> next; // 不会增加引用计数
};
- weak_ptr
// 弱引用,不增加引用计数
std::shared_ptr<int> sp = std::make_shared<int>(10);
std::weak_ptr<int> wp = sp; // 不会增加引用计数
// 检查对象是否还存在
if (auto sp2 = wp.lock()) { // 尝试获取shared_ptr
// 对象还存在,可以使用sp2
} else {
// 对象已经被销毁
}
// 用于解决循环引用
class Parent;
class Child {
std::weak_ptr<Parent> parent; // 不会导致循环引用
};
- auto_ptr (已废弃)
// C++98引入,C++11废弃,C++17移除
// 不要使用auto_ptr,使用unique_ptr代替
std::auto_ptr<int> p1(new int(10)); // 不推荐
std::unique_ptr<int> p2(new int(10)); // 推荐
11.2 智能指针最佳实践
- 优先使用make函数
// 不推荐
std::shared_ptr<int> p1(new int(10));
std::unique_ptr<int> p2(new int(10));
// 推荐
auto p1 = std::make_shared<int>(10);
auto p2 = std::make_unique<int>(10);
// 原因:
// 1. 异常安全
// 2. 性能更好(shared_ptr只需要一次内存分配)
// 3. 代码更简洁
- 避免循环引用
// 错误示例:循环引用
class Parent {
std::shared_ptr<Child> child;
};
class Child {
std::shared_ptr<Parent> parent;
};
// 正确示例:使用weak_ptr
class Parent {
std::shared_ptr<Child> child;
};
class Child {
std::weak_ptr<Parent> parent;
};
- 合理选择智能指针类型
// 独占所有权:使用unique_ptr
class Resource {
std::unique_ptr<Data> data;
public:
Resource() : data(std::make_unique<Data>()) {}
};
// 共享所有权:使用shared_ptr
class Cache {
std::shared_ptr<Data> data;
public:
void setData(std::shared_ptr<Data> newData) {
data = newData;
}
};
// 观察者模式:使用weak_ptr
class Observer {
std::weak_ptr<Subject> subject;
public:
void observe(std::shared_ptr<Subject> s) {
subject = s;
}
};
- 避免原始指针和智能指针混用
// 不推荐
void process(int* ptr) {
std::shared_ptr<int> sp(ptr); // 危险:可能导致重复删除
}
// 推荐
void process(std::shared_ptr<int> sp) {
// 使用智能指针
}
- 自定义删除器
// 文件句柄
auto fileDeleter = [](FILE* f) {
if (f) fclose(f);
};
std::unique_ptr<FILE, decltype(fileDeleter)> file(
fopen("data.txt", "r"),
fileDeleter
);
// 内存池
class MemoryPool {
public:
void* allocate(size_t size);
void deallocate(void* ptr);
};
auto poolDeleter = [](void* ptr) {
MemoryPool::instance().deallocate(ptr);
};
std::unique_ptr<int, decltype(poolDeleter)> data(
static_cast<int*>(MemoryPool::instance().allocate(sizeof(int))),
poolDeleter
);
- 智能指针和容器
// 使用智能指针作为容器元素
std::vector<std::unique_ptr<Shape>> shapes;
shapes.push_back(std::make_unique<Circle>());
shapes.push_back(std::make_unique<Square>());
// 使用智能指针管理容器
class Container {
std::unique_ptr<std::vector<int>> data;
public:
Container() : data(std::make_unique<std::vector<int>>()) {}
};
- 智能指针和异常安全
// 不推荐
void process() {
int* ptr = new int(10);
// 如果这里抛出异常,会导致内存泄漏
delete ptr;
}
// 推荐
void process() {
auto ptr = std::make_unique<int>(10);
// 即使抛出异常,ptr也会自动释放
}
- 智能指针和多线程
// shared_ptr是线程安全的
std::shared_ptr<int> sp = std::make_shared<int>(10);
// 多线程安全地修改shared_ptr
std::mutex mtx;
{
std::lock_guard<std::mutex> lock(mtx);
sp = std::make_shared<int>(20);
}
// 使用atomic<shared_ptr>
std::atomic<std::shared_ptr<int>> asp;
asp.store(std::make_shared<int>(10));
记住:
- 优先使用make函数创建智能指针
- 根据所有权语义选择合适的智能指针类型
- 使用weak_ptr避免循环引用
- 避免原始指针和智能指针混用
- 合理使用自定义删除器
- 注意智能指针在多线程环境下的使用
- 利用智能指针实现异常安全
- 理解智能指针的性能开销
12. 面向对象编程详解
12.1 类的基本概念
- 类定义对比
// Java
public class MyClass {
private int x;
private String name;
public MyClass(int x, String name) {
this.x = x;
this.name = name;
}
public void setX(int x) {
this.x = x;
}
}
// C++
class MyClass {
private:
int x;
std::string name;
public:
// 构造函数
MyClass(int x, const std::string& name)
: x(x), name(name) {} // 初始化列表
// 成员函数
void setX(int x) {
this->x = x; // 使用->而不是.
}
};
- 访问修饰符
// Java: public, protected, private, default(package-private)
// C++: public, protected, private
class MyClass {
public: // 公有成员,任何地方都可以访问
int publicVar;
void publicFunc() {}
protected: // 保护成员,只有本类和子类可以访问
int protectedVar;
void protectedFunc() {}
private: // 私有成员,只有本类可以访问
int privateVar;
void privateFunc() {}
};
12.2 构造函数和析构函数
- 构造函数
class MyClass {
private:
int x;
std::string name;
public:
// 默认构造函数
MyClass() : x(0), name("") {}
// 带参数的构造函数
MyClass(int x, const std::string& name)
: x(x), name(name) {}
// 拷贝构造函数
MyClass(const MyClass& other)
: x(other.x), name(other.name) {}
// 移动构造函数
MyClass(MyClass&& other) noexcept
: x(other.x), name(std::move(other.name)) {}
// 委托构造函数
MyClass(int x) : MyClass(x, "default") {}
};
// 使用示例
MyClass obj1; // 默认构造
MyClass obj2(10, "test"); // 带参数构造
MyClass obj3(obj2); // 拷贝构造
MyClass obj4(std::move(obj3)); // 移动构造
- 析构函数
class Resource {
private:
int* data;
public:
Resource() : data(new int(0)) {}
// 虚析构函数(当类可能被继承时)
virtual ~Resource() {
delete data;
}
};
// 使用示例
{
Resource res; // 构造
} // 析构
12.3 继承和多态
- 继承
// 基类
class Base {
protected:
int x;
public:
Base(int x) : x(x) {}
virtual void foo() { std::cout << "Base::foo" << std::endl; }
virtual ~Base() = default;
};
// 公有继承
class Derived : public Base {
private:
int y;
public:
Derived(int x, int y) : Base(x), y(y) {}
void foo() override { std::cout << "Derived::foo" << std::endl; }
};
// 保护继承
class ProtectedDerived : protected Base {
// 基类的public和protected成员变为protected
};
// 私有继承
class PrivateDerived : private Base {
// 基类的所有成员变为private
};
- 多态
// 虚函数
class Shape {
public:
virtual double area() const = 0; // 纯虚函数
virtual ~Shape() = default;
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
double area() const override {
return 3.14159 * radius * radius;
}
};
// 使用示例
std::vector<std::unique_ptr<Shape>> shapes;
shapes.push_back(std::make_unique<Circle>(5));
for (const auto& shape : shapes) {
std::cout << shape->area() << std::endl;
}
- 多重继承
// 基类
class Animal {
public:
virtual void eat() = 0;
};
class Flying {
public:
virtual void fly() = 0;
};
// 多重继承
class Bird : public Animal, public Flying {
public:
void eat() override { std::cout << "Bird eating" << std::endl; }
void fly() override { std::cout << "Bird flying" << std::endl; }
};
// 虚继承(解决菱形继承问题)
class Base {
public:
int value;
};
class Derived1 : virtual public Base {};
class Derived2 : virtual public Base {};
class Final : public Derived1, public Derived2 {
// 只有一个value成员
};
12.4 特殊成员函数
- 拷贝控制
class MyClass {
private:
std::string data;
public:
// 拷贝构造函数
MyClass(const MyClass& other) : data(other.data) {}
// 拷贝赋值运算符
MyClass& operator=(const MyClass& other) {
if (this != &other) {
data = other.data;
}
return *this;
}
// 移动构造函数
MyClass(MyClass&& other) noexcept
: data(std::move(other.data)) {}
// 移动赋值运算符
MyClass& operator=(MyClass&& other) noexcept {
if (this != &other) {
data = std::move(other.data);
}
return *this;
}
};
- 运算符重载
class Complex {
private:
double real, imag;
public:
Complex(double r, double i) : real(r), imag(i) {}
// 运算符重载
Complex operator+(const Complex& other) const {
return Complex(real + other.real, imag + other.imag);
}
Complex& operator+=(const Complex& other) {
real += other.real;
imag += other.imag;
return *this;
}
// 友元函数
friend std::ostream& operator<<(std::ostream& os, const Complex& c) {
os << c.real << " + " << c.imag << "i";
return os;
}
};
12.5 友元
- 友元函数
class MyClass {
private:
int secret;
// 声明友元函数
friend void friendFunction(MyClass& obj);
public:
MyClass(int s) : secret(s) {}
};
// 友元函数实现
void friendFunction(MyClass& obj) {
obj.secret = 42; // 可以直接访问私有成员
}
- 友元类
class MyClass {
private:
int secret;
// 声明友元类
friend class FriendClass;
public:
MyClass(int s) : secret(s) {}
};
class FriendClass {
public:
void accessSecret(MyClass& obj) {
obj.secret = 100; // 可以直接访问私有成员
}
};
12.6 Java和C++面向对象的主要区别
-
内存管理
- Java:自动垃圾回收
- C++:手动内存管理或智能指针
-
多重继承
- Java:只支持接口的多重继承
- C++:支持类的多重继承
-
虚函数
- Java:所有方法默认都是虚函数
- C++:需要显式声明virtual
-
访问控制
- Java:public, protected, private, default
- C++:public, protected, private
-
构造函数
- Java:只能通过this()调用其他构造函数
- C++:支持初始化列表和委托构造函数
-
析构函数
- Java:没有析构函数,使用finalize()
- C++:有析构函数,支持RAII
-
运算符重载
- Java:不支持运算符重载
- C++:支持运算符重载
-
友元
- Java:没有友元概念
- C++:支持友元函数和友元类
-
默认函数
- Java:自动生成默认构造函数
- C++:可以控制是否生成默认函数
-
接口
- Java:使用interface关键字
- C++:使用纯虚类实现接口
记住:
- C++的面向对象特性比Java更灵活但更复杂
- 理解C++的内存管理机制
- 合理使用虚函数和继承
- 注意多重继承的复杂性
- 掌握运算符重载的使用
- 理解友元机制的作用
- 重视RAII原则
- 注意拷贝控制和移动语义
// ... existing code ...