【C++】指针与智慧的邂逅:C++内存管理的诗意
文章目录
- RAII
- 智能指针
- `auto_ptr`
- `unique_ptr`
- shared_ptr
- 模拟实现
- 定制删除器
- 循环引用 和 `weak_ptr`
RAII
RAII(Resource Acquisition Is Initialization)
是一种广泛应用于 C++ 等编程语言中的编程范式,它的核心思想是:资源的获取和释放与对象的生命周期绑定
。在 RAII 中,资源(如内存、文件句柄、网络连接等)的获取通常发生在对象的构造函数中,而资源的释放则发生在对象的析构函数中。
这种设计模式确保了资源在不再需要时自动释放,从而避免了手动管理资源的复杂性和潜在的错误(如内存泄漏和资源泄露)。
核心思想
资源获取
: 当一个对象被创建时,它会立即获取某个资源。例如,分配内存、打开文件或创建数据库连接等。资源释放
: 当该对象超出作用域或被销毁时,它的析构函数会自动释放相应的资源。这意味着开发者不需要显式地释放资源,降低了出错的概率。
实现方式
构造函数
:在对象创建时,负责分配所需的资源。例如,在构造函数中打开一个文件或分配一块内存。析构函数
:在对象销毁时,负责释放该对象占用的资源。当对象的生命周期结束时,析构函数会自动执行,释放资源。
RAII 的优势
自动资源管理
: RAII 自动处理资源的释放,不需要显式调用资源释放代码,减少了出错的可能性(如忘记释放资源)。异常安全
: RAII 能够保证即使程序中发生异常,资源也会被正确释放。例如,在 try 块中的对象被销毁时,析构函数会自动释放资源,从而避免资源泄漏。简洁性和易维护性
: 使用 RAII 模式可以使资源管理代码更加简洁和模块化,减少了繁琐的手动管理。防止内存泄漏
: 通过将资源与对象的生命周期绑定,可以有效防止内存泄漏、悬挂指针等问题。
RAII 的缺点
不能自由控制资源释放的时机
: 在 RAII 模式中,资源的释放依赖于对象的生命周期,无法显式控制资源的释放时机。如果需要在对象销毁之前释放资源,RAII 可能不适用。资源生命周期绑定问题
: RAII 通过对象生命周期管理资源,这对于某些类型的资源可能不适用。例如,某些外部资源(如数据库连接)可能需要在特定时刻关闭,而不仅仅是在对象销毁时。
RAII 的应用场景
内存管理
:例如,unique_ptr
和shared_ptr
是 C++ 中的智能指针,它们的实现就是基于 RAII 模式,自动管理内存资源。文件操作
:如上文所示,RAII 可以用于文件的打开和关闭,确保即使发生异常,文件资源也会被自动释放。数据库连接
:RAII 可用于数据库连接的管理,确保连接在对象生命周期结束时被自动关闭。线程锁管理
:通过 RAII 模式,锁的获取和释放可以自动管理,避免忘记释放锁导致死锁。
智能指针
智能指针(Smart Pointer
是现代 C++ 中用于自动管理动态内存的一种工具,它通过封装原始指针,提供对内存资源的自动管理,帮助避免常见的内存管理错误,如内存泄漏和悬挂指针。
智能指针实际上是一个类,它重载了指针操作符(如 *
和 ->
),使得使用智能指针的代码和普通指针一样简便,但它能自动处理资源的释放。
C++标准库中的智能指针都在 <memory>
这个头文件下,智能指针主要有 auto_ptr
、unique_ptr
、shared_ptr
和 weak_ptr
等。
auto_ptr
auto_ptr
是C++98标准中引入的一个智能指针类型,通过自动释放资源来避免内存泄漏和悬挂指针的问题。
1. auto_ptr 的缺陷
auto_ptr
的设计存在巨大缺陷,在涉及资源所有权转移时(拷贝或者赋值时),原auto_ptr
不再拥有资源,资源的所有权转移给目标auto_ptr
,这导致了原auto_ptr
变成一个悬挂指针(类似于空指针)。
代码示例:
//模拟一个日期类
struct Date
{int _year;int _month;int _day;Date(int year = 2000, int month = 1, int day = 1):_year(year),_month(month),_day(day){}~Date(){cout << "~Date" << endl;}};int main()
{auto_ptr<Date> ap1(new Date);//拷贝时,Date的管理权限从ap1转移ap2,ap1被置空auto_ptr<Date> ap2(ap1);//ap1相当于空指针了,再去访问会造成程序崩溃//ap1->_day;//赋值也是同样的道理,Date的管理权限从ap2转移ap3,ap2被置空auto_ptr<Date> ap3;ap3 = ap2;//ap2被置空,访问会造成程序崩溃ap2->_day;return 0;
}
auto_ptr
的设计存在缺陷,在在涉及资源所有权转移时,其行为会造成意外的错误,auto_ptr
在C++11中被废弃,不推荐使用!
2. auto_ptr 的模拟实现
auto_ptr
的模拟实现比较简单,在涉及资源的转移时,将原指针置空即可。
template<class T>
class auto_ptr
{
public:auto_ptr(T* ptr = nullptr):_ptr(ptr){}auto_ptr(auto_ptr<T>& ap):_ptr(ap._ptr){ap._ptr = nullptr;}auto_ptr<T>& operator=(auto_ptr<T>& ap){if (_ptr != ap._ptr){if (_ptr)delete _ptr;_ptr = ap._ptr;ap._ptr = nullptr}return *this;}~auto_ptr(){if (_ptr)delete _ptr;_ptr = nullptr;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;
};
unique_ptr
unique_ptr
是独占式的智能指针,表示指向一个动态分配的对象的唯一所有者。该指针不支持拷贝和赋值,但支持移动构造或者赋值。
当一个资源只能有一个拥有者时,使用 unique_ptr
是最合适的选择。
代码示例:
unique_ptr<Date> up1(new Date);
//不支持拷贝或者赋值
//unique_ptr<Date> up2(up1);
//unique_ptr<Date> up3; up3 = up1;//支持移动构造或者赋值,但是ap1置空了,谨慎使用
unique_ptr<Date> up2(move(up1));
unique_ptr<Date> up3;
up3 = move(up2);
1. make_unique
make_unique
是 C++11/14 标准库中引入的一个函数模板,用于创建动态分配的对象并返回一个 unique_ptr
,从而安全高效地管理对象的生命周期。
template <typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args);
与直接使用 new
操作符相比的优势:
- 避免手动调用
new
和delete
:使用make_unique
能够简化动态内存分配,避免使用裸指针容易产生的内存泄漏或未定义行为。 - 性能优化 :它能够一次性分配对象和控制块所需的内存,减少额外开销。
- 强异常安全性:使用
make_unique
时,不会因为对象构造和分配的中间异常而泄漏内存。
//使用 make_unique 创建一个 int 类型的 unique_ptr(推荐)
auto up1 = make_unique<int>(20);//直接使用 unique_ptr(容易出错),如果构造函数抛异常就会出现内存泄漏
unique_ptr<int> up2(new int(10));
2. 定制删除器
unique_ptr
在释放资源时,默认是 delete _ptr
,如果指向的资源是 new type[num]
而来的,默认释放资源的方式就不适合了,需要 delete[]
的方式是释放资源,这时我们需要定制删除器。
new []
的方式经常使用,库里已经有了特化版本,而对于定制删除器,仿函数、函数指针、lamba表达式都可作为删除器。
不过要注意的是传定制删除器给 unique_ptr
,是传给模板参数,其构造参数也要传。
代码示例:
//new []特化
unique_ptr<Date[]> up1(new Date[5]);// 仿函数作删除器,将其传到模板参数,仿函数构造的对象可以直接调用,不需要传给构造参数
unique_ptr<Date, DeleteArray<Date>> up2(new Date[5]);//函数指针作删除器,既要传模板参数也要传构造参数
unique_ptr<Date, void(*)(Date*)> up3(new Date[5], DeleteArrayFunc<Date>);// lambda表达式作删除器,decltype获取delArr的类型
auto delArr = [](Date* ptr) {delete[] ptr; };
unique_ptr<Date, decltype(delArr)> up4(new Date[5], delArr);
简单说一下定制删除器的底层,将定制删除器的类型传过去,利用其类型创建删除器对象并用传给构造参数的具体定制删除器对象来初始化,这样底层就有了外层传进来的定制删除器,然后利用删除器释放资源。
3. unique_ptr 的模拟实现
unique_ptr
的模拟实现也比较简单,将其构造函数和赋值重载函数 delete
即可。
template<class T>
class unique_ptr
{
public://防止隐式类型转换explicit unique_ptr(T* ptr):_ptr(ptr){}~unique_ptr(){if (_ptr)delete _ptr;_ptr = nullptr;}unique_ptr(const unique_ptr<T>& up) = delete;unique_ptr<T>& operator=(const unique_ptr<T>& up) = delete;unique_ptr(unique_ptr<T>&& up):_ptr(up._ptr){up._ptr = nullptr;}unique_ptr<T>& operator=(unique_ptr<T>&& up){delete _ptr;_ptr = up._ptr;up._ptr = nullptr;}private:T* _ptr;
};
有关 explicit
:
关键字 explicit
的作用是修饰构造函数,防止隐式类型转换。
Date* ptr = new Date;
//hz::unique_ptr<Date> up1 = ptr; 这种写法编译会出错,explicit不允许隐式类型转换,本质是构造+拷贝
unique_ptr<Date> up1(ptr);
使用 explicit
修饰单参数构造函数可以提高代码的可读性,减少维护负担。
shared_ptr
shared_ptr
是 C++11 标准引入的一种智能指针,用于管理动态分配的对象,并允许多个 shared_ptr
实例共享同一对象的所有权。shared_ptr
使用 引用计数 来追踪有多少个 shared_ptr
对象共享资源,并在最后一个 shared_ptr
被销毁时自动释放资源。这种机制确保了内存管理的安全性,避免了内存泄漏,同时允许多个对象共享相同的资源。
shared_ptr
是一种 共享所有权 的智能指针,而非独占所有权(像 unique_ptr
)。多个 shared_ptr
对象可以共同管理一个动态分配的对象,而不必担心资源的重复释放或遗漏释放。
代码示例:
//创建一个 shared_ptr 管理 Date 对象
shared_ptr<Date> sp1(new Date);//复制sp1,增加引用计数
shared_ptr<Date> sp2(sp1);
cout << "Reference count: " << sp1.use_count() << endl;//当 sp1 和 sp2 超出作用域时,Date 对象会被自动销毁
代码解析:
1.shared_ptr<Date> sp1(new Date);
sp1
是一个shared_ptr
,它管理一个动态分配的Date
对象。此时引用计数为 1。
2.shared_ptr<Date> sp2(sp1);
sp2
是sp1
的副本,意味着它也指向同一个Date
对象,引用计数增加到 。
3.sp1.use_count()
返回当前有多少个 shared_ptr
管理相同的对象。此时返回 2。
4.当 sp1
和 sp2
超出作用域时,它们的引用计数都会减少。当引用计数降到 0 时,Date
对象会自动销毁。
有关 make_shared
make_shared
也是一个函数模板,用于创建共享指针,可以接受任何类型的参数,并返回一个指向该类型对象的共享指针。
template <class T, class... Args>shared_ptr<T> make_shared (Args&&... args);
make_shared
与直接使用shared_ptr
的对比
特性 | make_shared | 直接用shared_ptr |
---|---|---|
语法简洁性 | 更简洁 | 需要手动调用 new |
内存分配次数 | 1 次 | 2 次(对象和引用计数分别分配) |
异常安全性 | 更安全 | 容易出现内存泄漏 |
模拟实现
对于 shared_ptr
的模拟实现,我们首先要考虑的就是引用计数的设计。
引用计数用静态成员变量是无法实现的
:
因为静态成员变量是整个类共有的,每当指向一个资源,无论是不同的资源还是相同的资源,静态成员变量都会增加,不能做到对于不同的资源都有独立的一份引用计数。
比如 sp1 和 sp2 指向着资源1,引用计数是2,在创建一个 sp3 指向资源2,由于引用计数是静态成员变量,引用计数就变成3了,这显然是错误的,sp3 的引用计数应该是1.
- 引用计数的设计应该采用动态开辟的方式,做到每一个不同的资源都有一份独立的引用计数。
以下为 shared_ptr 的实现:
template<class T>
class shared_ptr
{
public://explicit防止隐式类型转换explicit shared_ptr(T* ptr = nullptr):_ptr(ptr), _pcount(new int(1)){}~shared_ptr(){if (--(*_pcount) == 0){delete _ptr;delete _pcount;}}shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr), _pcount(sp._pcount){++(*_pcount);}shared_ptr<T>& operator=(const shared_ptr<T>& sp){if (_ptr != sp._ptr){if (--(*_pcount) == 0){delete _ptr;delete _pcount;}_ptr = sp._ptr;_pcount = sp._pcount;++(*_pcount);}return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* get() const{return _ptr;}int use_count() const{return *_pcount;}private:T* _ptr;int* _pcount;
};
shared_ptr
的成员函数的实现都比较简单,但是赋值重载函数有比较多细节要注意:
-
赋值操作要保证不是一个指针自己给自己赋值,
this != &sp
不能完全处理所有情况,因为不同的shared_ptr
对象的_ptr
可能是一样的,得用_ptr != sp._ptr
才可以完全覆盖所有情况。 -
被赋值的指针的引用计数要先要减1,判断该指针是否是最后一个指向对应资源的指针,若是则要释放原来的资源。
-
进行赋值操作,完成后引用计数要+1,最后返回
*this
。
shared_ptr<T>& operator=(const shared_ptr<T>& sp)
{if (_ptr != sp._ptr){if (--(_pcount) == 0){delete _ptr;delete _pcount;}_ptr = sp._ptr;_pcount = sp._pcount;++(*_pcount);}return *this;
}
定制删除器
shared_ptr
也可以传定制删除器,不过相比 unique_ptr
的方式,shared_ptr
传递删除器的方式只需传到构造函数的参数即可。
//其构造函数的声明
template <class U, class D> shared_ptr (U* p, D del);
template <class D> shared_ptr (nullptr_t p, D del);
使用示例:
template<class T>
struct DeleteArray
{void operator()(T* ptr){delete[] ptr;}
};template<class T>
void DeleteArrayFunc(T* ptr)
{delete[] ptr;
}//[]特化版本
shared_ptr<int[]> sp1(new int[10]);//仿函数作删除器
shared_ptr<Date> sp2(new Date[10], DeleteArray<Date>());//函数指针作删除器
shared_ptr<Date> sp3(new Date[10], DeleteArrayFunc<Date>);//lambda作删除器
auto delArr = [](Date* ptr) {delete[] ptr; };
shared_ptr<Date> sp4(new Date[10], delArr);
增加定制删除器的模拟实现:
template<class T>
class shared_ptr
{
public://explicit防止隐式类型转换explicit shared_ptr(T* ptr = nullptr):_ptr(ptr), _pcount(new int(1)){}template<class D>shared_ptr(T* ptr, D del):_ptr(ptr), _pcount(new int(1)),_del(del){}~shared_ptr(){if (--(*_pcount) == 0){_del(_ptr);delete _pcount;}}shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr), _pcount(sp._pcount), _del(sp._del){++(*_pcount);}shared_ptr<T>& operator=(const shared_ptr<T>& sp){if (_ptr != sp._ptr){if (--(*_pcount) == 0){_del(_ptr);delete _pcount;}_ptr = sp._ptr;_pcount = sp._pcount;_del = sp._del;++(*_pcount);}return *this;}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}private:T* _ptr;int* _pcount;//利用function来包装 _del,默认是不带[] 的deletefunction<void(T*)> _del = [](T* ptr) {delete ptr; };
};
代码细节:
- 写多一个构造函数并套一层模板,当传递删除器的时候,调用此函数。
template<class D>shared_ptr(T* ptr, D del):_ptr(ptr), _pcount(new int(1)),_del(del){}
- 删除器可以是仿函数、函数指针和 lambda 表达式等,我们是没有办法用具体的某个类型去创建 _del 变量,但是C++11中有一个类模板
function
,它是通用的函数包装器,可以包装仿函数、函数指针和 lambda 表达式,而删除器的函数签名都是void(T* ptr)
(返回类型和参数类型)。我们就可以用function
来创建_del
变量,并给上lambda
缺省值[](T* ptr) {delete ptr; }
。
//利用function来包装 _del,默认是不带[] 的deletefunction<void(T*)> _del = [](T* ptr) {delete ptr; };
循环引用 和 weak_ptr
智能指针是用来管理动态分配的内存,以避免内存泄漏的问题。然而,如果使用不当,智能指针也会引入一些新的问题,例如循环引用。
循环引用(Cyclic Reference)
是指两个或多个对象互相持有对方的引用,形成一个环,导致它们无法被释放,即使它们已经不再被其他部分使用。
代码示例
class Node {
public://指向下一个节点的智能指针shared_ptr<Node> next;~Node() { cout << "Node destroyed" << endl; }
};int main() {auto node1 = make_shared<Node>();auto node2 = make_shared<Node>();// 相互引用node1->next = node2;node2->next = node1;// 程序结束时,node1和node2不会被释放return 0;
}
为什么会出现循环引用?
-
shared_ptr
的原理:shared_ptr
通过引用计数来管理对象的生命周期。- 当引用计数变为0时,
shared_ptr
会自动释放内存。
-
循环引用的本质:
- 在上述例子中,
node1
和node2
互相持有对方的shared_ptr
,node1
需要node2的shared_ptr
析构时释放,而node2的shared_ptr
是node2
的成员变量,需要让node2
释放才会析构,node2
需要node1的shared_ptr
析构时释放,node1的shared_ptr
需要让node1
释放才会析构。这样就形成一个环了,两个节点的shared_ptr
的引用计数始终不为0; - 即使它们超出了作用域,也不会被销毁,从而引发内存泄漏。
- 在上述例子中,
如何解决循环引用问题?
可以通过将其中一个 shared_ptr
替换为 weak_ptr
来打破循环引用。
class Node {
public:// 用weak_ptr打破循环引用weak_ptr<Node> next;~Node() { cout << "Node destroyed" << endl; }
};int main() {auto node1 = make_shared<Node>();auto node2 = make_shared<Node>();// weak_ptr不会增加引用计数node1->next = node2;node2->next = node1;// 程序结束时,node1和node2会被正确释放return 0;
}
程序结束时,先析构 node2 ,引用计数减到0,释放 node2 ,而不会像循环引用那般由于 node2 和 node1->next 指向同一个对象,析构 node2 时其引用计数从2减到1,导致引用计数永远不为0导致 node2 无法释放。node2 释放后 node1 也能正常释放了。
weak_ptr
是一种辅助智能指针,它与 shared_ptr
配合使用,用于解决循环引用问题或实现对象的非强拥有关系。
weak_ptr
是一种不参与引用计数的智能指针。- 它不会改变所指向对象的生命周期,仅仅是一个“弱引用”。
- 常用于观察由
shared_ptr
管理的对象,而不会影响其销毁时机。
基本用法:
weak_ptr
必须从shared_ptr
初始化,不能直接管理动态分配的内存。- 通过
weak_ptr
无法直接访问对象,需要调用lock()
方法将其转换为shared_ptr
。
lock()
方法返回一个指向相同对象的shared_ptr
,如果对象已被释放,则返回一个空指针。
与 shared_ptr
的区别
特性 | shared_ptr | weak_ptr |
---|---|---|
是否参与引用计数 | 是 | 否 |
是否影响生命周期 | 是 | 否 |
访问对象方式 | 直接使用 * 或 -> | 需调用 lock() 转换为 shared_ptr |
应用场景 | 强拥有关系,负责对象生命周期 | 弱引用,避免循环引用或临时访问 |
总结
weak_ptr
是一种轻量级的智能指针,用于观察对象,不参与对象生命周期管理。- 在设计需要临时引用或防止循环引用的场景中,
weak_ptr
是一个非常重要的工具。 - 配合
shared_ptr
使用,能够更好地管理复杂对象间的依赖关系。
拜拜,下期再见😏
摸鱼ing😴✨🎞
相关文章:
【C++】指针与智慧的邂逅:C++内存管理的诗意
文章目录 RAII 智能指针auto_ptrunique_ptr shared_ptr模拟实现定制删除器循环引用 和 weak_ptr RAII RAII(Resource Acquisition Is Initialization)是一种广泛应用于 C 等编程语言中的编程范式,它的核心思想是:资源的获取和释放…...
python中的高阶函数
1、什么是高阶函数? 高阶函数是指将函数作为参数传入。就是高阶函数 2、高阶函数有哪些? map 映射函数 >>> print(list(map(lambda x:x*x,range(1,11)))) [1, 4, 9, 16, 25, 36, 49, 64, 81, 100] >>> print(list(map(lambda x:st…...
spark关联hive 报 Filesystem Close 错误
请看如下问题: 假如我想将一个sql语句插入hive表中时,比如 insert into table tmp.app_user_active_range partition (dt2022-11-04) 报如下错误: 我的环境是pyspark,pyspark中操作hive,使用datagrip关联spark,在da…...
MySQL主从同步详解
文章目录 MySQL主从同步概述MySQL主从同步原理MySQL主从同步结构模式MySQL主从同步搭建搭建步骤一主一从实验环境master主机slave1主机验证主从同步 一主多从master主机slave2主机验证主从同步 MySQL主从同步复制模式 读写分离技术MaxScale简介部署MaxScale服务器授权用户maste…...
Python 单元测试基础脚本
单元测试的概念: 单元测试是针对程序中最小可测试单元进行检查和验证的过程。在Python中,通常一个函数或方法就是一个测试单元。 unittest框架: Python自带了一个名为unittest的单元测试框架,它受JUnit启发,为开发者提…...
鸿蒙开发-在ArkTS中实现socket功能
基本概念 在 ArkTS 中实现 Socket 功能主要涉及到网络通信中的套接字(Socket)编程。Socket 是一种用于在不同设备(如客户端和服务器)之间进行双向通信的接口,它允许应用程序发送和接收数据。在网络编程中,有两种主要的 Socket 类型:基于 TCP…...
【设计模式系列】策略模式(二十四)
一、什么是策略模式 策略模式(Strategy Pattern)是软件设计模式中的一种行为型模式。它定义了一系列算法,并将每一个算法封装起来,使它们可以互换使用,算法的变化不会影响使用算法的用户。策略模式让算法的变化独立于…...
D92【python 接口自动化学习】- pytest基础用法
day92 pytest的skip和skipif用法 学习日期:20241208 学习目标:pytest基础用法 -- pytest的skip和skipif用法 学习笔记: 测试用例跳过 skip和skipif用法,测试用例跳过 pytest.mark.skip 跳过标记的用例 pytest.mark.skipif(1 …...
spring中的@Bean和@Component有什么区别?
定义和作用范围 Bean: 是一个方法级别的注解。它主要用于在Java配置类(使用Configuration注解的类)中定义一个Bean。这个方法返回的对象会被Spring容器管理。例如,假设我们有一个配置类AppConfig: import org.sprin…...
docker入门
安装 官方下载 系统:CentOS 7.9 配置docker yum源。 sudo yum install -y yum-utils sudo yum-config-manager \ --add-repo \ http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo启动docker 关机后下次开机又得执行 sudo systemctl start dock…...
HDR视频技术之六:色调映射
图像显示技术的最终目的就是使得显示的图像效果尽量接近人们在自然界中观察到的对应的场景。 HDR 图像与视频有着更高的亮度、更深的位深、更广的色域,因此它无法在常见的普通显示器上显示。 入门级的显示器与播放设备(例如普通人家使用的电视࿰…...
MySQL高可用之MHA
华子目录 MHA概述为什么要用MHA什么是MHAMHA的组成MHA的特点故障切换备选主库的算法 MHA工作原理MHA环境搭建环境准备开始部署MHAMHA软件使用介绍配置MHA的管理环境创建MHA管理的模板文件 测试 模拟故障MySQL-master切换手动切换(在master存活状态下切换)…...
区块链——基本概念、技术原理
一、区块链基本概念 (一)区块链定义 区块链(Blockchain)是指通过去中心化和去信任的方式集体维护一个可靠数据库的技术方案。通俗一点说,区块链技术就指一种全民参与记账的方式,是一种防篡改、共享的、可…...
docker 部署共享文档ZFile
1、拉取ZFile镜像 docker pull crpi-k5k93ldwfc7o75ip.cn-hangzhou.personal.cr.aliyuncs.com/tirling-pdf/zfile:latest 2、创建文件夹和进入文件夹 mkdir zfile && cd zfile 3、创建docker-compose.yml配置文件。 vim docker-compose.yml version: 3.3 service…...
C# 自定义组件实现表格的多层表头功能
在 WinForms 中,想要实现多层表头功能时,DataGridView 本身并不支持该功能,而且又不希望使用第三方控件,因此选择通过自定义组件来实现这一需求。 首先,展示一下程序实现的效果: 接下来,创建一…...
给Squid代理添加HTTP basic认证
HTTP basic认证是一种简单的认证机制,要求用户在请求资源前提供有效的用户名和密码。 实例: 给Squid代理添加HTTP basic认证 要求: 只允许用户名为peter,密码为123的请求通过认证, 其他请求返回407(Proxy认证失败) 步骤 1 使用htpasswd工具,生成用户…...
使用伪装IP地址和MAC地址进行Nmap扫描
使用伪装IP地址和MAC地址进行Nmap扫描 在某些网络设置中,攻击者可以使用伪装的IP地址甚至伪装的MAC地址进行系统扫描。这种扫描方式只有在可以保证捕获响应的情况下才有意义。如果从某个随机的网络尝试使用伪装的IP地址进行扫描,很可能无法接收到任何响…...
Oceanbase离线集群部署
准备工作 两台服务器 服务器的配置参照官网要求来 服务器名配置服务器IPoceanbase116g8h192.168.10.239oceanbase216g8h192.168.10.239 这里选oceanbase1作为 obd机器 oceanbase安装包 选择社区版本的时候自己系统的安装包 ntp时间同步rpm包 联网机器下载所需的软件包 …...
剑指Offer-1 存在重复元素
记录学习过程 题目连接 题目连接 题目描述 给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 ,返回 true ;如果数组中每个元素互不相同,返回 false 。 示例一、 输入:nums [1,2,3,1] 输出:true 解释&…...
react跳转传参的方法
传参 首先下载命令行 npm react-router-dom 然后引入此代码 前面跳转的是页面 后面传的是你需要传的参数接参 引入此方法 useLocation():这是 react-router-dom 提供的一个钩子,用于获取当前路由的位置对象location.state:这是从其他页面传…...
【Java若依框架】RuoYi-Vue的前端和后端配置步骤和启动步骤
🎙告诉你:Java是世界上最美好的语言 💎比较擅长的领域:前端开发 是的,我需要您的: 🧡点赞❤️关注💙收藏💛 是我持续下去的动力! 目录 一. 作者有话说 …...
CSS学习记录04
CSS边框 CSS border 属性指定元素边框的样式、宽度和颜色。border-style 属性指定要显示的边框类型。dotted - 定义点线边框dashed - 定义虚线边框solid - 定义实线边框double - 定义双边框groove - 定义3D坡口边框,效果取决于border-color值ridge - 定义3D脊线边框…...
Kafka怎么发送JAVA对象并在消费者端解析出JAVA对象--示例
1、在pom.xml中加入依赖 <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-stream-kafka</artifactId><version>3.1.6</version></dependency> 2、配置application.yml 加入Kafk…...
vue3【实战】图表【组件封装】Chart ( 原生 ECharts ,支持自适配屏幕缩放,动态响应图表配置修改)
效果预览 技术方案 vue3 ( vite | TS | AutoImport ) Element Plus UnoCSS ECharts 技术要点 ECharts 实例的类型 let myChart: echarts.ECharts | null null默认生成随机 id id: {type: String,default: () > Math.random().toString(36).substring(2, 8)},深度监听图…...
Oracle系统性能监控工具oswatcher演示
1、关于 OSW OSWatcher 的使用符合 Oracle 的标准许可条款,并且不需要额外的许可即可使用!!!! OSWatcher (oswbb) 是一种 UNIX shell 脚本的集合,主要用于收集和归档操作系统和网络的度量,以便…...
Unix、GNU、BSD 风格中 ps 参数的区别
注:本文为“不同风格中 ps 命令参数的区别”相关文章合辑。 未去重。 BSD 风格和 UNIX 风格中 ps 参数的区别 作者:Daniel Stori 译者:LCTT Name1e5s | 2017-06-17 10:53 One Last Question ps aux 以及 ps -elf 都是查看进程的方式&…...
Jenkins环境一站式教程:从安装到配置,打造高效CI/CD流水线环境-Ubuntu 22.04.5 环境离线安装配置 Jenkins 2.479.1
文章目录 Jenkins环境一站式教程:从安装到配置,打造高效CI/CD流水线环境-Ubuntu 22.04.5 环境离线安装配置 Jenkins 2.479.1一、环境准备1.1 机器规划1.2 环境配置1.2.1 设置主机名1.2.2 停止和禁用防火墙1.2.3 更新系统 二、安装配置Jenkins2.1 安装JDK…...
百度文心一言全解析
一、技术基础 模型架构 多层神经网络构建:深度神经网络结构,包含多个隐藏层,有效处理复杂语言信息。注意力机制运用:精准聚焦文本关键部分,理解语义关联与重要性分布。多头注意力并行:多维度分析文本&#…...
在python中使用布尔逻辑
布尔是python中常见类型。它的值只能是两项内容之一:true或false. 编写"if"语句 若要在python中表达条件逻辑,可以使用if语句。——编写If语句离不开逻辑运算符:等于、不等于、小于、大于或等于、大于和大于或等于。 在python中…...
【Web】AlpacaHack Round 7 (Web) 题解
Treasure Hunt flag在md5值拼接flagtxt的文件里,如 d/4/1/d/8/c/d/9/8/f/0/0/b/2/0/4/e/9/8/0/0/9/9/8/e/c/f/8/4/2/7/e/f/l/a/g/t/x/t 访问已经存在的目录状态码是301 访问不存在的目录状态码是404 基于此差异可以写爆破脚本 这段waf可以用url编码绕过 做个lab …...
汽车48V电气系统
汽车48V电气系统 汽车48V电气系统汽车48V电气系统设计汽车48V电气系统测试汽车48V系统是48V供电和12V供电共存的么?48V供电系统是如何与12V供电系统共存的?48V电气系统测试的难点有哪些?在汽车48V电气系统通信测试中,如何向12V的控制器和48V的控制器供电?汽车48V电气系统通…...
完美解决Qt Qml窗口全屏软键盘遮挡不显示
1、前提 说明:我使用的是第三方软键盘 QVirtualKeyboard QVirtualKeyboard: Qt5虚拟键盘支持中英文,仿qt官方的virtualkeyboard模块,但使用QWidget实现。 - Gitee.com 由于参考了几篇文章尝试但没有效果,链接如下: 文章一:可能…...
docker逃逸总结
一、 检查是否在docker容器中 通过以下两个地方来判断 # 是否存在此文件 ls -al /.dockerenv# 在其中是否包含docker字符串 cat /proc/1/cgroup除了上面两种外还有其他方式判断,如检测mount、fdisk -l查看硬盘 、判断PID 1的进程名等也可用来辅助判断。 容器逃逸…...
DSA 和 ECDSA 签名算法
DSA 和 ECDSA 签名算法 基本介绍Java实现DSA创建密钥对签名验签 ECDSA创建密钥对签名验签 Go实现ECDSA创建密钥对签名验签 DSA创建密钥对签名验签 基本介绍 DSA 是一种基于离散对数问题的数字签名算法。它使用私钥和公钥对来进行签名和验证操作。 ECDSA 是基于椭圆曲线密码体制…...
Scrapy的简单实现
Scrapy的简单实现 1. Scrapy是什么 Scrapy是一个用于抓取网站(即网页爬取)和从网页中提取结构化数据的开源框架。它为编写网络爬虫来抓取网站内容提供了高效、灵活的方式,并将这些信息以常见的格式保存,如JSON、CSV或XML。Scrap…...
Python之爬虫入门--示例(2)
一、Requests库安装 可以使用命令提示符指令直接安装requests库使用 pip install requests 二、爬取JSON数据 (1)、点击网络 (2)、刷新网页 (3)、这里有一些数据类型,选择全部 (…...
JS的for in和for of
for...in 语句 工作原理 遍历属性:for...in 遍历对象的所有可枚举属性,这些属性不仅限于对象本身的属性,还包括原型链上的可枚举属性。返回键名:每次迭代时,循环变量会得到当前属性的键(即字符串形式的属…...
Spring IoC的基本概念
引言 在 Java 中,出现了大量轻量级容器,这些容器有助于将来自不同项目的组件组装成一个有凝聚力的应用程序。这些容器的底层是它们如何执行布线的常见模式,它们将这一概念称为“控制反转”。 🏢 本章内容 🏭 IoC服务…...
解决GitHub项目泄露API密钥问题
文章目录 所有的步骤参考gpt步骤一 使用环境变量步骤二 撤销并移除历史中的API密钥(1) 安装: pip install git-filter-repo(2) 清除特定文件 git filter-repo --path PATH_TO_YOUR_FILE --invert-paths出错解决 步骤三 处理移除的 origin 远程步骤四 强制推送到GitHub步骤五 重…...
【BUG】VMware|vmrest正在运行此虚拟机,无法配置或删除快照
VMware版本:VMware 16 文章目录 省流版问题解决方案 详细解释版问题解决方案总结 省流版 问题 只读,因为vmrest正在运行虚拟机。 解决方案 参考:虚拟机设置,只读,因为vmrest正在运行此虚拟机。有谁遇到过这种问题吗&…...
Ruby On Rails 笔记2——表的基本知识
Active Record Basics — Ruby on Rails Guides Active Record Migrations — Ruby on Rails Guides 原文链接自取 1.Active Record是什么? Active Record是MVC模式中M的一部分,是负责展示数据和业务逻辑的一层,可以帮助你创建和使用Ruby…...
VideoConvertor.java ffmpeg.exe
VideoConvertor.java ffmpeg.exe 视频剪切原理 入点 和 出点 选中时间点,导出...
java.lang.IllegalStateException: Error processing condition on org.springframework.boot.autoconfigur
在运行代码的过程中,报出以下错误: java.lang.IllegalStateException: Error processing condition on org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration.redisTemplate 或者是: Caused by: java.lang.IllegalArgu…...
C#设计模式--策略模式(Strategy Pattern)
策略模式是一种行为设计模式,它使你能在运行时改变对象的行为。在策略模式定义了一系列算法或策略,并将每个算法封装在独立的类中,使得它们可以互相替换。通过使用策略模式,可以在运行时根据需要选择不同的算法,而不需…...
Webpack Tree Shaking 技术原理及应用实战,优化代码,精简产物
前言 在前端开发中,优化代码体积和提升应用性能是至关重要的课题。Webpack 提供了多种优化手段来帮助开发者实现这一目标,Tree Shaking 就是其中一种非常重要的优化技术,它通过在编译阶段移除未被使用的代码模块,从而显著减小最终…...
C++(十二)
前言: 本文将进一步讲解C中,条件判断语句以及它是如何运行的以及内部逻辑。 一,if-else,if-else语句。 在if语句中,只能判断两个条件的变量,若想实现判断两个以上条件的变体,就需要使用if-else,if-else语…...
ModelScope-Agent(1): 基于开源大语言模型的可定制Agent系统
目录 简介快速入门 简介 github地址 快速入门 看前两篇,调用千问API和天气API # 选用RolePlay 配置agent from modelscope_agent.agents.role_play import RolePlay # NOQArole_template 你扮演一个天气预报助手,你需要查询相应地区的天气&#x…...
Jmeter进阶篇(30)深入探索 JMeter 监听器
前言 在性能测试领域里,Apache JMeter 是一款经典而强大的工具,而其中的监听器(Listeners)组件更是发挥着不可或缺的关键作用。 监听器就像敏锐的观察者,默默记录测试执行过程中的各种数据,作为系统性能分析的数据依据。 本文将带你全方位走进 JMeter 监听器的奇妙世界,…...
HTTP multipart/form-data 请求
序言 最近在写项目的过程中有一个需求是利用 HTTP 协议传输图片和视频,经过查询方法相应的方法发现使用 multipart/form-data 的方式,这是最常见处理二进制文件的表单编码类型。 学习了一下午,现在总结一下使用的方法和相关的知识点&#x…...
记录关于阿里云智能媒体预览pdf文件的问题
pdf仅支持预览,不支持编辑,需要将权限设置成只读。 readonly参数一定要传,不能不传!!!! readonly的设置一定要用示例提供的方法!!!! 用WebofficeP…...