C++智能指针[下](shared_ptr/weak_ptr/循环引用/删除器)

文章目录

  • 4.智能指针[shared_ptr]
    • 4.1设计理念
      • 成员属性
    • 4.2主要接口
      • 拷贝构造
    • 4.3引用计数线程安全问题
      • 测试线程安全
        • 通过对计数引用的加锁保护使得类线程安全
        • 类实例化的对象使用时需要手动加锁保护
      • "锁"的引进
      • 线程引用传参问题
    • 4.4整体代码
  • 5.循环引用问题
    • 5.1问题的引入
    • 5.2分析造成此问题的原因
    • 5.3weak_ptr的主要代码
  • 6.数组对象的删除问题
    • 6.1代码问题
    • 6.2std::shared_ptr面对此问题的解决方案
      • 1.首先看std::shared_ptr::~shared_ptr
      • 2.删除器的传参及使用
      • 3.添加封装删除器
  • 7.总结
    • 7.1完整代码
    • 7.2C++11和Boost智能指针的关系

4.智能指针[shared_ptr]

4.1设计理念

成员属性

在这里插入图片描述
在这里插入图片描述

每一个对象除了有一个主指针外 还有一个副指针用来计数 为什么不设置成int而设置成int*?

同一块空间被两个指针指向 当编译器得到需要销毁指针的指令时 会先判断这是不是最后一个指针 即count==1 时才释放空间 其余情况均只是的计数-- 而不释放空间 这是我们的最初目的 如果设置成int意味着每一个对象有各自的count 当ptr1拷贝给ptr2 我们想要的是 有一块空间单独来计数 如果执行拷贝则计数++ 即我们需要一块共有的空间来实现

不是要实现共有吗 为什么不直接用静态变量?

静态变量可以实现共有没错 但他是当前类的所有对象共有 举例说明: 代码的本意是ptr1 ptr2 ptr3指向同一块空间 此时count == 3 ptr4指向另一块空间时 代码的本意是count = 1 但是同时改变了ptr1/2/3

4.2主要接口

拷贝构造

在这里插入图片描述

	//赋值重载//1."自己"给"自己"赋值//不仅仅是p1 = p1 还要考虑p2 = p1 但之前p2就 = p1//2.左 = 右 赋值后 // 左指针指向空间的指针少了一个 左指针的count-- 进一步考虑count--后==0的情况// 右指针指向空间的指针多了一个 右指针的count++shared_ptr<T>& operator=(const shared_ptr<T>& sp){//"自己"给"自己"赋值: "自己"的本质if (_ptr != sp._ptr){//count--Release();_ptr = sp._ptr;_pcount = sp._pcount;_pmtx = sp._pmtx;//count++AddCount();}return *this;}

4.3引用计数线程安全问题

测试线程安全

通过对计数引用的加锁保护使得类线程安全
	struct Date{int _year = 0;int _month = 0;int _day = 0;};void SharePtrFunc(ape::shared_ptr<Date>& sp, size_t n, mutex& mtx){cout << "   SharePtrFunc: sp.GetPtr() == " << sp.GetPtr() << endl;for (size_t i = 0; i < n; ++i){ape::shared_ptr<Date> copy(sp);}}void test_shared_safe(){ape::shared_ptr<Date> p(new Date);cout << "test_shared_safe: p.GetPtr() == " << p.GetPtr() << endl;const size_t n = 10000;mutex mtx;//线程引用传参即便mtx已经是引用 此处仍然需要使用库函数ref();thread t1(SharePtrFunc, ref(p), n, ref(mtx));thread t2(SharePtrFunc, ref(p), n, ref(mtx));t1.join();t2.join();cout << "p.GetCount(): == " << p.GetCount() << endl;}

在这里插入图片描述

类实例化的对象使用时需要手动加锁保护

在这里插入图片描述
在这里插入图片描述

"锁"的引进

在这里插入图片描述

线程引用传参问题

在这里插入图片描述

简单理解为 p在传给sp前是需要先调用线程的构造函数的 期间发生了某种动作 使得失去引用属性

4.4整体代码

template<class T>
class shared_ptr
{
public://构造函数shared_ptr(T* ptr):_ptr(ptr), _pcount(new int(1)), _pmtx(new mutex){}void Release(){//上锁_pmtx->lock();//不可释放锁bool deleteFlag = false;if (--(*_pcount) == 0){cout << "delete:" << _ptr << endl;delete _ptr;delete _pcount;//可释放锁deleteFlag = true;}//解锁_pmtx->unlock();//判断并释放锁if (deleteFlag){delete _pmtx;}}//void Release()//{//	_pmtx->lock();//	bool deleteFlag = false;//	if (--(*_pcount) == 0)//	{//		if (_ptr)//		{//			//cout << "delete:" << _ptr << endl;//			//delete _ptr;////			// 删除器进行删除//			_del(_ptr);//		}////		delete _pcount;//		deleteFlag = true;//	}////	_pmtx->unlock();////	if (deleteFlag)//	{//		delete _pmtx;//	}//}void AddCount(){_pmtx->lock();++(*_pcount);_pmtx->unlock();}//拷贝构造shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr), _pcount(sp._pcount), _pmtx(sp._pmtx){AddCount();}//赋值重载//1."自己"给"自己"赋值//不仅仅是p1 = p1 还要考虑p2 = p1 但之前p2就 = p1//2.左 = 右 赋值后 // 左指针指向空间的指针少了一个 左指针的count-- 进一步考虑count--后==0的情况// 右指针指向空间的指针多了一个 右指针的count++shared_ptr<T>& operator=(const shared_ptr<T>& sp){//"自己"给"自己"赋值: "自己"的本质if (_ptr != sp._ptr){//count--Release();_ptr = sp._ptr;_pcount = sp._pcount;_pmtx = sp._pmtx;//count++AddCount();}return *this;}//析构函数~shared_ptr(){Release();}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* GetPtr(){return _ptr;}int GetCount(){return *_pcount;}private:T* _ptr;int* _pcount;mutex* _pmtx;
};void test_shared()
{shared_ptr<int> sp1(new int(1));shared_ptr<int> sp2(sp1);shared_ptr<int> sp3(sp2);shared_ptr<int> sp4(new int(10));sp1 = sp4;sp4 = sp1;sp1 = sp1;sp1 = sp2;
}//   线程安全问题   ///
struct Date
{int _year = 0;int _month = 0;int _day = 0;
};void SharePtrFunc(ape::shared_ptr<Date>& sp, size_t n, mutex& mtx)
{cout << "   SharePtrFunc: sp.GetPtr() == " << sp.GetPtr() << endl;cout << "   SharePtrFunc: &sp == " << &sp << endl;for (size_t i = 0; i < n; ++i){ape::shared_ptr<Date> copy(sp);mtx.lock();sp->_year++;sp->_day++;sp->_month++;mtx.unlock();}
}void test_shared_safe()
{ape::shared_ptr<Date> p(new Date);cout << "test_shared_safe: p.GetPtr() == " << p.GetPtr() << endl;cout << "test_shared_safe: &p == " << &p << endl;const size_t n = 100000;mutex mtx;//线程引用传参即便p和mtx已经是引用 此处仍然需要使用库函数ref();thread t1(SharePtrFunc, ref(p), n, ref(mtx));thread t2(SharePtrFunc, ref(p), n, ref(mtx));t1.join();t2.join();cout << "p.GetCount(): == " << p.GetCount() << endl;cout << "p->_year  == " << p->_year << endl;cout << "p->_month == " << p->_month << endl;cout << "p->_month == " << p->_month << endl;}

5.循环引用问题

5.1问题的引入

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

5.2分析造成此问题的原因

在这里插入图片描述

为了解决此问题 需要引进weaked_ptr 我们需要了解的是
智能指针shared_ptr满足

  1. 符合RAII思想
  2. 可以像指针一样使用
  3. 支持拷贝

智能指针weaked_ptr满足

  1. 不符合RAII思想
  2. 可以像指针一样使用
  3. 辅助解决shared_ptr的循环引用问题
  4. weaked_ptr可以指向资源,但是不参与管理,不增加引用计数

实际上库里的智能指针远比我们上述讲到的复杂得多 为了便于学习和理解 我们只学习核心框架 需要了解的是 库里所支持的这两个函数在这里插入图片描述
weaked_ptr有自己的count 配合expired来判断所指向资源是否还有被指向的必要 即weaked_ptr可以指向资源,但是不参与管理,不增加shared_ptr引用计数 所以存在于一种所指向资源的计数已经成0 此时expired判断是否失效 若所指向资源失效weaked_ptr就不再指向

在这里插入图片描述

5.3weak_ptr的主要代码

template<class T>
class weak_ptr
{
public:weak_ptr():_ptr(nullptr){}weak_ptr(const shared_ptr<T>& sp):_ptr(sp.GetPtr()){}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* GetPtr(){return _ptr;}private:T* _ptr;
};

6.数组对象的删除问题

6.1代码问题

ape::shared_ptr<Date> sparr(new Date[10]);
当使用我们自己模拟实现的简洁版shared_ptr时 上述代码会报错(手动实现Date的析构函数时会报错 不手动实现调用库的析构函数不报错 因为当手动实现了析构函数 Date的空间前会有一个4字节的空间用来存放实例化对象的个数 以便知道调用几次析构函数 但是此时析构函数应该先向前偏移4字节 以便析构时把这4个字节也释放

6.2std::shared_ptr面对此问题的解决方案

1.首先看std::shared_ptr::~shared_ptr

在这里插入图片描述

即库里的shared_ptr::~shared_ptr与我们写的析构函数不同之处在于 一个对象在实例化时 他会判断是否接受了参数deleter 如果接收则析构时调用deleter析构 若没有接收deleter则正常析构

2.删除器的传参及使用

在这里插入图片描述

销毁对象。但是,以前,根据成员use_count的值,它可能会产生以下副作用:如果use_count大于1(即该对象与其他shared_ptr对象共享其托管对象的所有权):与其共享所有权的其他对象的使用计数将减少1。如果use_coount为1(即,该对象是托管指针的唯一所有者):它所拥有的指针被删除(如果shared_ptr对象是用特殊的deleter构造的,则调用它;否则,函数使用运算符delete)。如果use_count为零(即对象为空),则此析构函数没有副作用。

	template<class T>struct DeleteArray{void operator()(T* ptr){cout << "匿名对象DeleteArray<Date>(): " << ptr << endl;delete[] ptr;}};void test_std_shared_deletor(){//template <class U, class D> //shared_ptr (U* p, D del);   带删除器的构造函数std::shared_ptr<Date> sparr1(new Date[10], DeleteArray<Date>());std::shared_ptr<Date> sparr2(new Date[10],[](Date* ptr){cout << "lambda表达式 delete[]: " << ptr << endl;delete[] ptr;});auto deleter = [](Date* ptr){cout << "lambda表达式 delete[]: " << ptr << endl;delete[] ptr;};std::shared_ptr<Date> sparr3(new Date[10], deleter);std::shared_ptr<FILE> spFile(fopen("Test.cpp", "r"),[](FILE* ptr){cout << "lambda表达式 delete[]: " << ptr << endl;fclose(ptr);});}

在这里插入图片描述

3.添加封装删除器

在这里插入图片描述

在这里插入图片描述

7.总结

7.1完整代码

#pragma once#include <mutex>
#include <thread>
#include <memory>namespace ape
{template<class T>class shared_ptr{public://构造函数shared_ptr(T* ptr = nullptr):_ptr(ptr), _pcount(new int(1)), _pmtx(new mutex){}//删除器构造函数template<class D>shared_ptr(T* ptr, D del):_ptr(ptr), _pcount(new int(1)), _pmtx(new mutex), _del(del){}//非删除器Release()/*void Release(){//上锁_pmtx->lock();//不可释放锁bool deleteFlag = false;if (--(*_pcount) == 0){if (_ptr != nullptr){cout << "delete:" << _ptr << endl;delete _ptr;}delete _pcount;//可释放锁deleteFlag = true;}//解锁_pmtx->unlock();//判断并释放锁if (deleteFlag){delete _pmtx;}}*///删除器Release()void Release(){_pmtx->lock();bool deleteFlag = false;if (--(*_pcount) == 0){if (_ptr != nullptr){_del(_ptr);}delete _pcount;deleteFlag = true;}_pmtx->unlock();if (deleteFlag){delete _pmtx;}}void AddCount(){_pmtx->lock();++(*_pcount);_pmtx->unlock();}//拷贝构造shared_ptr(const shared_ptr<T>& sp):_ptr(sp._ptr), _pcount(sp._pcount), _pmtx(sp._pmtx){AddCount();}//赋值重载//1."自己"给"自己"赋值//不仅仅是p1 = p1 还要考虑p2 = p1 但之前p2就 = p1//2.左 = 右 赋值后 // 左指针指向空间的指针少了一个 左指针的count-- 进一步考虑count--后==0的情况// 右指针指向空间的指针多了一个 右指针的count++shared_ptr<T>& operator=(const shared_ptr<T>& sp){//"自己"给"自己"赋值: "自己"的本质if (_ptr != sp._ptr){//count--Release();_ptr = sp._ptr;_pcount = sp._pcount;_pmtx = sp._pmtx;//count++AddCount();}return *this;}//析构函数~shared_ptr(){Release();}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* GetPtr() const{return _ptr;}int GetCount() const{return *_pcount;}private:T* _ptr;int* _pcount;mutex* _pmtx;//包装器//缺省值处理情况: ape::shared_ptr<Date> sp(new Date);//因为你析构时默认使用删除器 那么遇到没有显示传的要使用缺省值//构造函数传deleter时 可以是仿函数 lambda表达式 函数指针 此处用包装器接收function<void(T*)> _del = [](T* ptr){cout << "lambda表达式 delete: " << ptr << endl;delete ptr;};};void test_shared(){shared_ptr<int> sp1(new int(1));shared_ptr<int> sp2(sp1);shared_ptr<int> sp3(sp2);shared_ptr<int> sp4(new int(10));sp1 = sp4;sp4 = sp1;sp1 = sp1;sp1 = sp2;}//   线程安全问题   ///struct Date{int _year = 0;int _month = 0;int _day = 0;~Date(){}};void SharePtrFunc(ape::shared_ptr<Date>& sp, size_t n, mutex& mtx){cout << "   SharePtrFunc: sp.GetPtr() == " << sp.GetPtr() << endl;cout << "   SharePtrFunc: &sp == " << &sp << endl;for (size_t i = 0; i < n; ++i){ape::shared_ptr<Date> copy(sp);mtx.lock();sp->_year++;sp->_day++;sp->_month++;mtx.unlock();}}void test_shared_safe(){ape::shared_ptr<Date> p(new Date);cout << "test_shared_safe: p.GetPtr() == " << p.GetPtr() << endl;cout << "test_shared_safe: &p == " << &p << endl;const size_t n = 100000;mutex mtx;//线程引用传参即便p和mtx已经是引用 此处仍然需要使用库函数ref();thread t1(SharePtrFunc, ref(p), n, ref(mtx));thread t2(SharePtrFunc, ref(p), n, ref(mtx));t1.join();t2.join();cout << "p.GetCount(): == " << p.GetCount() << endl;cout << "p->_year  == " << p->_year << endl;cout << "p->_month == " << p->_month << endl;cout << "p->_month == " << p->_month << endl;}template<class T>class weak_ptr{public:weak_ptr():_ptr(nullptr){}weak_ptr(const shared_ptr<T>& sp):_ptr(sp.GetPtr()){}T& operator*(){return *_ptr;}T* operator->(){return _ptr;}T* GetPtr(){return _ptr;}private:T* _ptr;};// 循环引用struct ListNode{/*普通写法ListNode* _next;ListNode* _prev;int _val;*//*shared_ptrape::shared_ptr<ListNode> _next;ape::shared_ptr<ListNode> _prev;int _val;*///weaked_ptrape::weak_ptr<ListNode> _next;ape::weak_ptr<ListNode> _prev;int _val;~ListNode(){cout << "~ListNode()" << endl;}};// 循环引用void test_shared_cycle(){/*常规写法 -- 抛异常场景不适合ListNode* n1 = new ListNode;ListNode* n2 = new ListNode;n1->_next = n2;n2->_prev = n1;delete n1;delete n2;*///智能指针写法ape::shared_ptr<ListNode> n1(new ListNode);ape::shared_ptr<ListNode> n2(new ListNode);//内置类型 = 自定义类型 -- error/*ListNode* _next;ListNode* _prev;int _val;ape::shared_ptr<ListNode> n1(new ListNode);ape::shared_ptr<ListNode> n2(new ListNode);n1->_next = n2;n2->_prev = n1;*//*shared_ptr: 引发循环引用问题ape::shared_ptr<ListNode> _next;ape::shared_ptr<ListNode> _prev;int _val;ape::shared_ptr<ListNode> n1(new ListNode);ape::shared_ptr<ListNode> n2(new ListNode);n1->_next = n2;n2->_prev = n1;*//*weak_ptr: 解决循环引用ape::weak_ptr<ListNode> _next;ape::weak_ptr<ListNode> _prev;int _val;ape::shared_ptr<ListNode> n1(new ListNode);ape::shared_ptr<ListNode> n2(new ListNode);*/cout << "n1.GetCount() == " << n1.GetCount() << endl;cout << "n2.GetCount() == " << n2.GetCount() << endl;n1->_next = n2;n2->_prev = n1;cout << "n1.GetCount() == " << n1.GetCount() << endl;cout << "n2.GetCount() == " << n2.GetCount() << endl;}///定制删除器 -- 可调用对象template<class T>struct DeleteArray{void operator()(T* ptr){cout << "匿名对象DeleteArray<Date>(): " << ptr << endl;delete[] ptr;}};//std库deleter的学习/*void test_std_shared_deletor(){//template <class U, class D> //shared_ptr (U* p, D del);   带删除器的构造函数std::shared_ptr<Date> sparr1(new Date[10], DeleteArray<Date>());std::shared_ptr<Date> sparr2(new Date[10],[](Date* ptr){cout << "lambda表达式 delete[]: " << ptr << endl;delete[] ptr;});auto deleter = [](Date* ptr){cout << "lambda表达式 delete[]: " << ptr << endl;delete[] ptr;};std::shared_ptr<Date> sparr3(new Date[10], deleter);std::shared_ptr<FILE> spFile(fopen("Test.cpp", "r"),[](FILE* ptr){cout << "lambda表达式 delete[]: " << ptr << endl;fclose(ptr);});}*///删除器构造函数/*template<class D>shared_ptr(T* ptr, D del):_ptr(ptr), _pcount(new int(1)), _pmtx(new mutex), _del(del){}*/void test_ape_shared_deleter(){ ape::shared_ptr<Date> sp(new Date);ape::shared_ptr<Date> sparr1(new Date[10], DeleteArray<Date>());ape::shared_ptr<Date> sparr2(new Date[10], [](Date* ptr) {cout << "lambda表达式 delete[]: " << ptr << endl;delete[] ptr;});auto deleter = [](Date* ptr){cout << "lambda表达式 delete[]: " << ptr << endl;delete[] ptr;};ape::shared_ptr<Date> sparr3(new Date[10], deleter);ape::shared_ptr<FILE> spFile(fopen("Test.cpp", "r"),[](FILE* ptr){cout << "lambda表达式 delete[]: " << ptr << endl;fclose(ptr);});}
}

7.2C++11和Boost智能指针的关系

  1. C++ 98 中产生了第一个智能指针auto_ptr.
  2. C++ boost给出了更实用的scoped_ptr/shared_ptr/weak_ptr.
  3. C++ TR1,引入shared_ptr。[TR1不是标准版]
  4. C++ 11引入了unique_ptr/shared_ptr/weak_ptr。unique_ptr对应boost的scoped_ptr。[实现原理参考boost实现]

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

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

相关文章

项目管理中,如何建立里程碑式管理?

项目进度控制是项目管理中的重要环节&#xff0c;也是最具挑战性的工作之一。在项目管理中&#xff0c;项目进度失控受到多种因素的影响&#xff0c;导致项目失控。 为了解决这个问题&#xff0c;我们可以借鉴在旅途中学到的经验&#xff0c;通过设立里程碑来了解项目进度&am…

MSQL系列(九) Mysql实战-Join算法底层原理

Mysql实战-Join算法底层原理 前面我们讲解了BTree的索引结构&#xff0c;及Mysql的存储引擎MyISAM和InnoDB,今天我们来详细讲解下Mysql的查询连接Join的算法原理 文章目录 Mysql实战-Join算法底层原理1.Simple Nested-Loop Join 简单嵌套循环2.Block Nested-Loop Join 块嵌套…

CDN技术(1)

1. CDN简介 CDN 是构建在数据网络上的一种分布式的内容分发网。 CDN 的作用是采用流媒体服务器集群技术&#xff0c;克服单机系统输出带宽及并发能力不足的缺点&#xff0c;可极大提升系统支持的并发流数目&#xff0c;减少或避免单点失效带来的不良影响。 2. CDN作用 CDN 利…

list列表前端分页功能已经提交list时容易犯错的问题回顾

最近在开发中&#xff0c;有返回list需要前端分页的&#xff0c;而且后续还需提交整个list&#xff0c;虽说前端分页并不难&#xff0c;但还有会有一些问题&#xff1a; 从图片代码就可以很清晰的看到前端分页&#xff0c;如何点击页数翻页的&#xff0c;很简单&#xff0c;但…

2024通信保研-电磁场电磁波复习

标量场的梯度的旋度恒等于0&#xff0c;旋度的散度等于0。 旋度&#xff1a; rot ⁡ F ( e x ∂ ∂ x e y ∂ ∂ y e z ∂ ∂ z ) ( e x F x e y F y e z F z ) e x ( ∂ F z ∂ y − ∂ F y ∂ z ) e y ( ∂ F x ∂ z − ∂ F z ∂ x ) e x ( ∂ F y ∂ x − ∂ F x …

MS COCO数据集的评价标准以及不同指标的选择推荐(AP、mAP、MS COCO、AR、@、0.5、0.75、1、目标检测、评价指标)

目标检测模型性能衡量指标、MS COCO 数据集的评价标准以及不同指标的选择推荐 0. 引言 0.1 COCO 数据集评价指标 目标检测模型通过 pycocotools 在验证集上会得到 COCO 的评价列表&#xff0c;具体参数的含义是什么呢&#xff1f; 0.2 目标检测领域常用的公开数据集 PASCAL …

网络基础知识

1.什么是链接? 链接是指两个设备之间的连接。它包括用于一个设备能够与另一个设备通信的电缆类型和协议。 2.OSI 参考模型的层次是什么? 有 7 个 OSI 层&#xff1a;物理层&#xff0c;数据链路层&#xff0c;网络层&#xff0c;传输层&#xff0c;会话层&#xff0c;表示层和…

04 文件管理

文件管理 文件和目录的创建 删除文件和目录 文件查找命令 文件的拷贝和移动 打包和压缩

Macos文件图像比较工具:Kaleidoscope for Mac

Kaleidoscope是一款文件图像比较工具&#xff0c;它可以方便地比较两个文本或者图片文件的差异。这个工具可以在Mac系统上使用&#xff0c;并且支持多种文件格式&#xff0c;包括文本文件、图片文件、PDF文件等等。 Kaleidoscope有一个直观的用户界面&#xff0c;可以让用户轻…

【蓝牙协议】简介:蓝牙芯片、蓝牙协议架构

文章目录 蓝牙芯片架构另一个视角由下到上看&#xff1a;Controller-->Host由上到下看&#xff1a;Host-->Controller 蓝牙协议架构视角HW层——蓝牙芯片层Transport——数据传输层HOST——协议层 总结 参考&#xff1a;https://zhuanlan.zhihu.com/p/585248998 参考&…

OpenCV官方教程中文版 —— 图像金字塔

OpenCV官方教程中文版 —— 图像金字塔 前言一、原理二、使用金字塔进行图像融合 前言 • 学习图像金字塔 • 使用图像创建一个新水果&#xff1a;“橘子苹果” • 将要学习的函数有&#xff1a;cv2.pyrUp()&#xff0c;cv2.pyrDown()。 一、原理 一般情况下&#xff0c;我…

全连接层是什么,有什么作用?

大家好啊&#xff0c;我是董董灿。 如果你是搞AI算法的同学&#xff0c;相信你在很多地方都见过全连接层。 无论是处理图片的卷积神经网络&#xff08;CNN&#xff09;&#xff0c;还是处理文本的自然语言处理&#xff08;NLP&#xff09;网络&#xff0c;在网络的结尾做分类…

竞赛选题 深度学习动物识别 - 卷积神经网络 机器视觉 图像识别

文章目录 0 前言1 背景2 算法原理2.1 动物识别方法概况2.2 常用的网络模型2.2.1 B-CNN2.2.2 SSD 3 SSD动物目标检测流程4 实现效果5 部分相关代码5.1 数据预处理5.2 构建卷积神经网络5.3 tensorflow计算图可视化5.4 网络模型训练5.5 对猫狗图像进行2分类 6 最后 0 前言 &#…

循环队列c语言版

一、循环队列结构体 typedef int QueueDataType; #define CQ_MAX_SIZE 10typedef struct CircularQueue {QueueDataType data[CQ_MAX_SIZE];/**标记队列首*/QueueDataType head;/**标记队列尾部*/QueueDataType rear;} CircularQueue; 二、循环队列操作函数声明 /**创建队…

软件工程——期末复习知识点汇总

本帖的资料来源于某国内顶流高校的期末考试资料&#xff0c;仅包含核心的简答题&#xff0c;大家结合个人情况&#xff0c;按需复习~ 总的来说&#xff0c;大层面重点包括如下几个方面&#xff1a; 软件过程需求工程 设计工程软件测试软件项目管理软件过程管理 1.掌握软件生命…

Creaform形创HandySCAN MAX l Elite三维扫描仪便携式3D测量解决方案

CASAIM中科院广州电子智能制造事业部连续多年荣获形创Creaform战略级代理商证书。战略级代理商是形创Creaform最高级别的合作伙伴。 2023年CASAIM中科院广州电子智能制造事业部的形创Creaform战略级代理商证书&#xff1a; Creaform 形创是便携式三维测量解决方案和工程服务领…

[Python进阶] 消息框、弹窗:tkinter库

6.16 消息框、弹窗&#xff1a;tkinter 6.16.1 前言 应用程序中的提示信息处理程序是非常重要的部分&#xff0c;用户要知道他输入的资料到底正不正确&#xff0c;或者是应用程序有一些提示信息要告诉用户&#xff0c;都必须通过提示信息处理程序来显示适当的信息&#xff0c…

NPDP产品经理证书是什么行业的证书?

NPDP是一个跨行业的证书&#xff0c;它适用于各种不同类型和规模的组织。无论是制造业、服务业还是科技领域&#xff0c;都可以从NPDP认证中获益。 1. 制造业&#xff1a; 制造业涉及大量的产品开发和创新活动。从汽车制造到电子设备制造&#xff0c;从家居用品到航天航空&…

idea + Docker-Compose 实现自动化打包部署(仅限测试环境)

一、修改docker.service文件&#xff0c;添加监听端口 vi /usr/lib/systemd/system/docker.service ExecStart/usr/bin/dockerd -H fd:// --containerd/run/containerd/containerd.sock -H tcp://0.0.0.0:2375 -H unix://var/run/docker.sock重启docker服务 systemctl daemo…

Shopee、Lazada卖家不得不看的提升销量技巧,自养号测评打造权重

近年来&#xff0c;大部分虾皮、Lazada卖家开始通过测评补单的方式来提升店铺权重和产品排名&#xff0c;以吸引更多流量。这种方式可以有效提高产品的销售转化率&#xff0c;对店铺的运营起到推动作用。然而&#xff0c;测评补单并非简单的购买过程&#xff0c;其中涉及到许多…