C++11:unique_ptr的基本用法、使用场景和最佳使用指南
文章目录
- 1. 简介
- 2. 基本语法和用法
- 2.1. 创建unique_ptr
- 2.2. 访问指向的对象
- 2.3. 所有权管理
- 3. 自定义删除器
- 4. 数组支持
- 5. 常见使用场景
- 5.1. RAII资源管理
- 5.2. 工厂模式
- 5.3. 容器中存储多态对象
- 5.4. Pimpl(指针到实现)习惯用法
- 6. 与其他智能指针的比较
- 6.1. unique_ptr vs shared_ptr
- 6.2. unique_ptr vs 原始指针
- 7. 最佳实践指南
- 7.1. 创建对象
- 7.2. 函数参数传递
- 7.3. 函数返回值
- 7.4. 需要避免的反模式
- 8. 总结
1. 简介
unique_ptr
是C++11引入的智能指针,它具有对动态分配内存对象的独占所有权。是自动内存管理的核心工具,提供了异常安全的RAII(资源获取即初始化)语义,自动管理对象的生命周期,防止内存泄漏。
unique_ptr
遵循移动语义,只能被移动,不能被复制。这样就确保了在任何时候只有一个unique_ptr
拥有特定对象的所有权。
使用 unique_ptr
有以下几个优点:
- 内存安全:自动防止内存泄漏,是现代C++的核心特性
- 零开销:提供智能指针的便利性而不牺牲性能
- 异常安全:即使在异常情况下也能正确管理资源
2. 基本语法和用法
掌握基本语法是使用unique_ptr的基础,不同的创建和访问方式适用于不同的场景,了解它们有助于写出安全高效的代码。
2.1. 创建unique_ptr
可以通过原生指针、make_unique
、new
指针和默认初始化的方式创建 unique_ptr
。
#include <memory>// 方法1:使用new(不推荐)
std::unique_ptr<int> ptr1(new int(42));// 方法2:使用make_unique(推荐,C++14)
std::unique_ptr<int> ptr2 = std::make_unique<int>(42);// 方法3:默认构造(空指针)
std::unique_ptr<int> ptr3;// 方法4:从原始指针构造
int* raw_ptr = new int(100);
std::unique_ptr<int> ptr4(raw_ptr);
应该使用哪种创建方式比较好?
make_unique
最佳:提供异常安全,避免内存泄漏,代码更简洁- 避免直接new:直接使用new容易在异常时造成内存泄漏
- 空指针的用途:用于延迟初始化或条件性对象创建
- 原始指针转换:用于接管已有的原始指针,但要确保不会重复删除
2.2. 访问指向的对象
创建一个 unique_ptr
之后,需要通过这个指针访问所指向的对象。
访问对象的内容有两种常见的方式:
- 解引用操作符:直接访问对象的值,适用于简单类型
- 箭头操作符:访问对象的成员,特别是对于类对象
std::unique_ptr<int> ptr = std::make_unique<int>(42);// 解引用操作符
int value = *ptr; // 获取值: 42
std::cout << *ptr << std::endl; // 输出: 42// 箭头操作符(对于对象指针)
class Person {
public:std::string name;void speak() { std::cout << name << " is speaking" << std::endl; }
};std::unique_ptr<Person> person = std::make_unique<Person>();
person->name = "Alice";
person->speak(); // Alice is speaking// get()方法获取原始指针
int* raw = ptr.get(); // 获取原始指针,但不转移所有权
2.3. 所有权管理
由于 unique_ptr
是独占所有权,所以所有权只能转移或者消亡。那么,有哪些引起所有权变化的操作呢?
std::move
:转移一个指针的所有权release()
:释放unique_ptr
的所有权到原生指针,此时必须手动释放原生指针所指向的内存。reset()
或者reset(make_unique<int>(10))
:前者删除当前对象,并将指针设置为nullptr
;后者删除当前对象,指向新对象。swap()
:交换两者指针的所有权。
// 移动所有权
std::unique_ptr<int> ptr1 = std::make_unique<int>(42);
std::unique_ptr<int> ptr2 = std::move(ptr1); // ptr1变为nullptr,ptr2拥有所有权// 释放所有权
std::unique_ptr<int> ptr = std::make_unique<int>(42);
int* raw_ptr = ptr.release(); // ptr变为nullptr返回原始指针
// 注意:必须手动delete raw_ptr// 重置指针
ptr.reset(); // 删除当前对象,设为nullptr
ptr.reset(new int(100)); // 删除当前对象,指向新对象// 交换两个unique_ptr
std::unique_ptr<int> ptr1 = std::make_unique<int>(1);
std::unique_ptr<int> ptr2 = std::make_unique<int>(2);
ptr1.swap(ptr2); // 或 std::swap(ptr1, ptr2);
API 使用场景:
- 移动语义:高效转移所有权,避免不必要的复制,体现独占所有权语义
- 谨慎使用release():在需要与C风格API交互时使用,但容易引入内存泄漏
- reset()灵活管理:动态改变指向的对象,提供运行时灵活性
- swap()高效交换:避免临时对象,性能优化的需要
3. 自定义删除器
默认删除器只能处理用new分配的对象,但实际开发中经常需要管理各种资源(文件、网络连接、系统句柄等),自定义删除器提供了统一的RAII管理方式。
unique_ptr
允许自定义删除器,用于特殊的清理需求。例如下面的例子中,FILE
文件指针需要使用 fclose
函数关闭,这个时候就可以自定义文件删除器删除,避免手动调用 fclose
函数。当超出 file_ptr
的作用域时,会自动调用 FileDeleter
。
// 自定义删除器 - 函数对象
struct FileDeleter {void operator()(FILE* f) {if (f) {std::fclose(f);std::cout << "文件已关闭" << std::endl;}}
};std::unique_ptr<FILE, FileDeleter> file_ptr(std::fopen("test.txt", "w"));
4. 数组支持
动态数组在C++中很常见,unique_ptr
提供的数组支持解决了数组内存管理的痛点,自动使用正确的 delete[]
操作符,避免未定义行为。
unique_ptr
专门支持动态数组:
// 动态数组
std::unique_ptr<int[]> arr = std::make_unique<int[]>(10);// 使用下标访问
for (int i = 0; i < 10; ++i) {arr[i] = i * i;std::cout << arr[i] << " ";
}// 注意:数组版本不支持解引用和箭头操作符
// *arr; // 编译错误
// arr->x; // 编译错误
使用原因:
- 正确删除:自动使用delete[]而不是delete,避免未定义行为
- 类型安全:编译期防止在数组指针上使用解引用操作
- 内存安全:自动管理数组生命周期,防止内存泄漏
- 性能优化:避免使用vector的开销,适合简单的数组需求
5. 常见使用场景
了解典型使用场景有助于在实际开发中正确选择 unique_ptr
,下面这些模式是工业级代码的常见实践,掌握它们能显著提高代码质量。
5.1. RAII资源管理
结合 unique_ptr
和 RAII 管理文件资源、数据库连接、网络连接等。下面是一个管理文件资源的例子。
struct FileDeleter {void operator()(FILE* f) {if (f) {std::fclose(f);std::cout << "文件已关闭" << std::endl;}}
};std::unique_ptr<FILE, FileDeleter> file_ptr(std::fopen("test.txt", "w"));
RAII核心优势:
- 自动资源管理:构造时获取资源,析构时自动释放,无需手动管理;
- 异常安全保证:即使在异常情况下,
unique_ptr
也确保资源正确清理; - 组合资源管理:可以在同一类中管理多种不同类型的资源;
- 移动语义支持:支持高效的资源所有权转移,避免不必要的复制。
5.2. 工厂模式
class Shape {
public:virtual ~Shape() = default;virtual void draw() = 0;
};class Circle : public Shape {
public:void draw() override { std::cout << "绘制圆形" << std::endl; }
};class Rectangle : public Shape {
public:void draw() override { std::cout << "绘制矩形" << std::endl; }
};// 工厂函数返回unique_ptr
std::unique_ptr<Shape> create_shape(const std::string& type) {if (type == "circle") {return std::make_unique<Circle>();} else if (type == "rectangle") {return std::make_unique<Rectangle>();}return nullptr;
}// 使用
auto shape = create_shape("circle");
if (shape) {shape->draw(); // 绘制圆形
}
使用原因:
- 明确所有权:工厂返回unique_ptr明确表示调用者拥有对象
- 多态支持:基类指针可以指向派生类对象,支持多态
- 内存安全:对象自动管理,无需手动delete
- 空值语义:返回nullptr表示创建失败,语义清晰
5.3. 容器中存储多态对象
std::vector<std::unique_ptr<Shape>> shapes;shapes.push_back(std::make_unique<Circle>());
shapes.push_back(std::make_unique<Rectangle>());for (const auto& shape : shapes) {shape->draw();
}
// 容器销毁时,所有对象自动释放
使用原因:
- 多态容器:可以在同一容器中存储不同类型的对象
- 自动清理:容器销毁时所有对象自动释放,无内存泄漏
- 移动语义:对象在容器中移动而不是复制,性能更好
- 异常安全:即使在容器操作中发生异常,已创建的对象也会被正确清理
5.4. Pimpl(指针到实现)习惯用法
Pimpl的核心思想是在类的公有接口(通常在.h
头文件中声明)中,不直接包含私有成员变量和私有成员函数的具体声明,而是只包含一个指向不完整类型(通常是一个内部类或结构体,称为Impl
类)的指针。这个Impl
类则包含了所有原先的私有成员和实现细节。
实现逻辑:
- 头文件:
- 前向声明一个内部实现类,例如
class Impl
; - 持有一个指向该实现类的只能指针,通常是
std::unique_ptr<Impl>
,因为Pimpl通常意味着独占所有权;
- 前向声明一个内部实现类,例如
- 实现文件:
- 定义完整的内部
Impl
类,包含所有私有数据成员和辅助函数; - 实现外部类的构造函数、析构函数、拷贝构造函数等;
- 实现外部类的公有函数,函数体通过
Impl
指针调用内部类的函数。
- 定义完整的内部
有什么好处?
- 减少编译依赖。这是Pimpl最主要的优点。当类的私有成员(尤其是那些依赖于其他复杂头文件的成员)发生改变时,只需要重新编译该类的.cpp实现文件,而不需要重新编译所有包含该类头文件的客户端代码。
- 隐藏实现细节。类的用户只能看到公有接口,完全不知道其内部实现细节(如私有成员变量的类型和数量),这增强了封装性。例如,我们为甲方开发了一套库,但是不希望甲方知道算法的内部逻辑。
有什么缺点?
- 增加了构造和析构函数的开销。需要额外在堆上为Impl对象分配内存(通过
std::make_unique
),并进行构造。虽然std::unique_ptr
能很好地管理生命周期,但堆分配本身有开销。 - 增加了调试的复杂度。
代码例子:
// Widget.h - 头文件
class Widget {
public:Widget();Widget(int value, const std::string& name);~Widget();// 拷贝和移动操作需要特别处理Widget(const Widget& other);Widget& operator=(const Widget& other);Widget(Widget&& other) noexcept;Widget& operator=(Widget&& other) noexcept;// 公共接口void do_something();void set_value(int value);int get_value() const;std::string get_name() const;private:class Impl; // 前向声明,不暴露实现细节std::unique_ptr<Impl> pImpl; // 指向实现的智能指针
};
下面是实现文件:
// Widget.cpp - 实现文件
#include "Widget.h"
#include <iostream>
#include <vector>
#include <map>
#include <complex_third_party_library.h> // 只在.cpp中包含// 实现类定义(完全隐藏)
class Widget::Impl {
public:Impl(int val, const std::string& n) : value(val), name(n) {}void do_something() {std::cout << "处理 " << name << " 的值: " << value << std::endl;// 复杂的实现逻辑...process_data();use_third_party_library();}void set_value(int val) { value = val; }int get_value() const { return value; }std::string get_name() const { return name; }private:int value;std::string name;std::vector<double> data; // 复杂的数据结构std::map<std::string, int> cache;ThirdPartyObject complex_obj; // 第三方库对象void process_data() {// 复杂的内部逻辑}void use_third_party_library() {// 使用第三方库的代码}
};// 公共接口的实现
Widget::Widget() : pImpl(std::make_unique<Impl>(0, "default")) {}Widget::Widget(int value, const std::string& name) : pImpl(std::make_unique<Impl>(value, name)) {}Widget::~Widget() = default; // unique_ptr自动清理// 拷贝构造函数
Widget::Widget(const Widget& other) : pImpl(std::make_unique<Impl>(*other.pImpl)) {}// 拷贝赋值操作符
Widget& Widget::operator=(const Widget& other) {if (this != &other) {*pImpl = *other.pImpl;}return *this;
}// 移动构造函数
Widget::Widget(Widget&& other) noexcept = default;// 移动赋值操作符
Widget& Widget::operator=(Widget&& other) noexcept = default;// 委托给实现类的方法
void Widget::do_something() {pImpl->do_something();
}void Widget::set_value(int value) {pImpl->set_value(value);
}int Widget::get_value() const {return pImpl->get_value();
}std::string Widget::get_name() const {return pImpl->get_name();
}
6. 与其他智能指针的比较
了解不同智能指针的特点有助于在合适的场景选择合适的工具,避免过度工程或性能损失,这是高级C++程序员必备的知识。
6.1. unique_ptr vs shared_ptr
特性 | unique_ptr | shared_ptr |
---|---|---|
所有权 | 独占 | 共享 |
内存开销 | 低(通常只有一个指针大小) | 高(需要引用计数) |
性能 | 高(无引用计数开销) | 较低(原子操作开销) |
线程安全 | 移动操作需要同步 | 引用计数是线程安全的 |
使用场景 | 明确单一所有者 | 需要共享所有权 |
选择原因:
- unique_ptr优先:大多数情况下对象只需要一个所有者;
- 性能考虑:unique_ptr零开销,shared_ptr有引用计数开销;
- 设计清晰:unique_ptr强制明确所有权关系,设计更清晰;
- 特定需求:只有真正需要共享所有权时才使用shared_ptr。
6.2. unique_ptr vs 原始指针
// 原始指针的问题
void problematic_function() {int* ptr = new int(42);if (some_condition) {return; // 内存泄漏!}risky_operation(); // 如果抛出异常,内存泄漏!delete ptr; // 可能永远执行不到
}// unique_ptr解决方案
void safe_function() {auto ptr = std::make_unique<int>(42);if (some_condition) {return; // 自动清理,无泄漏}risky_operation(); // 异常安全,自动清理// 函数结束时自动清理
}
选择原因:
- 内存安全:unique_ptr防止内存泄漏,原始指针容易泄漏
- 异常安全:unique_ptr提供强异常安全保证
- 代码简洁:无需手动管理内存,减少样板代码
- 性能相等:unique_ptr零开销,性能与原始指针相同
7. 最佳实践指南
为什么重要:最佳实践是多年经验的总结,遵循这些指导原则可以避免常见陷阱,写出高质量、可维护的代码,这对团队协作和项目维护至关重要。
7.1. 创建对象
// 推荐:使用make_unique
auto ptr = std::make_unique<MyClass>(args);// 不推荐:使用new
std::unique_ptr<MyClass> ptr(new MyClass(args));
避免直接使用 new 创建智能指针,因为它无法提供异常安全,而make_unique
提供异常安全。
7.2. 函数参数传递
// 传递所有权:按值传递
void take_ownership(std::unique_ptr<Widget> widget) {// 函数拥有widget的所有权
}// 借用使用:传递原始指针或引用
void use_widget(Widget* widget) {// 临时使用,不改变所有权
}void use_widget_ref(const Widget& widget) {// 只读使用
}// 调用示例
auto widget = std::make_unique<Widget>();
use_widget(widget.get()); // 借用
use_widget_ref(*widget); // 借用(引用)
take_ownership(std::move(widget)); // 转移所有权
// widget现在是nullptr
使用原因:
- 意图明确:参数类型清楚表达函数是否需要所有权
- 性能优化:借用时避免不必要的所有权转移
- 接口设计:清晰的接口设计减少误用
- 兼容性:原始指针参数与现有代码兼容
7.3. 函数返回值
// 推荐:返回unique_ptr表明所有权转移
std::unique_ptr<Widget> create_widget() {return std::make_unique<Widget>();
}// 工厂函数的典型模式
std::unique_ptr<Shape> shape_factory(ShapeType type) {switch (type) {case ShapeType::Circle:return std::make_unique<Circle>();case ShapeType::Rectangle:return std::make_unique<Rectangle>();default:return nullptr; // 表示创建失败}
}
使用原因:
- 所有权转移:明确表示调用者获得对象所有权
- 异常安全:返回过程中的异常不会导致内存泄漏
- 错误处理:nullptr表示创建失败,语义清晰
- 移动语义:高效的对象传递,避免复制
7.4. 需要避免的反模式
// 反模式1:不要从unique_ptr创建shared_ptr
std::unique_ptr<Widget> unique_widget = std::make_unique<Widget>();
// 不推荐
std::shared_ptr<Widget> shared_widget(unique_widget.release());// 反模式2:不要将同一个原始指针给多个unique_ptr
Widget* raw = new Widget();
std::unique_ptr<Widget> ptr1(raw); // 危险!
std::unique_ptr<Widget> ptr2(raw); // 双重删除!// 反模式3:不要保存get()返回的指针
auto ptr = std::make_unique<Widget>();
Widget* raw = ptr.get();
ptr.reset(); // 现在raw是悬空指针!
// raw->do_something(); // 未定义行为!
避免原因:
- 双重删除:多个智能指针管理同一对象会导致双重删除
- 悬空指针:保存get()返回的指针容易产生悬空指针
- 设计混乱:混用不同的智能指针类型破坏设计清晰性
- 难以调试:这些反模式产生的bug往往难以定位和修复
8. 总结
unique_ptr
是现代C++中内存管理的基石,它提供了:
- 自动内存管理:无需手动调用 delete;
- 异常安全:即使在异常情况下也能正确清理资源;
- 移动语义:高效的所有权转移;
- 零开销:运行时性能与原始指针相当。
使用unique_ptr
的关键原则:
- 优先使用
make_unique
创建对象; - 通过移动语义转移所有权;
- 使用原始指针或引用进行临时访问;
- 在容器中存储
unique_ptr
实现多态; - 避免混合使用智能指针和原始指针。
掌握unique_ptr
是编写现代C++代码的必备技能,它能有效防止内存泄漏,提高代码的安全性和可维护性。
相关文章:
C++11:unique_ptr的基本用法、使用场景和最佳使用指南
文章目录 1. 简介2. 基本语法和用法2.1. 创建unique_ptr2.2. 访问指向的对象2.3. 所有权管理 3. 自定义删除器4. 数组支持5. 常见使用场景5.1. RAII资源管理5.2. 工厂模式5.3. 容器中存储多态对象5.4. Pimpl(指针到实现)习惯用法 6. 与其他智能指针的比较…...

测量3D翼片的距离与角度
1,目的。 测量3D翼片的距离与角度。说明: 标注A 红色框选的区域即为翼片,本示例的3D 对象共有3个翼片待测。L1与L2的距离、L1与L2的角度即为所求的翼片距离与角度。 2,原理。 使用线结构光模型(标定模式࿰…...

零基础学习计算机网络编程----socket实现UDP协议
本章将会详细的介绍如何使用 socket 实现 UDP 协议的传送数据。有了前面基础知识的铺垫。对于本章的理解将会变得简单。将会从基础的 Serve 的初始化,进阶到 Client 的初始化,以及 run。最后实现一个简陋的小型的网络聊天室。 目录 1.UdpSever.h 1.1 构造…...

谷歌地图2022高清卫星地图手机版v10.38.2 安卓版 - 前端工具导航
谷歌地图2022高清卫星地图手机版是由谷歌公司推出的一款非常好用的手机地图服务软件,用户能够通过精准的导航和定位来查看地图,周边的商店等生活服务都会在地图上显示,用起来超级方便。 谷歌卫星高清地图 下载链接:夸克网盘分享 …...

RAG的ETL Pipeline源码解读
原文链接:SpringAI(GA):RAG下的ETL源码解读 教程说明 说明:本教程将采用2025年5月20日正式的GA版,给出如下内容 核心功能模块的快速上手教程核心功能模块的源码级解读Spring ai alibaba增强的快速上手教程 源码级解读 版本&a…...

杭州白塔岭画室怎么样?和燕壹画室哪个好?
杭州作为全国美术艺考集训的核心区域,汇聚了众多实力强劲的画室,其中白塔岭画室和燕壹画室备受美术生关注。对于怀揣艺术梦想的考生而言,选择一所契合自身需求的画室,对未来的艺术之路影响深远。接下来,我们将从多个维…...
Linux文件系统:从VFS到Ext4的奇幻之旅
Linux文件系统:从VFS到Ext4的奇幻之旅 从虚拟文件到物理磁盘的魔法桥梁 引言:数据宇宙的"时空管理者" 当你在Linux终端输入ls -l时,一场跨越多个抽象层的精密协作悄然展开。文件系统作为操作系统中最复杂且最精妙的子系统之一&…...
5月底 端午节
感觉五月写的很少啊,尤其是这一周,真的事情特别多可能。但是实际上我晚上回宿舍之后大概九点十点这块,最后睡觉一般在十一点半到十二点。这一段时间我基本上都是浪费了。要么在打游戏要么在刷视频。但是最基本的生活保障和学习都没有做好。。…...
为何选择Spring框架学习设计模式与编码技巧?
📌 结论先行 推荐项目:Spring Framework 推荐理由:设计模式覆盖全面 编码技巧教科书级实现 Java 生态基石地位 🏆 三维度对比分析 维度SpringMyBatisXXL-JOB设计模式⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐代码抽象⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐生态价…...
软件评测师 综合测试 真题笔记
计算机组成原理 用作科学计算为主的计算机,其对主机的运算速度要求很高,应该重点考虑 CPU的主频和字长,以及内存容量; 用作大型数据库处理为主的计算机,其对主机的内存容量、存取速度和外存储器的读写速度要求较高; 对…...

晶台光耦在手机PD快充上的应用
光耦(光电隔离器)作为关键电子元件,在手机PD快充中扮演信号隔离与传输的“安全卫士”。其通过光信号实现电气隔离,保护手机电路免受高电压损害,同时支持实时信号反馈,优化充电效率。 晶台品牌推出KL817、KL…...
JS对数据类型的检测
typeof对基本数据类型有用,但是对引用数据类型不行 console.log(typeof 2)//number console.log(typeof [])//object 失效 instanceof只对引用数据类型有用 console.log([] instanceof Array) //true console.log(2 instanceof String) //false constructor基本…...
llama.cpp:纯 C/C++ 实现的大语言模型推理引擎详解一
🚀 llama.cpp:纯 C/C 实现的大语言模型推理引擎详解 一、什么是 llama.cpp? llama.cpp 是一个由 Georgi Gerganov 开源的项目,旨在使用纯 C/C 在 CPU 上运行 Meta 的 LLaMA 系列大语言模型。 它通过量化、优化注意力机制和内存…...

【亲测有效 | Cursor Pro每月500次快速请求扩5倍】(Windows版)Cursor中集成interactive-feedback-mcp
前言:使用这个interactive-feedback-mcp组件可以根据用户反馈来决定是否结束这一次的请求。如果本次请求并没有解决我们的问题,那我们便可以选择继续这次请求流程,直到问题解决。这样的话,就可以避免为了修复bug而白白多出的请求。…...
BaseTypeHandler用法-笔记
1.BaseTypeHandler简介 org.apache.ibatis.type.BaseTypeHandler 是 MyBatis 提供的一个抽象类,通过继承该类并实现关键方法,可用于实现 Java 类型 与 JDBC 类型 之间的双向转换。当数据库字段类型与 Java 对象属性类型不一致时(如ÿ…...
鸿蒙OSUniApp集成WebGL:打造跨平台3D视觉盛宴#三方框架 #Uniapp
UniApp集成WebGL:打造跨平台3D视觉盛宴 在移动应用开发日新月异的今天,3D视觉效果已经成为提升用户体验的重要手段。本文将深入探讨如何在UniApp中集成WebGL技术,实现炫酷的3D特效,并特别关注鸿蒙系统(HarmonyOS)的适配与优化。 …...
华为盘古 Ultra MoE 模型:国产 AI 的技术突破与行业影响
2025 年 5 月 30日,华为正式发布参数规模达 7180 亿的盘古 Ultra MoE 模型,全程基于昇腾 AI 计算平台完成训练。这一进展标志着中国在超大规模人工智能模型领域的自主研发能力达到新高度,同时也为全球 AI 技术发展提供了新的技术路径。 盘古 …...
Payload CMS:开发者优先的Next.js原生开源解决方案,重新定义无头内容管理
在无头内容管理系统(CMS)竞争激烈的今天,Payload CMS凭借其独特的开发理念和技术架构迅速崛起,成为Microsoft、ASICS、Blue Origin等创新企业的选择。这款基于Node.js与TypeScript构建的开源解决方案,正在彻底改变开发…...

CRM管理软件的数据可视化功能使用技巧:让数据驱动决策
在当今数据驱动的商业环境中,CRM管理系统的数据可视化功能已成为企业优化客户管理、提升销售效率的核心工具。据企销客研究显示,具备优秀可视化能力的CRM系统,用户决策效率可提升47%。本文将深入解析如何通过数据可视化功能最大化CRM管理软件…...

linux批量创建文件
文章目录 批量创建空文件touch命令批量创建空文件循环结构创建 创建含内容文件echo重定向多行内容写入 按日期创建日志文件根据文件中的列内容,创建文件一行只有一列内容一行有多列内容 批量创建空文件 touch命令批量创建空文件 # 创建文件file1.txt到file10.txt …...

颠覆传统!单样本熵最小化如何重塑大语言模型训练范式?
颠覆传统!单样本熵最小化如何重塑大语言模型训练范式? 大语言模型(LLM)的训练往往依赖大量标注数据与复杂奖励设计,但最新研究发现,仅用1条无标注数据和10步优化的熵最小化(EM)方法…...
华为数据之道 精读——【173页】读书笔记【附全文阅读】
在数字化浪潮中,企业数据管理的优劣直接关乎竞争力。华为凭借丰富实践经验总结的《华为数据之道》,为企业提供了全面且深入的数据治理方案。 笔记聚焦数字化转型与数据治理的紧密联系。华为作为非数字原生企业,在转型过程中克服了产业链条长、数据复杂等诸多难题,其…...
数据库OCP专业认证培训
认证简介 OCP 即 Oracle 数据库认证专家(Oracle Certified Professional),是 Oracle 公司的 Oracle 数据库 DBA(Database Administrator 数据库管理员)认证课程。通过该认证,表明持证人能够管理大型数据库…...

ssm学习笔记day04
RequestMapping 首先添加依赖 Maven的配置 测试 在controller创建HelloController,如果只加RequestMapping,默认跳转到新页面 如果要是加上ResponseBody就把数据封装在包(JSON),标签RestController是前后分离的注解(因为默认用…...

Read View在MVCC里如何工作
Read View的结构 Read View中有四个重要的字段: m_ids:创建 Read View 时,数据库中启动但未提交的「活跃事务」的事务 id 列表 。min_trx_id:创建 Read View 时,「活跃事务」中事务 id 最小的值,即 m_ids …...
HDFS 写入和读取流程
HDFS 写入流程细化 1. 主线流程速记口诀 “先找主脑定文件,分配块副找节点;流水传块多副本,写完通知主脑存。” 2. 详细流程拆解 1. 客户端请求上传(Create 文件) 关键方法: org.apache.hadoop.fs.File…...

建筑工程施工进度智能编排系统 (SCS-BIM)
建筑工程施工进度智能编排 (SCS-BIM) 源码可见于:https://github.com/Asionm/SCS-BIM 项目简介 本项目是一个面向建筑工程的施工进度智能编制平台,用户只需上传一份标准 IFC 建筑信息模型文件,系统将自动完成以下任务: 解析模…...
Laravel模型状态:深入理解Eloquent的隐秘力量
Laravel的Eloquent ORM(对象关系映射)提供了强大且灵活的功能来处理数据库操作。深入理解Eloquent模型状态对于优化应用程序性能和维护代码的简洁性至关重要。本文将详细探讨Laravel Eloquent的模型状态及其隐秘力量。 一、Eloquent模型的基本概念 Elo…...
Spring Cloud Eureka:微服务架构中的服务注册与发现核心组件
前言 在微服务架构日益流行的今天,服务注册与发现机制成为了构建弹性、可扩展分布式系统的关键。作为Spring Cloud生态中的核心组件,Eureka为微服务架构提供了高效的服务注册与发现解决方案。本文将深入探讨Eureka的设计原理、核心机制以及在实际项目中…...
matlab实现求解兰伯特问题
求解兰伯特问题的matlab代码,非常好用 solve_lambertLYP.m , 1899 StumpffC.m , 136 StumpffdF.m , 294 StumpffF.m , 151 StumpffS.m , 167 Stumpffy.m , 96 text2.m , 104...