乐闻世界logo
搜索文章和话题

面试题手册

C++ 设计模式如何实现

C++ 设计模式设计模式是软件设计中常见问题的可重用解决方案,C++ 作为一门强大的面向对象语言,非常适合实现各种设计模式。创建型模式单例模式(Singleton):class Singleton {private: static Singleton* instance; Singleton() = default; ~Singleton() = default;public: Singleton(const Singleton&) = delete; Singleton& operator=(const Singleton&) = delete; static Singleton& getInstance() { static Singleton instance; return instance; } void doSomething() { std::cout << "Singleton doing something" << std::endl; }};// 使用Singleton& singleton = Singleton::getInstance();singleton.doSomething();工厂模式(Factory):// 抽象产品class Product {public: virtual ~Product() = default; virtual void operation() = 0;};// 具体产品class ConcreteProductA : public Product {public: void operation() override { std::cout << "Product A operation" << std::endl; }};class ConcreteProductB : public Product {public: void operation() override { std::cout << "Product B operation" << std::endl; }};// 抽象工厂class Factory {public: virtual ~Factory() = default; virtual std::unique_ptr<Product> createProduct() = 0;};// 具体工厂class FactoryA : public Factory {public: std::unique_ptr<Product> createProduct() override { return std::make_unique<ConcreteProductA>(); }};class FactoryB : public Factory {public: std::unique_ptr<Product> createProduct() override { return std::make_unique<ConcreteProductB>(); }};// 使用std::unique_ptr<Factory> factory = std::make_unique<FactoryA>();auto product = factory->createProduct();product->operation();建造者模式(Builder):class Computer {private: std::string cpu; std::string gpu; int ram;public: void setCPU(const std::string& cpu) { this->cpu = cpu; } void setGPU(const std::string& gpu) { this->gpu = gpu; } void setRAM(int ram) { this->ram = ram; } void show() { std::cout << "CPU: " << cpu << ", GPU: " << gpu << ", RAM: " << ram << "GB" << std::endl; }};class ComputerBuilder {private: Computer computer;public: ComputerBuilder& setCPU(const std::string& cpu) { computer.setCPU(cpu); return *this; } ComputerBuilder& setGPU(const std::string& gpu) { computer.setGPU(gpu); return *this; } ComputerBuilder& setRAM(int ram) { computer.setRAM(ram); return *this; } Computer build() { return computer; }};// 使用Computer computer = ComputerBuilder() .setCPU("Intel i9") .setGPU("NVIDIA RTX 4090") .setRAM(32) .build();computer.show();结构型模式适配器模式(Adapter):// 目标接口class Target {public: virtual ~Target() = default; virtual void request() = 0;};// 被适配者class Adaptee {public: void specificRequest() { std::cout << "Adaptee specific request" << std::endl; }};// 适配器class Adapter : public Target {private: std::unique_ptr<Adaptee> adaptee;public: Adapter() : adaptee(std::make_unique<Adaptee>()) {} void request() override { adaptee->specificRequest(); }};// 使用std::unique_ptr<Target> target = std::make_unique<Adapter>();target->request();装饰器模式(Decorator):// 组件接口class Component {public: virtual ~Component() = default; virtual void operation() = 0;};// 具体组件class ConcreteComponent : public Component {public: void operation() override { std::cout << "ConcreteComponent operation" << std::endl; }};// 装饰器基类class Decorator : public Component {private: std::unique_ptr<Component> component;public: Decorator(std::unique_ptr<Component> comp) : component(std::move(comp)) {} void operation() override { component->operation(); }};// 具体装饰器class ConcreteDecoratorA : public Decorator {public: ConcreteDecoratorA(std::unique_ptr<Component> comp) : Decorator(std::move(comp)) {} void operation() override { Decorator::operation(); addedBehavior(); } void addedBehavior() { std::cout << "Added behavior A" << std::endl; }};// 使用std::unique_ptr<Component> component = std::make_unique<ConcreteComponent>();component = std::make_unique<ConcreteDecoratorA>(std::move(component));component->operation();外观模式(Facade):class SubsystemA {public: void operationA() { std::cout << "Subsystem A operation" << std::endl; }};class SubsystemB {public: void operationB() { std::cout << "Subsystem B operation" << std::endl; }};class SubsystemC {public: void operationC() { std::cout << "Subsystem C operation" << std::endl; }};// 外观class Facade {private: std::unique_ptr<SubsystemA> subsystemA; std::unique_ptr<SubsystemB> subsystemB; std::unique_ptr<SubsystemC> subsystemC;public: Facade() : subsystemA(std::make_unique<SubsystemA>()), subsystemB(std::make_unique<SubsystemB>()), subsystemC(std::make_unique<SubsystemC>()) {} void operation() { subsystemA->operationA(); subsystemB->operationB(); subsystemC->operationC(); }};// 使用Facade facade;facade.operation();行为型模式观察者模式(Observer):#include <vector>#include <functional>// 观察者接口class Observer {public: virtual ~Observer() = default; virtual void update(const std::string& message) = 0;};// 主题class Subject {private: std::vector<std::reference_wrapper<Observer>> observers;public: void attach(Observer& observer) { observers.push_back(observer); } void detach(Observer& observer) { observers.erase( std::remove_if(observers.begin(), observers.end(), [&observer](auto& obs) { return &obs.get() == &observer; }), observers.end()); } void notify(const std::string& message) { for (auto& observer : observers) { observer.get().update(message); } }};// 具体观察者class ConcreteObserver : public Observer {private: std::string name;public: ConcreteObserver(const std::string& n) : name(n) {} void update(const std::string& message) override { std::cout << name << " received: " << message << std::endl; }};// 使用Subject subject;ConcreteObserver observer1("Observer 1");ConcreteObserver observer2("Observer 2");subject.attach(observer1);subject.attach(observer2);subject.notify("Hello observers!");策略模式(Strategy):// 策略接口class Strategy {public: virtual ~Strategy() = default; virtual int execute(int a, int b) = 0;};// 具体策略class AddStrategy : public Strategy {public: int execute(int a, int b) override { return a + b; }};class MultiplyStrategy : public Strategy {public: int execute(int a, int b) override { return a * b; }};// 上下文class Context {private: std::unique_ptr<Strategy> strategy;public: void setStrategy(std::unique_ptr<Strategy> s) { strategy = std::move(s); } int executeStrategy(int a, int b) { return strategy->execute(a, b); }};// 使用Context context;context.setStrategy(std::make_unique<AddStrategy>());std::cout << context.executeStrategy(10, 20) << std::endl;context.setStrategy(std::make_unique<MultiplyStrategy>());std::cout << context.executeStrategy(10, 20) << std::endl;命令模式(Command):// 命令接口class Command {public: virtual ~Command() = default; virtual void execute() = 0; virtual void undo() = 0;};// 接收者class Receiver {public: void action() { std::cout << "Receiver action" << std::endl; } void reverseAction() { std::cout << "Receiver reverse action" << std::endl; }};// 具体命令class ConcreteCommand : public Command {private: Receiver& receiver;public: ConcreteCommand(Receiver& r) : receiver(r) {} void execute() override { receiver.action(); } void undo() override { receiver.reverseAction(); }};// 调用者class Invoker {private: std::vector<std::unique_ptr<Command>> commands;public: void setCommand(std::unique_ptr<Command> command) { commands.push_back(std::move(command)); } void executeCommands() { for (auto& command : commands) { command->execute(); } } void undoCommands() { for (auto it = commands.rbegin(); it != commands.rend(); ++it) { (*it)->undo(); } }};// 使用Receiver receiver;Invoker invoker;invoker.setCommand(std::make_unique<ConcreteCommand>(receiver));invoker.setCommand(std::make_unique<ConcreteCommand>(receiver));invoker.executeCommands();invoker.undoCommands();状态模式(State):// 状态接口class State {public: virtual ~State() = default; virtual void handle() = 0;};// 上下文class Context {private: std::unique_ptr<State> state;public: void setState(std::unique_ptr<State> s) { state = std::move(s); } void request() { state->handle(); }};// 具体状态class ConcreteStateA : public State {private: Context& context;public: ConcreteStateA(Context& ctx) : context(ctx) {} void handle() override { std::cout << "State A handling" << std::endl; context.setState(std::make_unique<ConcreteStateB>(context)); }};class ConcreteStateB : public State {private: Context& context;public: ConcreteStateB(Context& ctx) : context(ctx) {} void handle() override { std::cout << "State B handling" << std::endl; context.setState(std::make_unique<ConcreteStateA>(context)); }};// 使用Context context;context.setState(std::make_unique<ConcreteStateA>(context));context.request(); // State Acontext.request(); // State Bcontext.request(); // State A模板方法模式(Template Method)// 抽象类class AbstractClass {public: virtual ~AbstractClass() = default; void templateMethod() { primitiveOperation1(); primitiveOperation2(); hook(); }protected: virtual void primitiveOperation1() = 0; virtual void primitiveOperation2() = 0; virtual void hook() {} // 钩子方法,可选实现};// 具体类class ConcreteClass : public AbstractClass {protected: void primitiveOperation1() override { std::cout << "Primitive operation 1" << std::endl; } void primitiveOperation2() override { std::cout << "Primitive operation 2" << std::endl; } void hook() override { std::cout << "Hook called" << std::endl; }};// 使用std::unique_ptr<AbstractClass> obj = std::make_unique<ConcreteClass>();obj->templateMethod();责任链模式(Chain of Responsibility)// 处理者接口class Handler {protected: std::unique_ptr<Handler> next;public: virtual ~Handler() = default; void setNext(std::unique_ptr<Handler> h) { next = std::move(h); } virtual void handleRequest(int request) { if (next) { next->handleRequest(request); } }};// 具体处理者class ConcreteHandlerA : public Handler {public: void handleRequest(int request) override { if (request >= 0 && request < 10) { std::cout << "Handler A handles request " << request << std::endl; } else { Handler::handleRequest(request); } }};class ConcreteHandlerB : public Handler {public: void handleRequest(int request) override { if (request >= 10 && request < 20) { std::cout << "Handler B handles request " << request << std::endl; } else { Handler::handleRequest(request); } }};// 使用auto handlerA = std::make_unique<ConcreteHandlerA>();auto handlerB = std::make_unique<ConcreteHandlerB>();handlerA->setNext(std::move(handlerB));handlerA->handleRequest(5); // Handler AhandlerA->handleRequest(15); // Handler B最佳实践1. 优先使用组合而非继承// 推荐:组合class Engine {public: void start() { std::cout << "Engine started" << std::endl; }};class Car {private: Engine engine;public: void start() { engine.start(); }};// 不推荐:过度继承class Vehicle {public: virtual void start() = 0;};class Car : public Vehicle {public: void start() override { std::cout << "Car started" << std::endl; }};2. 使用智能指针管理对象生命周期// 推荐std::unique_ptr<Factory> factory = std::make_unique<FactoryA>();auto product = factory->createProduct();// 不推荐Factory* factory = new FactoryA();auto product = factory->createProduct();delete factory;3. 遵循 SOLID 原则单一职责原则(SRP)开闭原则(OCP)里氏替换原则(LSP)接口隔离原则(ISP)依赖倒置原则(DIP)4. 使用 RAII 确保资源管理class ResourceManager {private: std::unique_ptr<Resource> resource;public: ResourceManager() : resource(std::make_unique<Resource>()) {} // 析构函数自动释放资源};5. 避免过度设计// 简单场景不需要复杂模式int add(int a, int b) { return a + b;}// 复杂场景才使用设计模式class Calculator {public: virtual int calculate(int a, int b) = 0;};
阅读 0·2月18日 17:36

如何在 TensorFlow 中使用 tf.GradientTape 进行自动微分

tf.GradientTape 是 TensorFlow 2.x 中用于自动微分的核心 API,它允许我们计算函数相对于变量的梯度。这是训练神经网络的关键技术。基本概念什么是自动微分自动微分是一种计算数值梯度的技术,它通过链式法则自动计算复杂函数的导数。与数值微分和符号微分相比,自动微分结合了两者的优点:数值精度高计算效率高可以处理复杂的计算图tf.GradientTape 的工作原理tf.GradientTape 会记录在上下文管理器内执行的所有操作,构建计算图,然后可以通过反向传播计算梯度。基本用法1. 计算标量函数的梯度import tensorflow as tfx = tf.Variable(3.0)with tf.GradientTape() as tape: y = x ** 2# 计算 dy/dxdy_dx = tape.gradient(y, x)print(dy_dx) # 输出: tf.Tensor(6.0, shape=(), dtype=float32)2. 计算多元函数的梯度x = tf.Variable(2.0)y = tf.Variable(3.0)with tf.GradientTape() as tape: z = x ** 2 + y ** 3# 计算梯度dz_dx, dz_dy = tape.gradient(z, [x, y])print(dz_dx) # 输出: tf.Tensor(4.0, shape=(), dtype=float32)print(dz_dy) # 输出: tf.Tensor(27.0, shape=(), dtype=float32)3. 计算高阶导数x = tf.Variable(3.0)with tf.GradientTape() as tape2: with tf.GradientTape() as tape1: y = x ** 3 dy_dx = tape1.gradient(y, x)# 计算二阶导数d2y_dx2 = tape2.gradient(dy_dx, x)print(d2y_dx2) # 输出: tf.Tensor(18.0, shape=(), dtype=float32)高级特性1. 持久化 Tape默认情况下,GradientTape 只能调用一次 gradient() 方法。如果需要多次计算梯度,需要设置 persistent=True:x = tf.Variable(3.0)y = tf.Variable(4.0)with tf.GradientTape(persistent=True) as tape: z = x ** 2 + y ** 2dz_dx = tape.gradient(z, x)dz_dy = tape.gradient(z, y)print(dz_dx) # 输出: tf.Tensor(6.0, shape=(), dtype=float32)print(dz_dy) # 输出: tf.Tensor(8.0, shape=(), dtype=float32)# 必须手动释放资源del tape2. 监控张量默认情况下,GradientTape 只监控 tf.Variable。如果需要监控其他张量,使用 watch() 方法:x = tf.constant(3.0)with tf.GradientTape() as tape: tape.watch(x) y = x ** 2dy_dx = tape.gradient(y, x)print(dy_dx) # 输出: tf.Tensor(6.0, shape=(), dtype=float32)3. 停止梯度使用 tf.stop_gradient() 可以阻止某些操作的梯度传播:x = tf.Variable(2.0)with tf.GradientTape() as tape: y = x ** 2 z = tf.stop_gradient(y) + xdz_dx = tape.gradient(z, x)print(dz_dx) # 输出: tf.Tensor(1.0, shape=(), dtype=float32)# y 的梯度被停止,只计算 x 的梯度4. 控制可训练性可以通过设置 trainable=False 来阻止变量参与梯度计算:x = tf.Variable(2.0, trainable=True)y = tf.Variable(3.0, trainable=False)with tf.GradientTape() as tape: z = x ** 2 + y ** 2gradients = tape.gradient(z, [x, y])print(gradients[0]) # 输出: tf.Tensor(4.0, shape=(), dtype=float32)print(gradients[1]) # 输出: None(y 不可训练)实际应用:训练神经网络1. 自定义训练循环import tensorflow as tffrom tensorflow.keras import layers, models, losses, optimizers# 构建模型model = models.Sequential([ layers.Dense(64, activation='relu', input_shape=(10,)), layers.Dense(32, activation='relu'), layers.Dense(1)])# 定义优化器和损失函数optimizer = optimizers.Adam(learning_rate=0.001)loss_fn = losses.MeanSquaredError()# 训练数据x_train = tf.random.normal((100, 10))y_train = tf.random.normal((100, 1))# 自定义训练循环epochs = 10batch_size = 32for epoch in range(epochs): print(f'Epoch {epoch + 1}/{epochs}') for i in range(0, len(x_train), batch_size): x_batch = x_train[i:i + batch_size] y_batch = y_train[i:i + batch_size] with tf.GradientTape() as tape: # 前向传播 predictions = model(x_batch, training=True) loss = loss_fn(y_batch, predictions) # 计算梯度 gradients = tape.gradient(loss, model.trainable_variables) # 更新参数 optimizer.apply_gradients(zip(gradients, model.trainable_variables)) print(f'Loss: {loss.numpy():.4f}')2. 使用 tf.function 优化性能@tf.functiondef train_step(model, x_batch, y_batch, optimizer, loss_fn): with tf.GradientTape() as tape: predictions = model(x_batch, training=True) loss = loss_fn(y_batch, predictions) gradients = tape.gradient(loss, model.trainable_variables) optimizer.apply_gradients(zip(gradients, model.trainable_variables)) return loss# 在训练循环中使用for epoch in range(epochs): for i in range(0, len(x_train), batch_size): loss = train_step(model, x_train[i:i + batch_size], y_train[i:i + batch_size], optimizer, loss_fn)常见问题和注意事项1. 梯度为 None如果梯度为 None,可能的原因:变量不在计算图中使用了 tf.stop_gradient()变量的 trainable 属性为 False计算路径不连续2. 内存管理使用 persistent=True 时,记得手动释放 tape对于大型模型,注意内存使用3. 数值稳定性梯度可能过大或过小,导致数值问题考虑使用梯度裁剪gradients = tape.gradient(loss, model.trainable_variables)gradients = [tf.clip_by_norm(g, 1.0) for g in gradients]optimizer.apply_gradients(zip(gradients, model.trainable_variables))总结tf.GradientTape 是 TensorFlow 2.x 中强大而灵活的自动微分工具:简单易用:直观的 API,易于理解和使用功能强大:支持一阶和高阶导数计算灵活控制:可以精确控制梯度计算过程性能优化:结合 @tf.function 可以获得高性能掌握 tf.GradientTape 对于理解深度学习的训练过程和实现自定义训练逻辑至关重要。
阅读 0·2月18日 17:36

C++11/14/17/20 新特性有哪些

C++11/14/17/20 新特性C++ 标准不断演进,每个新版本都引入了许多强大的特性,显著提升了开发效率和代码质量。C++11 新特性自动类型推导(auto):// 自动推导类型auto x = 42; // intauto y = 3.14; // doubleauto z = "Hello"; // const char*auto& ref = x; // int&auto&& universal = x; // int& (万能引用)// 与迭代器配合使用std::vector<int> vec = {1, 2, 3};for (auto it = vec.begin(); it != vec.end(); ++it) { std::cout << *it << std::endl;}// 范围 for 循环for (auto& val : vec) { val *= 2;}范围 for 循环:std::vector<int> vec = {1, 2, 3, 4, 5};// 只读遍历for (const auto& val : vec) { std::cout << val << " ";}// 修改遍历for (auto& val : vec) { val *= 2;}// 初始化列表for (auto val : {1, 2, 3, 4, 5}) { std::cout << val << " ";}Lambda 表达式:// 基本语法auto lambda = [](int x, int y) { return x + y;};// 捕获变量int threshold = 10;auto filtered = [threshold](int x) { return x > threshold;};// 引用捕获int sum = 0;auto accumulate = [&sum](int x) { sum += x;};// 混合捕获int a = 1, b = 2;auto mixed = [a, &b]() { return a + b;};// 可变 lambdaauto variadic = [](auto... args) { return (args + ... + 0);};// 使用std::vector<int> vec = {5, 15, 25};auto it = std::find_if(vec.begin(), vec.end(), filtered);智能指针:#include <memory>// unique_ptr - 独占所有权auto ptr1 = std::make_unique<int>(42);auto ptr2 = std::move(ptr1); // 转移所有权// shared_ptr - 共享所有权auto shared1 = std::make_shared<int>(100);auto shared2 = shared1; // 引用计数增加std::cout << shared1.use_count() << std::endl; // 2// weak_ptr - 弱引用std::weak_ptr<int> weak = shared1;if (auto locked = weak.lock()) { std::cout << *locked << std::endl;}右值引用与移动语义:class MyString {private: char* data; size_t size;public: MyString(const char* str = ""); MyString(const MyString& other); // 拷贝构造 MyString(MyString&& other) noexcept; // 移动构造 MyString& operator=(const MyString& other); // 拷贝赋值 MyString& operator=(MyString&& other) noexcept; // 移动赋值};// 使用MyString str1 = "Hello";MyString str2 = std::move(str1); // 移动而非拷贝nullptr:// C++11 之前int* ptr1 = NULL;void func(int* ptr);void func(int value);func(NULL); // 歧义,可能调用 func(int)// C++11int* ptr2 = nullptr;func(nullptr); // 明确调用 func(int*)constexpr:// 编译期常量constexpr int factorial(int n) { return n <= 1 ? 1 : n * factorial(n - 1);}constexpr int result = factorial(5); // 编译期计算// 字面量类型struct Point { constexpr Point(double x, double y) : x(x), y(y) {} constexpr double distance() const { return x * x + y * y; } double x, y;};constexpr Point p(3.0, 4.0);constexpr double dist = p.distance(); // 25.0C++14 新特性泛型 lambda:// C++11auto add = [](int a, int b) { return a + b; };// C++14 - 泛型 lambdaauto genericAdd = [](auto a, auto b) { return a + b;};auto result1 = genericAdd(10, 20); // intauto result2 = genericAdd(3.14, 2.71); // double变量模板:template <typename T>constexpr T pi = T(3.1415926535897932385);// 使用double d = pi<double>;float f = pi<float>;二进制字面量:int binary = 0b1010; // 10int octal = 012; // 10int hex = 0xA; // 10函数返回类型推导:// C++11auto add(int a, int b) -> int { return a + b;}// C++14 - 自动推导返回类型auto add(int a, int b) { return a + b;}// 复杂返回类型auto getVector() { return std::vector<int>{1, 2, 3};}std::make_unique:// C++11auto ptr = std::unique_ptr<int>(new int(42));// C++14auto ptr = std::make_unique<int>(42);C++17 新特性结构化绑定:std::map<int, std::string> myMap = {{1, "one"}, {2, "two"}};// C++17 之前for (auto it = myMap.begin(); it != myMap.end(); ++it) { int key = it->first; std::string value = it->second;}// C++17for (const auto& [key, value] : myMap) { std::cout << key << ": " << value << std::endl;}// 元组解包auto [x, y, z] = std::make_tuple(1, 2.0, "three");if constexpr:template <typename T>auto process(T value) { if constexpr (std::is_integral_v<T>) { return value * 2; } else if constexpr (std::is_floating_point_v<T>) { return value / 2; } else { return value; }}// 使用process(10); // 20process(3.14); // 1.57std::optional:#include <optional>std::optional<int> divide(int a, int b) { if (b == 0) { return std::nullopt; } return a / b;}// 使用auto result = divide(10, 2);if (result) { std::cout << *result << std::endl;} else { std::cout << "Division by zero" << std::endl;}// 提供默认值int value = result.value_or(0);std::variant:#include <variant>std::variant<int, double, std::string> value;value = 42;value = 3.14;value = "Hello";// 访问std::visit([](auto&& arg) { std::cout << arg << std::endl;}, value);// 检查类型if (std::holds_alternative<int>(value)) { int intValue = std::get<int>(value);}std::any:#include <any>std::any value = 42;value = 3.14;value = "Hello";// 访问if (value.type() == typeid(int)) { int intValue = std::any_cast<int>(value);}折叠表达式:// C++17template <typename... Args>auto sum(Args... args) { return (args + ... + 0);}// 使用auto total = sum(1, 2, 3, 4, 5); // 15// 左折叠template <typename... Args>bool allTrue(Args... args) { return (args && ...);}// 右折叠template <typename... Args>void printAll(Args... args) { (std::cout << ... << args) << std::endl;}std::string_view:#include <string_view>void printString(std::string_view str) { std::cout << str << std::endl;}// 使用std::string str = "Hello";const char* cstr = "World";printString(str); // OKprintString(cstr); // OKprintString("Test"); // OK,无需创建临时 string 对象C++20 新特性概念(Concepts):#include <concepts>// 定义概念template <typename T>concept Integral = std::is_integral_v<T>;template <typename T>concept Sortable = requires(T t) { { t.begin() } -> std::same_as<typename T::iterator>; { t.end() } -> std::same_as<typename T::iterator>;};// 使用概念约束模板template <Integral T>T add(T a, T b) { return a + b;}// requires 子句template <typename T>requires std::is_integral_v<T>T multiply(T a, T b) { return a * b;}// 简写语法void process(Integral auto value) { std::cout << value << std::endl;}三向比较(Spaceship Operator):struct Point { int x, y; // 自动生成比较运算符 auto operator<=>(const Point&) const = default;};// 使用Point p1{1, 2};Point p2{1, 3};if (p1 < p2) { std::cout << "p1 < p2" << std::endl;}if (p1 == p2) { std::cout << "p1 == p2" << std::endl;}范围库(Ranges):#include <ranges>#include <vector>#include <algorithm>std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};// 过滤偶数auto evens = numbers | std::views::filter([](int n) { return n % 2 == 0;});// 转换auto squared = numbers | std::views::transform([](int n) { return n * n;});// 组合操作auto result = numbers | std::views::filter([](int n) { return n % 2 == 0; }) | std::views::transform([](int n) { return n * n; });// 使用for (auto n : result) { std::cout << n << " ";}协程(Coroutines):#include <coroutine>// 简单的生成器template <typename T>struct Generator { struct promise_type { T current_value; Generator get_return_object() { return Generator{std::coroutine_handle<promise_type>::from_promise(*this)}; } std::suspend_always initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { return {}; } std::suspend_always yield_value(T value) { current_value = value; return {}; } void return_void() {} void unhandled_exception() { std::terminate(); } }; std::coroutine_handle<promise_type> handle; Generator(std::coroutine_handle<promise_type> h) : handle(h) {} ~Generator() { if (handle) handle.destroy(); } bool next() { handle.resume(); return !handle.done(); } T value() { return handle.promise().current_value; }};Generator<int> fibonacci() { int a = 0, b = 1; while (true) { co_yield a; int temp = a + b; a = b; b = temp; }}// 使用auto gen = fibonacci();for (int i = 0; i < 10; ++i) { gen.next(); std::cout << gen.value() << " ";}模块(Modules):// math.ixx (模块接口)export module math;export int add(int a, int b) { return a + b;}export double multiply(double a, double b) { return a * b;}// main.cppimport math;int main() { int result = add(10, 20); return 0;}std::format:#include <format>std::string name = "Alice";int age = 30;// 格式化字符串std::string message = std::format("Name: {}, Age: {}", name, age);// 带格式的数字double pi = 3.14159;std::string formatted = std::format("Pi: {:.2f}", pi); // Pi: 3.14// 填充和对齐std::string padded = std::format("{:>10}", "Hello"); // " Hello"最佳实践1. 优先使用 auto 进行类型推导// 推荐auto it = vec.begin();auto result = std::make_unique<int>(42);// 不推荐std::vector<int>::iterator it = vec.begin();std::unique_ptr<int> result(new int(42));2. 使用智能指针管理资源// 推荐auto ptr = std::make_unique<Resource>();// 不推荐Resource* ptr = new Resource();3. 使用 nullptr 代替 NULL// 推荐int* ptr = nullptr;// 不推荐int* ptr = NULL;4. 使用 constexpr 进行编译期计算// 推荐constexpr int size = 1024;// 不推荐const int size = 1024;5. 使用范围 for 循环// 推荐for (const auto& val : vec) { std::cout << val << std::endl;}// 不推荐for (auto it = vec.begin(); it != vec.end(); ++it) { std::cout << *it << std::endl;}
阅读 0·2月18日 17:36

如何在 TensorFlow 中构建和训练神经网络模型

在 TensorFlow 中构建和训练神经网络模型是深度学习的核心任务。TensorFlow 提供了多种方式来构建模型,从高级 API 到低级自定义实现。使用 Keras Sequential APISequential API 是最简单的方式,适用于简单的线性堆叠模型:import tensorflow as tffrom tensorflow.keras import layers, models# 创建 Sequential 模型model = models.Sequential([ layers.Dense(128, activation='relu', input_shape=(784,)), layers.Dropout(0.2), layers.Dense(64, activation='relu'), layers.Dropout(0.2), layers.Dense(10, activation='softmax')])# 查看模型结构model.summary()使用 Keras Functional APIFunctional API 提供更灵活的模型构建方式,支持复杂的多输入多输出模型:from tensorflow.keras import layers, models, Input# 定义输入层inputs = Input(shape=(784,))# 构建隐藏层x = layers.Dense(128, activation='relu')(inputs)x = layers.Dropout(0.2)(x)x = layers.Dense(64, activation='relu')(x)x = layers.Dropout(0.2)(x)# 定义输出层outputs = layers.Dense(10, activation='softmax')(x)# 创建模型model = models.Model(inputs=inputs, outputs=outputs)model.summary()自定义模型类对于更复杂的模型,可以继承 tf.keras.Model 类:import tensorflow as tffrom tensorflow.keras import layers, modelsclass CustomModel(models.Model): def __init__(self): super(CustomModel, self).__init__() self.dense1 = layers.Dense(128, activation='relu') self.dropout1 = layers.Dropout(0.2) self.dense2 = layers.Dense(64, activation='relu') self.dropout2 = layers.Dropout(0.2) self.dense3 = layers.Dense(10, activation='softmax') def call(self, inputs, training=False): x = self.dense1(inputs) x = self.dropout1(x, training=training) x = self.dense2(x) x = self.dropout2(x, training=training) return self.dense3(x)# 创建模型实例model = CustomModel()常用层类型1. 全连接层(Dense)layers.Dense(units=64, activation='relu', input_shape=(784,))2. 卷积层(Conv2D)layers.Conv2D(filters=32, kernel_size=(3, 3), activation='relu', input_shape=(28, 28, 1))3. 池化层(MaxPooling2D)layers.MaxPooling2D(pool_size=(2, 2))4. 批归一化层(BatchNormalization)layers.BatchNormalization()5. Dropout 层layers.Dropout(0.5)6. Flatten 层layers.Flatten()7. LSTM 层layers.LSTM(units=64, return_sequences=True)8. 注意力层layers.Attention()激活函数# ReLUlayers.Dense(64, activation='relu')# Sigmoidlayers.Dense(64, activation='sigmoid')# Tanhlayers.Dense(64, activation='tanh')# Softmaxlayers.Dense(10, activation='softmax')# LeakyReLUlayers.LeakyReLU(alpha=0.1)# ELUlayers.Dense(64, activation='elu')# SELUlayers.Dense(64, activation='selu')编译模型在训练之前,需要编译模型,指定优化器、损失函数和评估指标:model.compile( optimizer='adam', # 或使用 tf.keras.optimizers.Adam(learning_rate=0.001) loss='sparse_categorical_crossentropy', # 或使用自定义损失函数 metrics=['accuracy'] # 可以指定多个指标)常用优化器# SGDoptimizer = tf.keras.optimizers.SGD(learning_rate=0.01, momentum=0.9)# Adamoptimizer = tf.keras.optimizers.Adam(learning_rate=0.001)# RMSpropoptimizer = tf.keras.optimizers.RMSprop(learning_rate=0.001)# Adagradoptimizer = tf.keras.optimizers.Adagrad(learning_rate=0.01)# Adadeltaoptimizer = tf.keras.optimizers.Adadelta(learning_rate=1.0)常用损失函数# 回归问题loss = 'mse' # 均方误差loss = 'mae' # 平均绝对误差# 二分类问题loss = 'binary_crossentropy'# 多分类问题loss = 'categorical_crossentropy' # one-hot 编码loss = 'sparse_categorical_crossentropy' # 整数标签# 自定义损失函数def custom_loss(y_true, y_pred): return tf.reduce_mean(tf.square(y_true - y_pred))常用评估指标metrics = ['accuracy', 'precision', 'recall']训练模型使用 fit 方法训练import numpy as np# 准备数据x_train = np.random.random((1000, 784))y_train = np.random.randint(0, 10, size=(1000,))x_val = np.random.random((200, 784))y_val = np.random.randint(0, 10, size=(200,))# 训练模型history = model.fit( x_train, y_train, epochs=10, batch_size=32, validation_data=(x_val, y_val), callbacks=[ tf.keras.callbacks.EarlyStopping(patience=3, restore_best_weights=True), tf.keras.callbacks.ModelCheckpoint('best_model.h5', save_best_only=True), tf.keras.callbacks.ReduceLROnPlateau(factor=0.1, patience=2) ])使用 tf.data.Dataset 训练# 创建 Datasettrain_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))train_dataset = train_dataset.shuffle(buffer_size=1000).batch(32).prefetch(tf.data.AUTOTUNE)val_dataset = tf.data.Dataset.from_tensor_slices((x_val, y_val))val_dataset = val_dataset.batch(32)# 训练history = model.fit( train_dataset, epochs=10, validation_data=val_dataset)自定义训练循环对于更复杂的训练逻辑,可以使用自定义训练循环:import tensorflow as tffrom tensorflow.keras import optimizers, losses# 定义优化器和损失函数optimizer = optimizers.Adam(learning_rate=0.001)loss_fn = losses.SparseCategoricalCrossentropy()# 训练步骤@tf.functiondef train_step(x_batch, y_batch): with tf.GradientTape() as tape: predictions = model(x_batch, training=True) loss = loss_fn(y_batch, predictions) gradients = tape.gradient(loss, model.trainable_variables) optimizer.apply_gradients(zip(gradients, model.trainable_variables)) return loss# 验证步骤@tf.functiondef val_step(x_batch, y_batch): predictions = model(x_batch, training=False) loss = loss_fn(y_batch, predictions) return loss# 训练循环epochs = 10for epoch in range(epochs): print(f'Epoch {epoch + 1}/{epochs}') # 训练 train_loss = 0 for x_batch, y_batch in train_dataset: loss = train_step(x_batch, y_batch) train_loss += loss.numpy() train_loss /= len(train_dataset) # 验证 val_loss = 0 for x_batch, y_batch in val_dataset: loss = val_step(x_batch, y_batch) val_loss += loss.numpy() val_loss /= len(val_dataset) print(f'Train Loss: {train_loss:.4f}, Val Loss: {val_loss:.4f}')回调函数(Callbacks)TensorFlow 提供了多种回调函数来控制训练过程:from tensorflow.keras.callbacks import Callbackclass CustomCallback(Callback): def on_train_begin(self, logs=None): print('Starting training...') def on_epoch_end(self, epoch, logs=None): print(f'Epoch {epoch + 1} - Loss: {logs["loss"]:.4f}') def on_batch_end(self, batch, logs=None): if batch % 100 == 0: print(f'Batch {batch} - Loss: {logs["loss"]:.4f}')# 使用回调model.fit( x_train, y_train, epochs=10, callbacks=[CustomCallback()])常用回调函数callbacks = [ # 早停 tf.keras.callbacks.EarlyStopping( monitor='val_loss', patience=5, restore_best_weights=True ), # 模型检查点 tf.keras.callbacks.ModelCheckpoint( 'model_{epoch:02d}.h5', save_best_only=True, monitor='val_loss' ), # 学习率调度 tf.keras.callbacks.ReduceLROnPlateau( monitor='val_loss', factor=0.1, patience=3 ), # TensorBoard tf.keras.callbacks.TensorBoard( log_dir='./logs', histogram_freq=1 ), # 学习率衰减 tf.keras.callbacks.LearningRateScheduler( lambda epoch: 0.001 * (0.9 ** epoch) )]评估模型# 评估模型test_loss, test_acc = model.evaluate(x_test, y_test)print(f'Test Loss: {test_loss:.4f}, Test Accuracy: {test_acc:.4f}')# 预测predictions = model.predict(x_test)predicted_classes = np.argmax(predictions, axis=1)保存和加载模型# 保存整个模型model.save('my_model.h5')# 加载模型loaded_model = tf.keras.models.load_model('my_model.h5')# 只保存权重model.save_weights('model_weights.h5')# 加载权重model.load_weights('model_weights.h5')# 保存为 SavedModel 格式model.save('saved_model/my_model')# 加载 SavedModelloaded_model = tf.keras.models.load_model('saved_model/my_model')完整示例:MNIST 分类import tensorflow as tffrom tensorflow.keras import layers, models# 加载数据(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()# 预处理x_train = x_train.reshape(-1, 784).astype('float32') / 255.0x_test = x_test.reshape(-1, 784).astype('float32') / 255.0# 构建模型model = models.Sequential([ layers.Dense(128, activation='relu', input_shape=(784,)), layers.Dropout(0.2), layers.Dense(64, activation='relu'), layers.Dropout(0.2), layers.Dense(10, activation='softmax')])# 编译模型model.compile( optimizer='adam', loss='sparse_categorical_crossentropy', metrics=['accuracy'])# 训练模型history = model.fit( x_train, y_train, epochs=10, batch_size=128, validation_split=0.2, callbacks=[ tf.keras.callbacks.EarlyStopping(patience=3, restore_best_weights=True) ])# 评估模型test_loss, test_acc = model.evaluate(x_test, y_test)print(f'Test Accuracy: {test_acc:.4f}')性能优化建议使用 GPU 加速:确保 TensorFlow 能够使用 GPU数据预取:使用 tf.data.Dataset.prefetch() 提高数据加载效率混合精度训练:使用 tf.keras.mixed_precision 提高训练速度批归一化:使用 BatchNormalization 加速收敛学习率调度:使用适当的学习率调度策略总结在 TensorFlow 中构建和训练神经网络模型的关键步骤:选择模型构建方式:Sequential API、Functional API 或自定义模型类设计网络架构:选择合适的层和激活函数编译模型:指定优化器、损失函数和评估指标训练模型:使用 fit() 方法或自定义训练循环监控训练过程:使用回调函数和 TensorBoard评估和优化:评估模型性能并进行调优掌握这些技能将帮助你有效地构建和训练各种深度学习模型。
阅读 0·2月18日 17:35

C++ 模板的特化与偏特化如何使用

C++ 模板的特化与偏特化模板是 C++ 泛型编程的核心机制,而模板特化和偏特化则为模板提供了更精细的控制能力,允许为特定类型提供定制化的实现。模板基础函数模板:template <typename T>T max(T a, T b) { return (a > b) ? a : b;}// 使用int result = max(10, 20); // T = intdouble result2 = max(3.14, 2.71); // T = double类模板:template <typename T>class Stack {private: std::vector<T> elements;public: void push(const T& element) { elements.push_back(element); } T pop() { T element = elements.back(); elements.pop_back(); return element; }};// 使用Stack<int> intStack;Stack<std::string> stringStack;模板特化全特化(Full Specialization):为所有模板参数提供具体类型,完全替换模板定义。// 通用模板template <typename T>class Vector {public: void push(const T& value) { std::cout << "Generic push: " << value << std::endl; }};// bool 类型的全特化template <>class Vector<bool> {public: void push(bool value) { std::cout << "Bool push: " << (value ? "true" : "false") << std::endl; }};// 使用Vector<int> intVec;intVec.push(42); // 输出: Generic push: 42Vector<bool> boolVec;boolVec.push(true); // 输出: Bool push: true函数模板特化:// 通用模板template <typename T>bool compare(T a, T b) { std::cout << "Generic compare" << std::endl; return a < b;}// const char* 的特化template <>bool compare<const char*>(const char* a, const char* b) { std::cout << "String compare" << std::endl; return strcmp(a, b) < 0;}// 使用compare(10, 20); // 输出: Generic comparecompare("hello", "world"); // 输出: String compare模板偏特化偏特化(Partial Specialization)只适用于类模板,允许部分指定模板参数。基本示例:// 通用模板:两个类型参数template <typename T, typename U>class Pair {public: T first; U second; void print() { std::cout << "Pair<" << typeid(T).name() << ", " << typeid(U).name() << ">" << std::endl; }};// 偏特化:两个类型参数相同template <typename T>class Pair<T, T> {public: T first; T second; void print() { std::cout << "Pair<" << typeid(T).name() << ", " << typeid(T).name() << "> (Same types)" << std::endl; }};// 偏特化:第二个参数是指针template <typename T>class Pair<T, T*> {public: T first; T* second; void print() { std::cout << "Pair<" << typeid(T).name() << ", " << typeid(T).name() << "*> (Pointer)" << std::endl; }};// 使用Pair<int, double> p1; // 使用通用模板p1.print(); // Pair<int, double>Pair<int, int> p2; // 使用偏特化(相同类型)p2.print(); // Pair<int, int> (Same types)Pair<int, int*> p3; // 使用偏特化(指针)p3.print(); // Pair<int, int*> (Pointer)指针和引用的偏特化// 通用模板template <typename T>class TypeInfo {public: static const char* name() { return "Unknown type"; }};// 指针类型的偏特化template <typename T>class TypeInfo<T*> {public: static const char* name() { return "Pointer type"; }};// 引用类型的偏特化template <typename T>class TypeInfo<T&> {public: static const char* name() { return "Reference type"; }};// const 类型的偏特化template <typename T>class TypeInfo<const T> {public: static const char* name() { return "Const type"; }};// 使用std::cout << TypeInfo<int>::name() << std::endl; // Unknown typestd::cout << TypeInfo<int*>::name() << std::endl; // Pointer typestd::cout << TypeInfo<int&>::name() << std::endl; // Reference typestd::cout << TypeInfo<const int>::name() << std::endl; // Const typeSFINAE(替换失败并非错误)SFINAE 是模板元编程的重要技术,允许在模板参数替换失败时排除该模板,而不是产生编译错误。基本示例:// 检查类型是否有 value_type 成员template <typename T>class has_value_type { template <typename U> static auto test(int) -> decltype(typename U::value_type(), std::true_type{}); template <typename> static std::false_type test(...);public: static constexpr bool value = decltype(test<T>(0))::value;};// 使用 SFINAE 的函数重载template <typename T>typename std::enable_if<has_value_type<T>::value, void>::typeprocess(T container) { std::cout << "Container has value_type" << std::endl;}template <typename T>typename std::enable_if<!has_value_type<T>::value, void>::typeprocess(T value) { std::cout << "Type doesn't have value_type" << std::endl;}// 使用std::vector<int> vec;process(vec); // Container has value_typeint x = 42;process(x); // Type doesn't have value_typeC++17 的 if constexpr:template <typename T>void printTypeInfo(T value) { if constexpr (std::is_pointer_v<T>) { std::cout << "Pointer type" << std::endl; } else if constexpr (std::is_integral_v<T>) { std::cout << "Integral type" << std::endl; } else if constexpr (std::is_floating_point_v<T>) { std::cout << "Floating point type" << std::endl; } else { std::cout << "Other type" << std::endl; }}// 使用int* ptr = nullptr;printTypeInfo(ptr); // Pointer typeint num = 42;printTypeInfo(num); // Integral typedouble d = 3.14;printTypeInfo(d); // Floating point type模板元编程编译期计算:// 编译期计算阶乘template <int N>struct Factorial { static constexpr int value = N * Factorial<N - 1>::value;};// 特化作为递归终止条件template <>struct Factorial<0> { static constexpr int value = 1;};// 使用constexpr int result = Factorial<5>::value; // 120编译期判断:template <typename T>struct IsPointer { static constexpr bool value = false;};template <typename T>struct IsPointer<T*> { static constexpr bool value = true;};// 使用static_assert(IsPointer<int*>::value == true);static_assert(IsPointer<int>::value == false);类型萃取(Type Traits)C++11 引入了 <type_traits> 头文件,提供了丰富的类型萃取工具。常用类型萃取:#include <type_traits>// 检查类型属性static_assert(std::is_integral_v<int> == true);static_assert(std::is_floating_point_v<double> == true);static_assert(std::is_pointer_v<int*> == true);static_assert(std::is_reference_v<int&> == true);static_assert(std::is_const_v<const int> == true);// 类型转换using IntPtr = std::add_pointer_t<int>; // int*using ConstInt = std::add_const_t<int>; // const intusing RemoveConst = std::remove_const_t<const int>; // int// 条件类型template <typename T>using ElementType = typename std::conditional< std::is_pointer_v<T>, std::remove_pointer_t<T>, T>::type;// 使用static_assert(std::is_same_v<ElementType<int*>, int>);static_assert(std::is_same_v<ElementType<int>, int>);实际应用示例智能指针的删除器特化:template <typename T>class SmartPtr {private: T* ptr;public: explicit SmartPtr(T* p = nullptr) : ptr(p) {} ~SmartPtr() { delete ptr; }};// 数组类型的特化template <typename T>class SmartPtr<T[]> {private: T* ptr;public: explicit SmartPtr(T* p = nullptr) : ptr(p) {} ~SmartPtr() { delete[] ptr; }};// 使用SmartPtr<int> ptr1(new int(42));SmartPtr<int[]> ptr2(new int[10]);容器的优化特化:template <typename T, size_t N>class FixedArray {private: T data[N];public: T& operator[](size_t index) { return data[index]; }};// bool 类型的特化,使用位压缩template <size_t N>class FixedArray<bool, N> {private: unsigned char data[(N + 7) / 8];public: bool operator[](size_t index) { return (data[index / 8] >> (index % 8)) & 1; }};最佳实践1. 优先使用类型萃取而非手动特化// 推荐template <typename T>void process(T value) { if constexpr (std::is_pointer_v<T>) { // 处理指针 } else { // 处理非指针 }}// 不推荐template <typename T>void process(T value);template <typename T>void process(T* value);2. 使用 constexpr 进行编译期计算// C++11/14template <int N>struct Factorial { static constexpr int value = N * Factorial<N - 1>::value;};// C++17constexpr int factorial(int n) { if (n <= 1) return 1; return n * factorial(n - 1);}3. 使用概念(C++20)约束模板template <typename T>concept Integral = std::is_integral_v<T>;template <Integral T>T add(T a, T b) { return a + b;}// 使用add(10, 20); // OKadd(3.14, 2.71); // 编译错误4. 避免过度复杂的模板元编程// 不推荐:过度复杂template <typename T>struct ComplexMetaProgramming { // 大量嵌套模板};// 推荐:简洁明了template <typename T>void simpleFunction(T value) { // 简单直接的实现}注意事项函数模板不支持偏特化,只能全特化特化版本必须在主模板之后声明特化版本必须与主模板的接口一致SFINAE 可能导致编译错误信息难以理解过度使用模板特化可能导致代码膨胀C++20 的概念可以替代部分 SFINAE 用法,提供更好的可读性
阅读 0·2月18日 17:35

C++ 异常处理机制如何使用

C++ 异常处理机制C++ 异常处理提供了一种结构化的错误处理方式,允许程序在运行时检测和处理错误,而不会导致程序崩溃。异常处理基础基本语法:#include <iostream>#include <stdexcept>int divide(int a, int b) { if (b == 0) { throw std::runtime_error("Division by zero"); } return a / b;}int main() { try { int result = divide(10, 0); std::cout << "Result: " << result << std::endl; } catch (const std::runtime_error& e) { std::cerr << "Error: " << e.what() << std::endl; } catch (...) { std::cerr << "Unknown error occurred" << std::endl; } return 0;}标准异常类异常类层次结构:std::exception├── std::logic_error│ ├── std::invalid_argument│ ├── std::domain_error│ ├── std::length_error│ └── std::out_of_range└── std::runtime_error ├── std::range_error ├── std::overflow_error └── std::underflow_error使用标准异常:#include <stdexcept>void processValue(int value) { if (value < 0) { throw std::invalid_argument("Value must be non-negative"); } if (value > 100) { throw std::out_of_range("Value must be <= 100"); } // 处理逻辑}void allocateMemory(size_t size) { try { int* ptr = new int[size]; // 使用内存 delete[] ptr; } catch (const std::bad_alloc& e) { std::cerr << "Memory allocation failed: " << e.what() << std::endl; throw; }}自定义异常类基本自定义异常:class MyException : public std::exception {private: std::string message;public: MyException(const std::string& msg) : message(msg) {} const char* what() const noexcept override { return message.c_str(); }};// 使用void riskyOperation() { throw MyException("Something went wrong");}带错误代码的异常:class DatabaseException : public std::runtime_error {private: int errorCode;public: DatabaseException(const std::string& msg, int code) : std::runtime_error(msg), errorCode(code) {} int getErrorCode() const noexcept { return errorCode; }};// 使用void queryDatabase() { throw DatabaseException("Connection failed", 1001);}try { queryDatabase();} catch (const DatabaseException& e) { std::cerr << "Database error " << e.getErrorCode() << ": " << e.what() << std::endl;}异常规范noexcept 规范:// C++11 之前的异常规范(已弃用)void oldFunction() throw(std::runtime_error) { // 可能抛出 runtime_error}// C++11 的 noexceptvoid safeFunction() noexcept { // 保证不抛出异常}// 条件 noexcepttemplate <typename T>void conditionalNoexcept(T&& value) noexcept(std::is_nothrow_move_constructible_v<T>) { // 根据 T 的移动构造函数是否 noexcept 来决定}noexcept 的作用:优化编译器生成的代码允许标准库选择更高效的实现(如 vector 移动)提供更好的错误处理保证RAII 与异常安全异常安全级别:1. 基本保证(Basic Guarantee):class ResourceManager {private: int* resource1; int* resource2;public: ResourceManager() : resource1(nullptr), resource2(nullptr) { resource1 = new int; resource2 = new int; } ~ResourceManager() { delete resource1; delete resource2; } void modify() { // 即使抛出异常,对象仍处于有效状态 int* temp = new int; delete resource1; resource1 = temp; // 如果后续操作抛出异常,资源1已更新,对象仍然有效 }};2. 强烈保证(Strong Guarantee):class StrongGuaranteeExample {private: std::vector<int> data;public: void update(const std::vector<int>& newData) { // 创建副本 std::vector<int> temp = data; // 修改副本 temp.insert(temp.end(), newData.begin(), newData.end()); // 原子交换 std::swap(data, temp); // 如果抛出异常,原数据不受影响 }};3. 不抛出保证(No-throw Guarantee):class NoThrowExample {private: std::unique_ptr<int> ptr;public: void reset() noexcept { ptr.reset(); // unique_ptr::reset 是 noexcept }};智能指针与异常安全使用智能指针确保异常安全:// 不推荐:手动管理内存void unsafeFunction() { int* ptr = new int(42); // 如果这里抛出异常,ptr 会泄漏 someRiskyOperation(); delete ptr;}// 推荐:使用智能指针void safeFunction() { auto ptr = std::make_unique<int>(42); // 即使抛出异常,ptr 也会自动释放 someRiskyOperation();}std::lock_guard 与异常安全:std::mutex mtx;void threadSafeOperation() { std::lock_guard<std::mutex> lock(mtx); // 临界区代码 // 即使抛出异常,锁也会自动释放 riskyOperation();}异常捕获与重新抛出捕获并重新抛出:void process() { try { riskyOperation(); } catch (const std::exception& e) { // 记录日志 logError(e.what()); // 重新抛出 throw; }}// 使用 std::current_exception 保存异常std::exception_ptr currentException;void saveException() { try { riskyOperation(); } catch (...) { currentException = std::current_exception(); }}void rethrowException() { if (currentException) { std::rethrow_exception(currentException); }}异常与构造函数/析构函数构造函数中的异常:class MyClass {private: int* data; std::string name;public: MyClass(const std::string& n, size_t size) : name(n) { data = new int[size]; // 如果后续操作失败,构造函数抛出异常 if (size == 0) { delete[] data; // 清理已分配的资源 throw std::invalid_argument("Size cannot be zero"); } } ~MyClass() { delete[] data; }};析构函数中的异常:class MyClass {private: std::unique_ptr<int> ptr;public: ~MyClass() noexcept { // 析构函数不应该抛出异常 try { cleanup(); } catch (...) { // 吞掉异常或记录日志 std::cerr << "Exception in destructor" << std::endl; } } void cleanup() { // 清理逻辑 }};异常与函数指针异常规范与函数指针:// noexcept 函数指针using NoThrowFunction = void(*)() noexcept;void safeFunction() noexcept { // 不抛出异常}void unsafeFunction() { // 可能抛出异常}NoThrowFunction func1 = safeFunction; // OK// NoThrowFunction func2 = unsafeFunction; // 编译错误最佳实践1. 按值抛出,按引用捕获// 推荐throw MyException("Error message");try { riskyOperation();} catch (const MyException& e) { // 按引用捕获 std::cerr << e.what() << std::endl;}// 不推荐throw new MyException("Error message"); // 不要抛出指针2. 捕获最具体的异常try { riskyOperation();} catch (const std::invalid_argument& e) { // 处理特定异常} catch (const std::runtime_error& e) { // 处理运行时错误} catch (const std::exception& e) { // 处理其他标准异常} catch (...) { // 处理未知异常}3. 使用 RAII 确保资源释放class FileHandler {private: FILE* file;public: FileHandler(const char* filename) { file = fopen(filename, "r"); if (!file) { throw std::runtime_error("Failed to open file"); } } ~FileHandler() noexcept { if (file) { fclose(file); } } // 禁止拷贝 FileHandler(const FileHandler&) = delete; FileHandler& operator=(const FileHandler&) = delete;};4. 异常只用于异常情况// 推荐:异常用于真正的错误void processValue(int value) { if (value < 0) { throw std::invalid_argument("Value must be non-negative"); } // 正常处理}// 不推荐:异常用于控制流void findElement(const std::vector<int>& vec, int target) { for (int val : vec) { if (val == target) { throw FoundException(); // 不要这样做 } }}5. 提供有意义的错误信息class DatabaseException : public std::runtime_error {public: DatabaseException(const std::string& operation, const std::string& reason) : std::runtime_error("Database error in " + operation + ": " + reason) {}};// 使用throw DatabaseException("query", "connection timeout");注意事项不要在析构函数中抛出异常构造函数中抛出异常时,确保已构造的成员被正确析构异常处理会增加运行时开销,避免在性能关键路径过度使用noexcept 函数如果抛出异常会调用 std::terminate跨 DLL 边界抛出异常可能导致问题异常对象应该轻量级,避免在异常中包含大量数据考虑使用错误码或 std::optional 作为异常的替代方案在多线程环境中,异常只在当前线程中传播
阅读 0·2月18日 17:34

C++ 并发编程与多线程如何实现

C++ 并发编程与多线程C++11 引入了标准化的多线程支持,提供了丰富的并发编程工具,包括线程、互斥锁、条件变量、原子操作等。线程基础创建线程:#include <thread>#include <iostream>void hello() { std::cout << "Hello from thread!" << std::endl;}int main() { // 创建线程 std::thread t(hello); // 等待线程完成 t.join(); return 0;}带参数的线程:void printMessage(const std::string& message, int count) { for (int i = 0; i < count; ++i) { std::cout << message << std::endl; }}int main() { std::string msg = "Hello"; std::thread t(printMessage, msg, 3); t.join(); return 0;}使用 lambda 表达式:int main() { int value = 42; std::thread t([value]() { std::cout << "Value: " << value << std::endl; }); t.join(); return 0;}互斥锁(Mutex)基本使用:#include <mutex>#include <thread>#include <iostream>std::mutex mtx;int counter = 0;void increment() { for (int i = 0; i < 10000; ++i) { std::lock_guard<std::mutex> lock(mtx); ++counter; }}int main() { std::thread t1(increment); std::thread t2(increment); t1.join(); t2.join(); std::cout << "Counter: " << counter << std::endl; // 20000 return 0;}unique_lock:std::mutex mtx;void process() { std::unique_lock<std::mutex> lock(mtx); // 可以手动解锁 lock.unlock(); // 执行一些不需要锁的操作 // 重新加锁 lock.lock();}// 条件变量配合使用std::mutex mtx;std::condition_variable cv;bool ready = false;void worker() { std::unique_lock<std::mutex> lock(mtx); cv.wait(lock, []{ return ready; }); // 执行工作}try_lock:std::mutex mtx1, mtx2;void process() { std::unique_lock<std::mutex> lock1(mtx1, std::defer_lock); std::unique_lock<std::mutex> lock2(mtx2, std::defer_lock); if (std::try_lock(lock1, lock2) == -1) { // 成功获取两个锁 // 执行操作 } else { // 未能获取所有锁 }}条件变量(Condition Variable)基本使用:#include <condition_variable>#include <mutex>#include <thread>#include <queue>#include <iostream>std::mutex mtx;std::condition_variable cv;std::queue<int> dataQueue;bool finished = false;void producer() { for (int i = 0; i < 10; ++i) { { std::lock_guard<std::mutex> lock(mtx); dataQueue.push(i); std::cout << "Produced: " << i << std::endl; } cv.notify_one(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); } { std::lock_guard<std::mutex> lock(mtx); finished = true; } cv.notify_all();}void consumer() { while (true) { std::unique_lock<std::mutex> lock(mtx); cv.wait(lock, []{ return !dataQueue.empty() || finished; }); if (dataQueue.empty() && finished) { break; } int value = dataQueue.front(); dataQueue.pop(); lock.unlock(); std::cout << "Consumed: " << value << std::endl; }}int main() { std::thread p(producer); std::thread c(consumer); p.join(); c.join(); return 0;}原子操作(Atomic)基本类型:#include <atomic>#include <thread>#include <iostream>std::atomic<int> atomicCounter(0);void increment() { for (int i = 0; i < 10000; ++i) { atomicCounter.fetch_add(1, std::memory_order_relaxed); }}int main() { std::thread t1(increment); std::thread t2(increment); t1.join(); t2.join(); std::cout << "Atomic counter: " << atomicCounter << std::endl; return 0;}内存顺序:std::atomic<bool> flag(false);std::atomic<int> data(0);void writer() { data.store(42, std::memory_order_release); flag.store(true, std::memory_order_release);}void reader() { while (!flag.load(std::memory_order_acquire)) { // 等待 } int value = data.load(std::memory_order_acquire); std::cout << "Data: " << value << std::endl;}CAS(Compare-And-Swap):std::atomic<int> value(0);bool updateIfEqual(int expected, int desired) { return value.compare_exchange_weak(expected, desired);}// 使用if (updateIfEqual(0, 1)) { std::cout << "Updated successfully" << std::endl;} else { std::cout << "Update failed" << std::endl;}线程局部存储thread_local:#include <thread>#include <iostream>thread_local int threadLocalVar = 0;void printThreadId() { ++threadLocalVar; std::cout << "Thread ID: " << std::this_thread::get_id() << ", Value: " << threadLocalVar << std::endl;}int main() { std::thread t1(printThreadId); std::thread t2(printThreadId); std::thread t3(printThreadId); t1.join(); t2.join(); t3.join(); return 0;}异步操作(Future 和 Promise)基本使用:#include <future>#include <iostream>int calculate() { std::this_thread::sleep_for(std::chrono::seconds(2)); return 42;}int main() { std::future<int> result = std::async(std::launch::async, calculate); std::cout << "Doing other work..." << std::endl; int value = result.get(); // 等待结果 std::cout << "Result: " << value << std::endl; return 0;}Promise:#include <future>#include <thread>#include <iostream>void setValue(std::promise<int> prom) { std::this_thread::sleep_for(std::chrono::seconds(1)); prom.set_value(100);}int main() { std::promise<int> prom; std::future<int> fut = prom.get_future(); std::thread t(setValue, std::move(prom)); int value = fut.get(); std::cout << "Value: " << value << std::endl; t.join(); return 0;}Packaged Task:#include <future>#include <functional>int add(int a, int b) { return a + b;}int main() { std::packaged_task<int(int, int)> task(add); std::future<int> result = task.get_future(); std::thread t(std::move(task), 10, 20); t.join(); std::cout << "Result: " << result.get() << std::endl; return 0;}线程池实现简单线程池:#include <thread>#include <mutex>#include <condition_variable>#include <queue>#include <functional>#include <vector>class ThreadPool {private: std::vector<std::thread> workers; std::queue<std::function<void()>> tasks; std::mutex queueMutex; 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(this->queueMutex); this->condition.wait(lock, [this] { return this->stop || !this->tasks.empty(); }); if (this->stop && this->tasks.empty()) { return; } task = std::move(this->tasks.front()); this->tasks.pop(); } task(); } }); } } template <class F, class... Args> auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> { using return_type = typename std::result_of<F(Args...)>::type; auto task = std::make_shared<std::packaged_task<return_type()>>( std::bind(std::forward<F>(f), std::forward<Args>(args)...) ); std::future<return_type> res = task->get_future(); { std::unique_lock<std::mutex> lock(queueMutex); if (stop) { throw std::runtime_error("enqueue on stopped ThreadPool"); } tasks.emplace([task]() { (*task)(); }); } condition.notify_one(); return res; } ~ThreadPool() { { std::unique_lock<std::mutex> lock(queueMutex); stop = true; } condition.notify_all(); for (std::thread& worker : workers) { worker.join(); } }};// 使用int main() { ThreadPool pool(4); auto result1 = pool.enqueue([](int a, int b) { return a + b; }, 10, 20); auto result2 = pool.enqueue([](int a, int b) { return a * b; }, 5, 6); std::cout << "Result 1: " << result1.get() << std::endl; std::cout << "Result 2: " << result2.get() << std::endl; return 0;}最佳实践1. 避免数据竞争// 错误:数据竞争int sharedData = 0;std::thread t1([&](){ ++sharedData; });std::thread t2([&](){ ++sharedData; });// 正确:使用互斥锁std::mutex mtx;int sharedData = 0;std::thread t1([&](){ std::lock_guard<std::mutex> lock(mtx); ++sharedData; });std::thread t2([&](){ std::lock_guard<std::mutex> lock(mtx); ++sharedData; });2. 避免死锁// 错误:可能导致死锁std::mutex mtx1, mtx2;void thread1() { std::lock_guard<std::mutex> lock1(mtx1); std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::lock_guard<std::mutex> lock2(mtx2);}void thread2() { std::lock_guard<std::mutex> lock2(mtx2); std::this_thread::sleep_for(std::chrono::milliseconds(10)); std::lock_guard<std::mutex> lock1(mtx1);}// 正确:使用 std::lockvoid thread1Safe() { std::lock(mtx1, mtx2); std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock); std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);}void thread2Safe() { std::lock(mtx1, mtx2); std::lock_guard<std::mutex> lock1(mtx1, std::adopt_lock); std::lock_guard<std::mutex> lock2(mtx2, std::adopt_lock);}3. 使用 RAII 管理锁// 推荐void safeFunction() { std::lock_guard<std::mutex> lock(mtx); // 临界区代码} // 自动释放锁// 不推荐void unsafeFunction() { mtx.lock(); // 临界区代码 mtx.unlock(); // 可能忘记或异常导致未释放}4. 优先使用原子操作// 推荐:原子操作std::atomic<int> counter(0);++counter;// 不推荐:互斥锁(对于简单操作)std::mutex mtx;int counter = 0;{ std::lock_guard<std::mutex> lock(mtx); ++counter;}注意事项始终确保线程被正确 join 或 detach避免在析构函数中使用互斥锁注意条件变量的虚假唤醒合理选择内存顺序,避免过度使用 memoryorderseq_cst避免过度细粒度的锁,可能导致性能下降使用线程池管理大量短期任务注意异常安全,确保资源正确释放避免在多线程环境中使用全局变量
阅读 0·2月18日 17:34

C++ 内存管理与内存泄漏如何避免

C++ 内存管理与内存泄漏C++ 提供了强大的内存管理能力,但也要求开发者对内存生命周期有清晰的认识。不当的内存管理会导致内存泄漏、悬空指针、重复释放等严重问题。C++ 内存区域1. 栈(Stack)存储局部变量、函数参数、返回地址自动分配和释放大小有限(通常几 MB)分配速度快2. 堆(Heap)动态分配的内存手动管理(new/delete)或智能指针管理大小受限于系统可用内存分配速度较慢3. 全局/静态区存储全局变量、静态变量程序启动时分配,结束时释放生命周期贯穿整个程序4. 常量区存储字符串常量、const 变量只读,不可修改5. 代码区存储程序二进制代码只读内存泄漏的原因1. 忘记释放内存void leakExample() { int* ptr = new int(42); // 忘记 delete ptr;}2. 异常导致跳过释放void leakWithException() { int* ptr = new int(42); someFunctionThatThrows(); // 如果抛出异常,ptr 不会被释放 delete ptr;}3. 循环引用class A {public: std::shared_ptr<B> b;};class B {public: std::shared_ptr<A> a; // 循环引用导致内存泄漏};auto a = std::make_shared<A>();auto b = std::make_shared<B>();a->b = b;b->a = a;4. 不当的指针赋值void lostPointer() { int* ptr = new int(42); ptr = new int(100); // 第一个分配的内存泄漏 delete ptr;}检测内存泄漏1. Valgrind(Linux)valgrind --leak-check=full --show-leak-kinds=all ./your_program2. AddressSanitizer(ASan)g++ -fsanitize=address -g your_program.cpp -o your_program./your_program3. Visual Studio 调试器使用 CRT 调试堆在代码开头添加:#define _CRTDBG_MAP_ALLOC#include <crtdbg.h>_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);4. 自定义内存跟踪class MemoryTracker {private: static std::unordered_map<void*, size_t> allocations;public: static void* allocate(size_t size) { void* ptr = malloc(size); allocations[ptr] = size; return ptr; } static void deallocate(void* ptr) { if (allocations.erase(ptr) == 0) { std::cerr << "Double free or invalid pointer: " << ptr << std::endl; } free(ptr); } static void reportLeaks() { if (!allocations.empty()) { std::cerr << "Memory leaks detected:" << std::endl; for (const auto& [ptr, size] : allocations) { std::cerr << " Leaked " << size << " bytes at " << ptr << std::endl; } } }};防止内存泄漏的方法1. 使用智能指针// 不推荐void badExample() { Resource* res = new Resource(); // 容易忘记 delete}// 推荐void goodExample() { auto res = std::make_unique<Resource>(); // 自动释放}2. 遵循 RAII 原则class FileHandler {private: FILE* file;public: FileHandler(const char* filename) { file = fopen(filename, "r"); if (!file) { throw std::runtime_error("Failed to open file"); } } ~FileHandler() { if (file) { fclose(file); } } // 禁止拷贝 FileHandler(const FileHandler&) = delete; FileHandler& operator=(const FileHandler&) = delete;};3. 使用标准容器// 不推荐void badContainer() { int* arr = new int[100]; // 处理数组 delete[] arr;}// 推荐void goodContainer() { std::vector<int> arr(100); // 自动管理内存}4. 正确处理异常// 不推荐void badException() { int* ptr = new int(42); riskyOperation(); // 可能抛出异常 delete ptr;}// 推荐void goodException() { auto ptr = std::make_unique<int>(42); riskyOperation(); // 即使抛出异常,ptr 也会被正确释放}内存对齐为什么需要内存对齐:CPU 访问对齐的内存更快某些架构要求特定类型必须对齐避免性能下降或程序崩溃对齐方式:// 使用 alignas 指定对齐struct alignas(16) AlignedStruct { int a; double b;};// 使用 alignof 查询对齐要求std::cout << "Alignment: " << alignof(AlignedStruct) << std::endl;// 使用 aligned_alloc 分配对齐内存void* ptr = aligned_alloc(16, 1024);内存池内存池的优点:减少内存碎片提高分配/释放速度减少系统调用次数简单实现:template <typename T, size_t BlockSize = 1024>class MemoryPool {private: struct Block { T data; Block* next; }; Block* freeList; std::vector<std::unique_ptr<Block[]>> blocks;public: MemoryPool() : freeList(nullptr) { allocateBlock(); } ~MemoryPool() = default; T* allocate() { if (!freeList) { allocateBlock(); } Block* block = freeList; freeList = freeList->next; return &block->data; } void deallocate(T* ptr) { Block* block = reinterpret_cast<Block*>(ptr); block->next = freeList; freeList = block; }private: void allocateBlock() { auto newBlock = std::make_unique<Block[]>(BlockSize); for (size_t i = 0; i < BlockSize - 1; ++i) { newBlock[i].next = &newBlock[i + 1]; } newBlock[BlockSize - 1].next = nullptr; freeList = &newBlock[0]; blocks.push_back(std::move(newBlock)); }};最佳实践1. 优先使用栈内存// 优先void stackPreferred() { int value = 42; process(value);}// 仅在必要时使用堆void heapWhenNeeded() { auto value = std::make_unique<int>(42); process(*value);}2. 使用标准库容器std::vector<int> vec; // 自动管理内存std::string str; // 自动管理内存std::map<int, int> map; // 自动管理内存3. 避免裸指针// 不推荐int* ptr = new int(42);// ... 使用 ptrdelete ptr;// 推荐auto ptr = std::make_unique<int>(42);// ... 使用 ptr// 自动释放4. 使用 const 正确性void process(const std::vector<int>& data); // 避免拷贝void modify(std::vector<int>& data); // 明确表示会修改5. 定期进行内存分析# 使用 Valgrind 检测内存泄漏valgrind --leak-check=full --show-leak-kinds=all ./your_program# 使用 AddressSanitizerg++ -fsanitize=address -g your_program.cpp -o your_program./your_program常见错误1. 重复释放int* ptr = new int(42);delete ptr;delete ptr; // 未定义行为2. 释放未分配的内存int* ptr;delete ptr; // 未定义行为3. 数组 new/delete 不匹配int* arr = new int[10];delete arr; // 错误,应该使用 delete[] arr4. 栈内存使用 deleteint value = 42;int* ptr = &value;delete ptr; // 错误,不能释放栈内存5. 悬空指针int* ptr = new int(42);delete ptr;*ptr = 100; // 未定义行为,悬空指针
阅读 0·2月18日 17:34

C++ STL 容器与算法如何使用

C++ STL 容器与算法C++ 标准模板库(STL)提供了丰富的容器和算法,是 C++ 编程的重要组成部分。熟练掌握 STL 可以显著提高开发效率和代码质量。序列容器std::vector动态数组,连续内存存储支持随机访问,O(1) 时间复杂度尾部插入和删除效率高 O(1),中间插入删除 O(n)自动扩容,通常容量翻倍#include <vector>#include <iostream>int main() { // 创建 vector std::vector<int> vec = {1, 2, 3, 4, 5}; // 访问元素 std::cout << vec[0] << std::endl; // 1 std::cout << vec.at(1) << std::endl; // 2(带边界检查) // 添加元素 vec.push_back(6); // 尾部添加 vec.emplace_back(7); // 原地构造 // 插入元素 vec.insert(vec.begin() + 2, 100); // 在位置 2 插入 // 删除元素 vec.pop_back(); // 删除最后一个 vec.erase(vec.begin()); // 删除第一个 // 容量信息 std::cout << "Size: " << vec.size() << std::endl; std::cout << "Capacity: " << vec.capacity() << std::endl; // 预留空间 vec.reserve(100); // 预留至少 100 个元素的空间 // 调整大小 vec.resize(50, 0); // 调整为 50 个元素,新元素初始化为 0 return 0;}std::deque双端队列,分段连续内存支持两端快速插入删除 O(1)支持随机访问 O(1)内存开销比 vector 大#include <deque>int main() { std::deque<int> dq = {1, 2, 3}; // 两端操作 dq.push_front(0); // 头部添加 dq.push_back(4); // 尾部添加 dq.pop_front(); // 头部删除 dq.pop_back(); // 尾部删除 // 随机访问 std::cout << dq[1] << std::endl; return 0;}std::list双向链表,非连续内存任意位置插入删除 O(1)不支持随机访问额外内存开销(每个节点两个指针)#include <list>int main() { std::list<int> lst = {1, 2, 3, 4, 5}; // 插入元素 auto it = lst.begin(); std::advance(it, 2); lst.insert(it, 100); // 在位置 2 插入 // 删除元素 lst.erase(lst.begin()); // 删除第一个 // 链表特有操作 lst.splice(lst.end(), lst, lst.begin()); // 移动元素 lst.sort(); // 排序 lst.unique(); // 去重 lst.reverse(); // 反转 return 0;}关联容器std::map键值对容器,按键排序基于红黑树实现查找、插入、删除 O(log n)键唯一#include <map>#include <string>int main() { std::map<std::string, int> scores; // 插入元素 scores["Alice"] = 90; scores["Bob"] = 85; scores.insert({"Charlie", 95}); // 访问元素 std::cout << scores["Alice"] << std::endl; // 90 // 查找元素 auto it = scores.find("Bob"); if (it != scores.end()) { std::cout << "Bob's score: " << it->second << std::endl; } // 删除元素 scores.erase("Alice"); // 遍历 for (const auto& [name, score] : scores) { std::cout << name << ": " << score << std::endl; } return 0;}std::unordered_map键值对容器,无序基于哈希表实现平均查找、插入、删除 O(1)最坏情况 O(n)#include <unordered_map>int main() { std::unordered_map<std::string, int> cache; cache["key1"] = 100; cache["key2"] = 200; // 快速查找 auto it = cache.find("key1"); if (it != cache.end()) { std::cout << it->second << std::endl; } return 0;}std::set有序集合,元素唯一基于红黑树实现查找、插入、删除 O(log n)#include <set>int main() { std::set<int> s = {5, 3, 1, 4, 2}; // 插入元素 s.insert(6); // 查找元素 auto it = s.find(3); if (it != s.end()) { std::cout << "Found: " << *it << std::endl; } // 删除元素 s.erase(1); // 遍历(自动排序) for (int val : s) { std::cout << val << " "; } return 0;}容器适配器std::stack后进先出(LIFO)基于 deque 或 vector 实现#include <stack>int main() { std::stack<int> stk; stk.push(1); stk.push(2); stk.push(3); while (!stk.empty()) { std::cout << stk.top() << " "; stk.pop(); } return 0;}std::queue先进先出(FIFO)基于 deque 实现#include <queue>int main() { std::queue<int> q; q.push(1); q.push(2); q.push(3); while (!q.empty()) { std::cout << q.front() << " "; q.pop(); } return 0;}std::priority_queue优先级队列默认大顶堆#include <queue>int main() { std::priority_queue<int> pq; // 大顶堆 pq.push(3); pq.push(1); pq.push(4); pq.push(2); while (!pq.empty()) { std::cout << pq.top() << " "; pq.pop(); } // 小顶堆 std::priority_queue<int, std::vector<int>, std::greater<int>> minPq; return 0;}STL 算法排序算法:#include <algorithm>#include <vector>int main() { std::vector<int> vec = {5, 2, 8, 1, 9, 3}; // 排序 std::sort(vec.begin(), vec.end()); // 部分排序 std::partial_sort(vec.begin(), vec.begin() + 3, vec.end()); // 第 n 大元素 std::nth_element(vec.begin(), vec.begin() + 2, vec.end()); return 0;}查找算法:int main() { std::vector<int> vec = {1, 2, 3, 4, 5}; // 二分查找(需要有序) auto it = std::lower_bound(vec.begin(), vec.end(), 3); // 查找 auto found = std::find(vec.begin(), vec.end(), 3); // 计数 int count = std::count(vec.begin(), vec.end(), 3); return 0;}变换算法:int main() { std::vector<int> vec1 = {1, 2, 3}; std::vector<int> vec2(3); // 复制 std::copy(vec1.begin(), vec1.end(), vec2.begin()); // 变换 std::transform(vec1.begin(), vec1.end(), vec2.begin(), [](int x) { return x * 2; }); // 填充 std::fill(vec2.begin(), vec2.end(), 0); // 生成 std::generate(vec2.begin(), vec2.end(), []() { return rand() % 100; }); return 0;}数值算法:#include <numeric>int main() { std::vector<int> vec = {1, 2, 3, 4, 5}; // 求和 int sum = std::accumulate(vec.begin(), vec.end(), 0); // 内积 std::vector<int> vec2 = {2, 3, 4, 5, 6}; int product = std::inner_product(vec.begin(), vec.end(), vec2.begin(), 0); // 差分 std::vector<int> diff(vec.size()); std::adjacent_difference(vec.begin(), vec.end(), diff.begin()); return 0;}迭代器迭代器分类:输入迭代器:只读,单向输出迭代器:只写,单向前向迭代器:读写,单向双向迭代器:读写,双向随机访问迭代器:读写,随机访问int main() { std::vector<int> vec = {1, 2, 3, 4, 5}; // 正向迭代器 for (auto it = vec.begin(); it != vec.end(); ++it) { std::cout << *it << " "; } // 反向迭代器 for (auto it = vec.rbegin(); it != vec.rend(); ++it) { std::cout << *it << " "; } // 常量迭代器 for (auto it = vec.cbegin(); it != vec.cend(); ++it) { std::cout << *it << " "; } return 0;}函数对象与 LambdaLambda 表达式:int main() { std::vector<int> vec = {5, 2, 8, 1, 9}; // 简单 lambda std::sort(vec.begin(), vec.end(), [](int a, int b) { return a < b; }); // 捕获变量 int threshold = 5; auto it = std::find_if(vec.begin(), vec.end(), [threshold](int x) { return x > threshold; }); // 可变 lambda int sum = 0; std::for_each(vec.begin(), vec.end(), [&sum](int x) mutable { sum += x; }); return 0;}std::function:#include <functional>int main() { std::function<int(int, int)> add = [](int a, int b) { return a + b; }; std::cout << add(10, 20) << std::endl; return 0;}最佳实践1. 选择合适的容器// 需要随机访问,频繁尾部插入std::vector<int> vec;// 需要频繁两端插入std::deque<int> dq;// 需要频繁中间插入删除std::list<int> lst;// 需要按键查找std::map<std::string, int> mp;// 需要快速查找,不关心顺序std::unordered_map<std::string, int> ump;2. 预分配空间std::vector<int> vec;vec.reserve(1000); // 预先分配空间,避免多次重新分配3. 使用 emplace 代替 pushstd::vector<std::pair<int, int>> vec;// 推荐vec.emplace_back(1, 2); // 原地构造// 不推荐vec.push_back(std::make_pair(1, 2)); // 可能产生临时对象4. 使用移动语义std::vector<std::string> vec;std::string str = "Hello";vec.push_back(std::move(str)); // 移动而非拷贝5. 使用算法代替循环std::vector<int> vec = {1, 2, 3, 4, 5};// 推荐int sum = std::accumulate(vec.begin(), vec.end(), 0);// 不推荐int sum = 0;for (int val : vec) { sum += val;}性能考虑时间复杂度:vector::push_back: 平均 O(1),最坏 O(n)list::insert: O(1)map::find: O(log n)unordered_map::find: 平均 O(1),最坏 O(n)空间复杂度:vector: 连续内存,无额外开销list: 每个节点额外两个指针map: 每个节点额外三个指针(红黑树)unordered_map: 每个节点额外一个指针 + 哈希表开销缓存友好性:vector: 最好(连续内存)deque: 中等(分段连续)list: 最差(分散内存)
阅读 0·2月18日 17:34

C++ 移动语义与完美转发如何使用

C++ 移动语义与完美转发C++11 引入的移动语义和完美转发是现代 C++ 性能优化的核心特性,它们通过减少不必要的拷贝操作显著提升了程序性能。移动语义左值与右值:左值(lvalue):有名字、可以取地址的对象,通常在赋值运算符左边右值(rvalue):临时对象、字面量、即将销毁的对象,通常在赋值运算符右边int a = 10; // a 是左值,10 是右值int b = a + 5; // b 是左值,a + 5 是右值std::move:std::move 将左值转换为右值引用,启用移动语义。std::string str1 = "Hello";std::string str2 = std::move(str1); // 移动构造,str1 变为空// str1 现在处于有效但未指定的状态移动构造函数与移动赋值运算符移动构造函数:class MyString {private: char* data; size_t size;public: // 普通构造函数 MyString(const char* str = "") { size = strlen(str); data = new char[size + 1]; strcpy(data, str); } // 拷贝构造函数 MyString(const MyString& other) { size = other.size; data = new char[size + 1]; strcpy(data, other.data); std::cout << "Copy constructor called" << std::endl; } // 移动构造函数 MyString(MyString&& other) noexcept { data = other.data; size = other.size; other.data = nullptr; other.size = 0; std::cout << "Move constructor called" << std::endl; } // 拷贝赋值运算符 MyString& operator=(const MyString& other) { if (this != &other) { delete[] data; size = other.size; data = new char[size + 1]; strcpy(data, other.data); } std::cout << "Copy assignment called" << std::endl; return *this; } // 移动赋值运算符 MyString& operator=(MyString&& other) noexcept { if (this != &other) { delete[] data; data = other.data; size = other.size; other.data = nullptr; other.size = 0; } std::cout << "Move assignment called" << std::endl; return *this; } ~MyString() { delete[] data; }};移动语义的优势性能提升:// 不使用移动语义std::vector<std::string> createVector() { std::vector<std::string> vec; vec.push_back("Hello"); vec.push_back("World"); return vec; // C++11 之前会进行深拷贝}// 使用移动语义std::vector<std::string> createVectorMove() { std::vector<std::string> vec; vec.push_back("Hello"); vec.push_back("World"); return vec; // C++11 之后会进行移动,避免深拷贝}// 使用auto vec = createVectorMove(); // 移动构造,无拷贝容器操作优化:std::vector<MyString> vec;vec.reserve(10);MyString str1("Hello");vec.push_back(str1); // 拷贝构造vec.push_back(std::move(str1)); // 移动构造,更快vec.emplace_back("World"); // 原地构造,最快完美转发完美转发允许函数模板将其参数完美地转发给其他函数,保持参数的值类别(左值或右值)。std::forward:template <typename T>void wrapper(T&& arg) { target(std::forward<T>(arg)); // 完美转发}void target(const std::string& str) { std::cout << "Lvalue reference: " << str << std::endl;}void target(std::string&& str) { std::cout << "Rvalue reference: " << str << std::endl;}// 使用std::string str = "Hello";wrapper(str); // 转发为左值引用wrapper(std::string("World")); // 转发为右值引用引用折叠规则:T& & → T&T& && → T&T&& & → T&T&& && → T&&万能引用万能引用(Universal Reference)是使用 T&& 声明的引用,可以绑定到左值或右值。template <typename T>void process(T&& arg) { // arg 是万能引用 if constexpr (std::is_lvalue_reference_v<T>) { std::cout << "Lvalue reference" << std::endl; } else { std::cout << "Rvalue reference" << std::endl; }}// 使用int x = 42;process(x); // T = int&,绑定到左值process(42); // T = int,绑定到右值注意: 只有在类型推导上下文中 T&& 才是万能引用,否则是右值引用。template <typename T>class MyClass { void process(T&& arg); // 右值引用,不是万能引用};template <typename T>void process(T&& arg); // 万能引用实际应用示例智能指针工厂函数:template <typename T, typename... Args>std::unique_ptr<T> makeUnique(Args&&... args) { return std::unique_ptr<T>(new T(std::forward<Args>(args)...));}// 使用class Widget {public: Widget(int x, double y) : x_(x), y_(y) {}private: int x_; double y_;};auto widget = makeUnique<Widget>(10, 3.14);线程任务包装器:template <typename F, typename... Args>auto createTask(F&& f, Args&&... args) { return std::async(std::forward<F>(f), std::forward<Args>(args)...);}// 使用void task(int x, const std::string& str) { std::cout << "Task: " << x << ", " << str << std::endl;}auto future = createTask(task, 42, "Hello");容器插入优化:template <typename Container, typename T>void insert(Container& container, T&& value) { container.push_back(std::forward<T>(value));}// 使用std::vector<std::string> vec;std::string str = "Hello";insert(vec, str); // 拷贝插入insert(vec, std::string("World")); // 移动插入noexcept 规范移动操作应该标记为 noexcept,以便标准库在重新分配时使用移动而非拷贝。class MyClass {public: MyClass(MyClass&& other) noexcept { // 移动构造实现 } MyClass& operator=(MyClass&& other) noexcept { // 移动赋值实现 return *this; }};最佳实践1. 优先使用移动语义// 推荐std::string result = std::move(tempString);// 不推荐std::string result = tempString; // 不必要的拷贝2. 使用 emplace 系列函数// 推荐vec.emplace_back(args...); // 原地构造// 不推荐vec.push_back(Type(args...)); // 可能产生临时对象3. 正确使用 std::forwardtemplate <typename T>void wrapper(T&& arg) { // 正确 target(std::forward<T>(arg)); // 错误 target(arg); // 总是左值 target(std::move(arg)); // 总是右值}4. 移动后对象的状态std::string str1 = "Hello";std::string str2 = std::move(str1);// str1 处于有效但未指定的状态// 可以安全地赋值或销毁str1 = "New value"; // OKstd::cout << str1.length(); // OK,但值未定义5. 避免对 const 对象使用 std::moveconst std::string str = "Hello";std::string str2 = std::move(str); // 不会移动,会拷贝常见错误1. 滥用 std::move// 错误std::string str = "Hello";std::cout << std::move(str); // 不必要,可能影响性能// 正确std::cout << str;2. 移动后使用对象std::string str1 = "Hello";std::string str2 = std::move(str1);std::cout << str1; // 未定义行为3. 返回局部变量时不使用 std::move// 错误std::string createString() { std::string str = "Hello"; return std::move(str); // 阻止 RVO}// 正确std::string createString() { std::string str = "Hello"; return str; // 编译器会自动优化}4. 忘记 noexcept// 不推荐MyClass(MyClass&& other); // 没有 noexcept// 推荐MyClass(MyClass&& other) noexcept; // 允许标准库优化性能对比#include <chrono>#include <vector>class BigObject {private: std::vector<int> data;public: BigObject(size_t size) : data(size) {} BigObject(const BigObject& other) : data(other.data) { std::cout << "Copy" << std::endl; } BigObject(BigObject&& other) noexcept : data(std::move(other.data)) { std::cout << "Move" << std::endl; }};void benchmark() { const size_t size = 1000000; // 拷贝性能测试 auto start = std::chrono::high_resolution_clock::now(); std::vector<BigObject> vec1; for (size_t i = 0; i < 100; ++i) { BigObject obj(size); vec1.push_back(obj); // 拷贝 } auto end = std::chrono::high_resolution_clock::now(); auto copy_time = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); // 移动性能测试 start = std::chrono::high_resolution_clock::now(); std::vector<BigObject> vec2; for (size_t i = 0; i < 100; ++i) { BigObject obj(size); vec2.push_back(std::move(obj)); // 移动 } end = std::chrono::high_resolution_clock::now(); auto move_time = std::chrono::duration_cast<std::chrono::milliseconds>(end - start); std::cout << "Copy time: " << copy_time.count() << " ms" << std::endl; std::cout << "Move time: " << move_time.count() << " ms" << std::endl;}移动语义和完美转发是现代 C++ 的重要组成部分,正确使用它们可以显著提升程序性能,特别是在处理大型对象和容器时。
阅读 0·2月18日 17:34