动态内存管理 智能指针 shared_ptr、unique_ptr、weak_ptr + 定制删除器

动态内存管理常出现的两种问题:

1.忘记释放内存,造成内存泄漏

2.这块内存还有其他指针指向的情况下,就释放了它,会产生引用非法内存的指针,例如

  • 如果类中有属性指向堆区,做赋值操作时会出现浅拷贝的问题

内存泄漏分类:

1.堆内存泄漏(Heap leak)

堆内存指的是程序执行中依据需要分配,

  • (malloc/calloc/realloc) / free     从堆中分配/释放的一块内存  
  • new/delete                                  从堆中分配/释放的一块内存
  • new []/delete[]                            从堆中分配/释放的一块内存

没有配套使用,就产生Heap leak

2.系统资源泄漏

      指程序使用系统分配的资源,比如套接字,文件描述符,管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定

RAII [1](Resource Acquisition Is Initialization),也称为“资源获取就是初始化”,是C++语言的一种管理资源、避免泄漏的惯用法。C++标准保证任何情况下,已构造的对象最终会销毁即它的析构函数最终会被调用简单的说,RAII 的做法是使用一个对象,在其构造时获取资源,在对象生命期控制对资源的访问使之始终保持有效,最后在对象析构的时候释放资源。(百度百科)

这种做法有两大好处:

  1. 不需要显示地释放资源
  2. 对象所有的资源在其生命期内始终保持有效

智能指针的原理:

  1. RAII特性
  2. 重载operator*和opertaor->,具有像指针一样的行为
#include <iostream>
using namespace std;
/*动态内存管理经常会出现两种问题:1.忘记释放内存,造成内存泄漏2.尚有指针引用内存的情况下就释放了它,就会产生引用非法内存的指针智能指针可以更加容易(更加安全)的使用动态内存.它的行为类似常规指针,重要的区别是它自动释放所指向的对象内存泄漏:因为疏忽或错误造成程序未能释放已经不再使用的内存的情况.内存泄漏是应用程序分配某段内存后,因为设计错误,失去了对该段内存的控制.不仅自己无法使用这段内存,而且也无法再将这段内存分配给其他程序,因而造成了内存的浪费内存泄漏危害:长期运行的程序出现内存泄漏,影响很不好.如操作系统/后台服务等出现内存泄漏会导致响应越来越慢,最终卡死,宕机内存泄漏分类:1.堆内存泄漏(Heap leak)堆内存指的是程序执行中依据需要分配,(malloc/calloc/realloc) / (free)  从堆中分配/释放的一块内存  (new)/(delete)                    从堆中分配/释放的一块内存 假设程序设计错误导致这块内存没有释放掉, 这部分空间再也无法被使用,也就产生Heap leak2.系统资源泄漏指程序使用系统分配的资源,比方套接字,文件描述符,管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存,文件句柄,网络连接,互斥量等等)的简单技术在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源.借此,我们实际上把管理一份资源的责任托管给了一个对象这种做法有两大好处:1.不需要显示地释放资源2.采用这种方式,对象所有的资源在其生命期内始终保持有效3.智能指针的原理*/
#include <iostream>
using namespace std;template<class T>
class SmartPtr {
public:SmartPtr(T* ptr) :m_ptr(ptr) {}~SmartPtr() {cout<<"delete->"<<m_ptr<<endl;// 释放资源delete m_ptr;}//指针可以通过->去访问所指空间中的内容(->重载)T* operator->() {return m_ptr;}// 指针可以解引用(*重载)T& operator*() {return *m_ptr;}
private:T* m_ptr;
};struct Person {string m_name;int m_age;
};int main() {// p在出作用域的前一刻会自动调用析构函数来释放资源// SmartPtr<int> p(new int); // delete->0x224d5a3d1c0// 对同一个资源进行了多次析构造成的SmartPtr<Person> person(new Person);person->m_name = "Tom";person->m_age = 18;cout<<"name: " <<(*person).m_name<<" age: "<<person->m_age<<endl;// 默认的拷贝构造函数就属于浅拷贝SmartPtr<Person> p1(person);// SmartPtr<Person> p1 = person;cout<<"name: " <<p1->m_name<<" age: "<<p1->m_age<<endl;return 0;
}

这里的SmartPtr<Person> p1(person);用到了默认拷贝构造函数

SmartPtr<Person> p1= person;用到了默认operator= 

大致实现逻辑如下:

SmartPtr(const SmartPtr<T>& sp) :m_ptr(sp.m_ptr) {}
SmartPtr<T>& operator=(const SmartPtr<T>& sp) {if (this != &sp) {m_ptr = sp.m_ptr;}return *this;
}
  • 故对同一个资源进行了多次析构会造成异常中断

一、auto_ptr

(1) 智能指针auto_ptr

  • auto_ptr_test.cpp
#include <iostream>
#include <string>
#include <memory>
using namespace std;
// C++98版本的库中就提供了auto_ptr的智能指针
class Person {
public:Person(string name,int age):m_name(name),m_age(age){}~Person(){cout<<"~Person()"<<endl;}void setName(string name) {m_name = name;}void setAge(int age) {m_age = age;}void print() const{cout<<"name: "<<m_name<<", age: "<<m_age<<endl;}
private:string m_name;int m_age;
};int main() {std::auto_ptr<Person> ptr(new Person("Tom",12));ptr->print();// auto_ptr的问题:当对象拷贝或者赋值后,前面的对象就悬空了std::auto_ptr<Person> copyPtr(ptr);copyPtr->print();copyPtr->setName("Jerry");copyPtr->setAge(8);copyPtr->print();// 这里ptr就悬空了// ptr->setName("heheda");return 0;
}
PS D:\Work\c++\bin> ."D:/Work/c++/bin/app.exe"
name: Tom, age: 12
name: Tom, age: 12
name: Jerry, age: 8
~Person()
PS D:\Work\c++\bin>

 auto_ptr的问题:当对象拷贝或者赋值后,前面的对象就悬空了

(2) auto_ptr的实现原理:管理权转移的思想

  • AutoPtr_main.cpp 
#include <iostream>
using namespace std;class Person {
public:Person(){ cout << "Person()" << endl; }~Person(){ cout << "~Person()" << endl; }string m_name;int m_age;
};// 管理权转移的思想
template<class T>
class AutoPtr {
public:AutoPtr(T* ptr = nullptr) : m_ptr(ptr) {}~AutoPtr() { if(m_ptr) delete m_ptr; }// 一旦发生拷贝,就把ap中资源转移到当前对象中,然后// 让ap与其所管理资源断开联系,这样就解决了一块空// 间被多个对象使用而造成程序崩溃问题AutoPtr(AutoPtr<T>& ap) : m_ptr(ap.m_ptr) { ap.m_ptr = nullptr; }AutoPtr<T>& operator=(AutoPtr<T>& ap) {// 检测是否为自己给自己赋值if (this != &ap) {// 释放当前对象中资源if(m_ptr) delete m_ptr;// 转移ap中资源到当前对象中m_ptr = ap.m_ptr;ap.m_ptr = nullptr;}return *this;}/*重载operator*和opertaor->,具有像指针一样的行为*/T* operator->() const { return m_ptr; }T& operator*() const { return *m_ptr; }
private:T* m_ptr;
};int main() {AutoPtr<Person> ap(new Person);// 现在再从实现原理层来分析会发现,这里拷贝后把ap对象的指针赋空了,// 导致ap对象悬空,再通过ap对象访问资源时就会出现问题AutoPtr<Person> copy(ap);// 这里访问资源时,就会出问题// ap->m_name = "Tom";return 0;
}
  • 用auto_ptr的原理来修改前面的Smart_Ptr

修改拷贝构造函数和operator=函数

    SmartPtr(SmartPtr<T>& sp) :m_ptr(sp.m_ptr) {sp.m_ptr = nullptr;}SmartPtr<T>& operator=(SmartPtr<T>& sp) {// 检测是否为自己给自己赋值if (this != &sp) {// 释放当前对象中资源if (m_ptr) delete m_ptr;// 转移sp中资源到当前对象中m_ptr = sp.m_ptr;sp.m_ptr = nullptr;}return *this;}
  • SmartPtr.cpp 
#include <iostream>
using namespace std;template<class T>
class SmartPtr {
public:SmartPtr(T* ptr) :m_ptr(ptr) {}~SmartPtr() {cout << "delete->" << m_ptr << endl;// 释放资源if(m_ptr) delete m_ptr;}SmartPtr(SmartPtr<T>& sp) :m_ptr(sp.m_ptr) {sp.m_ptr = nullptr;}SmartPtr<T>& operator=(SmartPtr<T>& sp) {// 检测是否为自己给自己赋值if (this != &sp) {// 释放当前对象中资源if (m_ptr) delete m_ptr;// 转移sp中资源到当前对象中m_ptr = sp.m_ptr;sp.m_ptr = nullptr;}return *this;}//指针可以通过->去访问所指空间中的内容(->重载)T* operator->() {return m_ptr;}// 指针可以解引用(*重载)T& operator*() {return *m_ptr;}
private:T* m_ptr;
};struct Person {string m_name;int m_age;
};int main() {SmartPtr<Person> person(new Person);person->m_name = "Tom";person->m_age = 18;cout << "name: " << (*person).m_name << " age: " << person->m_age << endl;// 默认的拷贝构造函数就属于浅拷贝SmartPtr<Person> p1 = person;cout << "name: " << p1->m_name << " age: " << p1->m_age << endl;// 导致person对象悬空,再通过person对象访问资源时就会出现问题//cout << "name: " << (*person).m_name << " age: " << person->m_age << endl; // 异常中断return 0;
}

二、unique_ptr

(1) 智能指针unique_ptr

  • unique_ptr_test.cpp
// C++11中开始提供更靠谱的unique_ptr
#include <iostream>
#include <string>
#include <memory>
using namespace std;
class Person
{
public:Person(string name, int age) :m_name(name), m_age(age) { cout << "name: " << name << ", age: " << age << endl; }~Person() { cout << "~Person()" << endl; }string m_name;int m_age;
};
int main() {std::unique_ptr<Person> person(new Person("heheda", 12));// unique_ptr的设计思路非常的粗暴-防拷贝,也就是不让拷贝和赋值//unique_ptr<Person> copyPerson(person); // error// 下面的可以不看unique_ptr<Person> person2 = std::move(person);cout << "name: " << person2->m_name << ", age:" << person2->m_age << endl;//cout << "name: " << person->m_name << ", age:" << person->m_age << endl; // 异常return 0;
}

(2) unique_ptr的实现原理:简单粗暴的防拷贝,就是将拷贝构造函数和operator=函数设置为delete

  • UniquePtr_main.cpp 
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:Person(string name,int age):m_name(name),m_age(age){ cout<<"name: "<<name<<", age: "<<age<<endl;}~Person(){ cout << "~Person()" << endl; }string m_name;int m_age;
};
// unique_ptr的实现原理:简单粗暴的防拷贝
template<class T>
class UniquePtr {
public:UniquePtr(T* ptr = nullptr) : m_ptr(ptr) {}~UniquePtr() { if(m_ptr) delete m_ptr; }T& operator*() const { return *m_ptr; }T* operator->() const { return m_ptr; }// C++11防拷贝的方式:deleteUniquePtr(UniquePtr<T> const&) = delete;UniquePtr<T>& operator=(UniquePtr<T> const&) = delete;
private:T* m_ptr;
};int main() {UniquePtr<Person> person(new Person("heheda",12));// unique_ptr的设计思路非常的粗暴-防拷贝,也就是不让拷贝和赋值// UniquePtr<Person> copyPerson(person); // errorreturn 0;
}

三、shared_ptr

(1) 智能指针 shared_ptr

shared_ptr是一种智能指针(smart pointer),作用有如同指针,但会记录有多少个shared_ptrs共同指向一个对象。这便是所谓的引用计数(reference counting)。

一旦最后一个这样的指针被销毁,也就是一旦某个对象的引用计数变为0,这个对象会被自动删除。这在非环形数据结构中防止资源泄露很有帮助。(百度百科)

  • shared_ptr_test.cpp
// C++11中开始提供更靠谱的并且支持拷贝的shared_ptr
#include <iostream>
#include <string>
#include <memory>
using namespace std;
class Person
{
public:Person(string name, int age) :m_name(name), m_age(age) { cout << "name: " << name << ", age: " << age << endl; }~Person() { cout << "~Person()" << endl; }string m_name;int m_age;
};
int main() {// shared_ptr 通过引用计数支持智能指针对象的拷贝shared_ptr<Person> sp(new Person("heheda", 12));shared_ptr<Person> copySp = sp;cout << "ref count:" << sp.use_count() << endl;cout << "ref count:" << copySp.use_count() << endl;return 0;
}

 (2) shared_ptr的实现原理:通过引用计数的方式来实现多个shared_ptr对象之间共享资源

​ 1.类似RAII的思路(普通构造创建资源、析构释放资源)

​ 2.像指针一样使用(operator 、 operator->等运算符的重载

 3.能够拷贝(且必须是浅拷贝) (自己编写拷贝构造、赋值重载的函数)

 4.计数器Count(计数器必须是同一份资源的计数器,而且是多个对象所共享的

  • 不能是普通int型变量,不然每个对象都会有不同的一个count
  • 也不能是static型变量,否则所有资源都是共享的同一份计数器
  • 只能用int* 类型的计数器

 5.SharedPtr本身是线程安全的,所以在实现的过程中也必须上锁

#include <iostream>
#include <string>
#include <mutex>
using namespace std;class Person
{
public:Person(string name,int age):m_name(name),m_age(age){ cout<<"name: "<<name<<", age: "<<age<<endl;}~Person(){ cout << "~Person()" << endl; }string getName() const { return m_name; }int getAge() const { return m_age; }
private:string m_name;int m_age;
};template <class T>
class SharePtr
{
public:SharePtr(T *ptr = nullptr): m_ptr(ptr), m_pRefCount(new int(1)), m_mutex(new mutex){}// 拷贝构造函数SharePtr(const SharePtr<T> &sp): m_ptr(sp.m_ptr), m_pRefCount(sp.m_pRefCount), m_mutex(sp.m_mutex){// 增加引用计数addRefCount();}// operator=SharePtr<T>& operator=(const SharePtr<T> &sp) {// if(this != &sp)// 这样写防止 p2(p1) p1=p2 防止"自己赋值自己"if(m_ptr != sp.m_ptr) {// 先释放之前管理的资源release();m_ptr = sp.m_ptr;m_pRefCount = sp.m_pRefCount;m_mutex = sp.m_mutex;// 增加引用计数addRefCount();}return *this;}T* get() { return m_ptr; }int useCount() { return *m_pRefCount; }// operator 、 operator->等运算符的重载T* operator->(){ return m_ptr; }T& operator*(){ return *m_ptr; }~SharePtr() { release(); }
private:void release() {bool deleteflag = false;m_mutex->lock();if(--(*m_pRefCount) == 0) {cout << "delete:" << m_ptr << endl;// 释放资源delete m_ptr;delete m_pRefCount;deleteflag = true;}m_mutex->unlock();//表示m_ptr 和 m_pRefCount资源已经被释放,此时需要释放锁,否则产生资源泄漏if(deleteflag) delete m_mutex;}void addRefCount() {m_mutex->lock();++(*m_pRefCount);m_mutex->unlock();}
private:T *m_ptr;         // 指向管理资源的指针int *m_pRefCount; // 指向引用计数的指针mutex *m_mutex;   // 互斥锁,处理线程安全
};int main() {// shared_ptr 通过引用计数支持智能指针对象的拷贝SharePtr<Person> sp(new Person("heheda",12));SharePtr<Person> copySp = sp;cout << "ref count:" << sp.useCount() << endl;cout << "ref count:" << copySp.useCount() << endl;cout<<"name:"<<sp->getName()<<" age:"<<sp->getAge()<<endl;cout<<"name:"<<copySp->getName()<<" age:"<<copySp->getAge()<<endl;return 0;
}
PS D:\Work\c++\bin> ."D:/Work/c++/bin/app.exe"
name: heheda, age: 12
ref count:2
ref count:2
name:heheda age:12
name:heheda age:12
delete:0x21ef825f6a0
~Person()
PS D:\Work\c++\bin>

注意:智能指针是线程安全的,因为智能指针中引用计数++,--加锁了。但是智能指针管理的资源不是线程安全的,需要自己手动控制

(3)循环引用

// std::shared_ptr的循环引用
#include <iostream>
#include <string>
#include <memory>
using namespace std;struct ListNode {int m_data;shared_ptr<ListNode> m_prev;shared_ptr<ListNode> m_next;~ListNode() { cout<< "~ListNode()" << endl; }
};int main() {shared_ptr<ListNode> node1(new ListNode);shared_ptr<ListNode> node2(new ListNode);cout<<node1.use_count() << endl; cout<<node2.use_count() << endl;node1->m_next = node2;node2->m_prev = node1;cout<<node1.use_count() << endl;cout<<node2.use_count() << endl;// 循环引用:node1和node2在生命周期结束后并没有去调用析构函数return 0;
}
  • 循环引用:node1和node2在生命周期结束后并没有去调用析构函数,原因就是引用计数不为0,故导致资源无法释放
PS D:\Work\c++\bin> ."D:/Work/c++/bin/app.exe"
1
1
2
2
PS D:\Work\c++\bin>

 

 四、weak_ptr

(1)weak_ptr_test.cpp

解决方案:在引用计数的场景下,把节点中的m_prev和m_next改成weak_ptr就可以了,因为weak_ptr类型的不会自增引用计数

#include <iostream>
#include <string>
#include <memory>
using namespace std;// 解决方案:在引用计数的场景下,把节点中的m_prev和m_next改成weak_ptr就可以了
struct ListNode {int m_data;weak_ptr<ListNode> m_prev;weak_ptr<ListNode> m_next;~ListNode() { cout<<"~ListNode()"<<endl;}
};int main() {shared_ptr<ListNode> node1(new ListNode);shared_ptr<ListNode> node2(new ListNode);cout<<node1.use_count()<<endl; cout<<node2.use_count()<<endl;node1->m_next = node2;node2->m_prev = node1;cout<<node1.use_count()<<endl; cout<<node2.use_count()<<endl;return 0;
}
PS D:\Work\c++\bin> ."D:/Work/c++/bin/app.exe"
1
1
1
1
~ListNode()
~ListNode()
PS D:\Work\c++\bin>

原因就是weak_ptr类型的不会使得引用计数自增 

 (2)weak_ptr的实现原理,不让shared_ptr的引用计数增加就可以了

#include <iostream>
#include <string>
#include <memory>
using namespace std;template<class T>
class WeakPtr {
public:WeakPtr() : m_ptr(nullptr){}// 拷贝构造函数WeakPtr(const shared_ptr<T>& sp) : m_ptr(sp.get()){}// operator=WeakPtr& operator=(const shared_ptr<T>& sp) {m_ptr = sp.get();return *this;}T& operator*() { return *m_ptr; }// 重载*T* operator->() { return m_ptr; }// 重载->
private:T* m_ptr;
};struct ListNode {int m_data;WeakPtr<ListNode> m_prev;WeakPtr<ListNode> m_next;~ListNode() { cout<<"~ListNode()"<<endl;}
};// 原理:node1->m_next = node2; 和node2->m_prev = node1;
//     时weak_ptr的m_next 和 m_prev不会增加node1和node2的
//     引用计数
int main() {shared_ptr<ListNode> node1(new ListNode);shared_ptr<ListNode> node2(new ListNode);cout<<node1.use_count()<<endl; cout<<node2.use_count()<<endl;node1->m_next = node2;node2->m_prev = node1;cout<<node1.use_count()<<endl; cout<<node2.use_count()<<endl;return 0;
}
PS D:\Work\c++\bin> ."D:/Work/c++/bin/app.exe"
1
1
1
1
~ListNode()
~ListNode()
PS D:\Work\c++\bin>

 五、定制删除器

  • delete只会调用一次对象的析构函数,而delete[]会调用后续所有对象的析构函数

// 定制删除器
#include <iostream>
#include <memory>
using namespace std;
class Object {
public:~Object() { cout << "~Object()" << endl; }
private:int m_data = 0;
};void test01() {unique_ptr<Object> p1(new Object);
}/*** 默认情况下:智能指针底层都是delete进行释放资源,* 这里new了10个Object,但底层delete时是delete p2,* 而不是delete[] p2* 
*/
void test02() {unique_ptr<Object> p2(new Object[10]); // error
}// 定制删除器
template<class T>
struct DeleteArray{void operator()(T* p) const {cout << "delete[]" << endl;delete[] p;}
}; 
// 如果资源是new[],malloc,fopen出来的,需要用到定制删除器
void test03() {unique_ptr<Object, DeleteArray<Object>> p2(new Object[10]);  
}int main() {test03();return 0;
}
PS D:\Work\c++\bin> ."D:/Work/c++/bin/app.exe"
delete[]
~Object()
~Object()
~Object()
~Object()
~Object()
~Object()
~Object()
~Object()
~Object()
~Object()
PS D:\Work\c++\bin>

 参考文章:

C++智能指针之shared_Ptr的原理以及简单实现_shared ptr-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/Arthur__Cui/article/details/131969612

C++中的浅拷贝、深拷贝、智能指针 - joannae - 博客园 (cnblogs.com)icon-default.png?t=N7T8https://www.cnblogs.com/qionglouyuyu/p/4620714.html C++智能指针_智能指针是线程安全的吗-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_56044032/article/details/125583967

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/665857.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

在jetbrains IDEA/Pycharm/Android Studio中安装官方rust插件,开始rust编程

在idea插件市场搜索rust&#xff1a;JetBrains Marketplace &#xff0c;就可以找到rust插件&#xff1a; jetbrains官方rust插件地址&#xff1a;[Deprecated] Rust - IntelliJ IDEs Plugin | Marketplace 直接在idea中搜索rust好像是搜不到的&#xff1a; 需要在这个插件市场…

【数据结构】二叉树链式结构的实现

简单不先于复杂&#xff0c;而是在复杂之后。 文章目录 1. 二叉树链式结构的实现1.1 前置说明1.2 二叉树的遍历1.2.1 前序、中序以及后序遍历1.2.2 层序遍历 1.3 节点个数以及高度等1.4 二叉树基础oj练习1.5 二叉树的创建和销毁 1. 二叉树链式结构的实现 1.1 前置说明 在学习二…

Cambalache in Ubuntu

文章目录 前言apt install flatpak这很ok后记 前言 gtkmm4相比gtkmm3有很多改革, 代码也干净了许多, 但在windows上开发 有ui设计器那自然方便很多, 但glade又不支持gtkmm4, windows上装Cambalache很是困难. 各种问题都找不到答案.于是 我用VMware虚拟机Ubuntu20.xx安装Cambal…

macOS虚拟机安装全过程的详细教程

macOS虚拟机安装全过程的详细教程 一、安装虚拟机软件 选择软件&#xff1a;首先&#xff0c;你需要选择一个适合macOS的虚拟机软件。在本教程中&#xff0c;我们以VirtualBox为例。下载与安装&#xff1a;访问VirtualBox的官网&#xff0c;下载适用于macOS的安装包。运行安装…

【leetcode热题100】颜色分类

难度&#xff1a; 中等通过率&#xff1a; 40.7%题目链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 题目描述 给定一个包含红色、白色和蓝色&#xff0c;一共 n 个元素的数组&#xff0c;原地对它们进行排序&#xff0c;使得相同颜…

高校建设AI算力平台方案探索

近年来&#xff0c;人工智能行业发展迅速&#xff0c;在自动驾驶、金融、医疗、教育等行业广泛应用。尤其是ChatGPT发布以后更是掀起了生成式AI的热潮&#xff0c;国内各大互联网厂商也相继发布自己的AI大模型。这也造成了大量的AI人才缺口&#xff0c;同时促进了高校的AI专业建…

CSP-202305-2-矩阵运算

CSP-202305-2-矩阵运算&#xff1a;题目链接 知识点一&#xff1a;申请矩阵 1.动态分配 // 申请 int** dynamicArray new int*[rows]; for (int i 0; i < rows; i) {dynamicArray[i] new int[cols]; }// 释放 for (int i 0; i < rows; i) {delete[] dynamicArray[…

有哪些流行的中文开源语言模型?

支持中文的流行开源语言模型有很多&#xff0c;这些模型在自然语言处理领域的中文任务上表现出色&#xff0c;包括文本分类、情感分析、机器翻译、问答系统等。以下是一些支持中文的流行开源语言模型&#xff1a; 1. **BERT-Base, Chinese**&#xff1a;Google发布的BERT模型的…

【Linux】【Shell】常用压缩和解压缩命令(超详细)

目录 1. 指令&#xff1a; 1.1 tar 1.2 gz、.tar.gz 1.3 .bz2、.tar.bz2、.bz 1.4 .z、.tar.z 1.5 .zip 1.6 .rar 1.7 lzop 2. 示例&#xff1a; 1. 指令&#xff1a; 快速压缩&#xff1a;XZ_DEFAULTS"-T0" tar cJvf xxxxx.tar.xz sourcefile&#xff08;压…

解决浏览器端 globalThis is not defined 报错

解决浏览器端 globalThis is not defined 报错 前言解决办法&#xff1a; 前言 在使用低版本火狐浏览器出现报错globalThis is not defined 解决办法&#xff1a; 在vue的index.html 中添加 this.globalThis || (this.globalThis this) <head><script>this.g…

Power BI无法加载模型无法加载与此报表关联的模型架构。请确保已连接服务器,然后重试。这是可能遇到什么问题了

在Power BI中遇到“无法加载与此报表关联的模型架构。请确保已连接服务器&#xff0c;然后重试。”这样的错误信息&#xff0c;通常意味着模型的数据源有问题&#xff0c;或者模型与报表之间的连接存在问题。以下是一些可能的解决方案&#xff1a; 检查数据源连接&#xff1a; …

Lambda表达式(匿名函数)

C11中引入了lambda表达式&#xff0c;定义匿名的内联函数。 我们可以直接原地定义函数而不用再跑到外面去定义函数跳来跳去。 同时在stl的排序上也有作用。 [capture] (parameters) mutable ->return-type {statement}下面逐一介绍各个参数的含义. [capture] : 捕获&#…

Java语法学习线程基础

Java语法学习线程基础 大纲 概念创建线程线程终止常用方法用户线程和守护线程线程的七大状态线程的同步互斥锁线程死锁释放锁 具体案例 1.概念 2. 创建线程 第一种&#xff1a; class Cat extends Thread {int time 0;Overridepublic void run() {while (true) {System.o…

重写Sylar基于协程的服务器(5、IO协程调度模块的设计)

重写Sylar基于协程的服务器&#xff08;5、IO协程调度模块的设计&#xff09; 重写Sylar基于协程的服务器系列&#xff1a; 重写Sylar基于协程的服务器&#xff08;0、搭建开发环境以及项目框架 || 下载编译简化版Sylar&#xff09; 重写Sylar基于协程的服务器&#xff08;1、…

C++设计模式-单一职责原则

单一职责原则定义了类的封装规范。&#xff08;封装、继承、多态&#xff09; 定义&#xff1a;有且仅有一个引起类变化的原因。 e.g.1.一个需要重新封装的类 class A { public:void funName() {}void funSex() {}void funAge() {}void funCourse() {}//要重新封装到其他类v…

红日靶场1搭建渗透

环境搭建 下载好镜像文件并解压&#xff0c;启动vmware 这里我用自己的win7 sp1虚拟机作为攻击机&#xff0c;设置为双网卡NAT&#xff0c;vm2 其中用ipconfig查看攻击机ip地址 设置win7 x64为双网卡&#xff0c;vm1&#xff0c;vm2 设置win08单网卡vm1&#xff0c;win2k3为单…

关于python依赖包的问题(番外)

前言: 经常一个项目写完,需要导出他所有的依赖包到另外一个项目里面,那么怎么进行操作呢? 正文: 到处依赖包文件: 在一个Python项目中使用pip freeze > requirements.txt命令是一种常用方式&#xff0c;用以生成一个包含所有已安装Python依赖包及其对应版本的列表文件。…

jupyter notebook更改工作目录的2个细节

详细步骤参考知乎原文&#xff1a; 如何更改Jupyter Notebook的默认工作路径&#xff1f; - 知乎 (zhihu.com​​​​​​) 步骤4中需要删除 #符号和后面的空格&#xff01;一定要删除空格&#xff0c;否则会出现语法错误的报错 步骤5中&#xff0c;经过评论区提醒后&#xf…

蓝桥杯备战——12.PCF8591芯片的使用

目录 1.芯片简介2.读写时序3.控制字4.代码封装库5.原理图分析6.使用示例 1.芯片简介 截取自NXP的PCF8591芯片数据手册&#xff0c;我把重点关注部分划出来了&#xff0c;请务必自行阅读一遍数据手册&#xff01; 2.读写时序 ①器件地址&#xff1a; Bit0决定是读还是写操作&…

Python 数据分析(PYDA)第三版(七)

原文&#xff1a;wesmckinney.com/book/ 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 附录 附录 A&#xff1a;高级 NumPy 原文&#xff1a;wesmckinney.com/book/advanced-numpy 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 此开放访问网络版本的《Pyt…