C++【智能指针】

news2024/11/19 9:36:46

文章目录

  • 一、什么是智能指针
    • RAII思想
    • std::auto_ptr
  • 二、智能指针的拷贝问题(C++98)
    • 1.unique_ptr
    • 2.shared_ptr
      • shared_ptr的问题
      • 循环引用的问题
    • 3.weak_ptr
    • 内存泄漏的危害

一、什么是智能指针

#include<iostream>

using namespace std;
int div()
{
    int a, b;
    cin >> a >> b;
    if (b == 0)
        throw invalid_argument("除0错误");
    return a / b;
}
void Func()
{
// 1、如果p1这里new 抛异常会如何?
// 2、如果p2这里new 抛异常会如何?
// 3、如果div调用这里又会抛异常会如何?
// 如果出了异常,会直接跳转到捕获的地方去了。
    int* p1 = new int;
    int* p2 = new int;
    cout << div() << endl;
    //如果是p1 抛异常需要释放p2和div
    //如果是p2 抛异常需要释放p1和div
    //如果是div抛异常,需要释放p1和p2
    delete p1;
    delete p2;
}
int main()
{
    try
    {
        Func();
    }
    catch (exception& e)
    {
        cout << e.what() << endl;
    }
    return 0;
}

RAII思想

RAII(Resource Acquisition Is Initialization)(资源请求即初始化,也就是获取到资源,马上就初始化)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:
①不需要显式地释放资源。
②采用这种方式,对象所需的资源在其生命期内始终保持有效

#include<iostream>

using namespace std;

//利用RAII思想设计的delete资源的类
template<class T>
class SmartPtr
{
public:
    SmartPtr(T* ptr)
        :_ptr(ptr)
    {}

    ~SmartPtr(){
        cout<<"delete"<<endl;
        delete _ptr;
    }

private:
    T* _ptr;
};
int div()
{
    int a, b;
    cin >> a >> b;
    if (b == 0)
        throw invalid_argument("除0错误");
    return a / b;
}
void Func()
{
    SmartPtr<int>  sp1(new int);
    SmartPtr<int>  sp2(new int);
    cout << div() << endl;

    cout<<"释放资源"<<endl;
}
int main()
{
    try
    {
        Func();
    }
    catch (exception& e)
    {
        cout << e.what() << endl;
    }
    return 0;
}

在这里插入图片描述
不管是正方func结束,还是抛异常,sp1和sp2都会调用析构函数,释放资源。

但是我们这里就不能通过解引用的方式获取到资源了,这里我们就需要通过运算符重载来获取到我们的资源。

#include<iostream>

using namespace std;

//利用RAII思想设计的delete资源的类
template<class T>
class SmartPtr
{
public:
    SmartPtr(T* ptr)
        :_ptr(ptr)
    {}

    ~SmartPtr(){
        cout<<"delete"<<endl;
        delete _ptr;
    }

    T&operator*()
    {
        return *_ptr;
    }

    T&operator->()
    {
        return *_ptr;
    }
private:
    T* _ptr;
};
int div()
{
    int a, b;
    cin >> a >> b;
    if (b == 0)
        throw invalid_argument("除0错误");
    return a / b;
}
void Func()
{
    SmartPtr<int>  sp1(new int);
    SmartPtr<int>  sp2(new int);
    cout << div() << endl;
    *sp1=1;
    *sp2=2;
    cout<<*sp1<<endl;
    cout<<*sp2<<endl;
    cout<<"释放资源"<<endl;
}
int main()
{
    try
    {
        Func();
    }
    catch (exception& e)
    {
        cout << e.what() << endl;
    }
    return 0;
}

1.利用RAII思想设计delete类
2.重载*和->运算符

std::auto_ptr

C++98版本的库中就提供了auto_ptr的智能指针。下面演示的auto_ptr的使用及问题。

#include<iostream>
#include <memory>
using namespace std;
class A
{
public:
    ~A(){
        cout<<"~A()"<<endl;
    }
private:
    int _a1;
    int _a2;
};
int main()
{
    auto_ptr<A>ap1(new A);

    return 0;
}

在这里插入图片描述

#include<iostream>
#include <memory>
using namespace std;
class A
{
public:
    ~A(){
        cout<<"~A()"<<endl;
    }
//private:
    int _a1=0;
    int _a2=0;
};
int main()
{
    auto_ptr<A>ap1(new A);
    ap1->_a1++;
    ap1->_a2++;
    return 0;
}

在这里插入图片描述

二、智能指针的拷贝问题(C++98)

因为智能指针在拷贝的时候,只有一个内置类型的拷贝,所以发生的是浅拷贝,会析构同一块空间,而二次析构内存就会报错,也就是我们的浅拷贝问题

#include<iostream>
#include <memory>
using namespace std;


template<class T>
class SmartPtr {
public:
    SmartPtr(T* ptr = nullptr)
            : _ptr(ptr)
    {}
    ~SmartPtr()
    {
        if(_ptr)
            delete _ptr;
    }
    T& operator*() {return *_ptr;}
    T* operator->() {return _ptr;}
private:
    T* _ptr;
};

class A
{
public:
    ~A(){
        cout<<"~A()"<<endl;
    }
//private:
    int _a1=0;
    int _a2=0;
};
int main()
{
    SmartPtr<A>sp1(new A);
    sp1->_a1++;
    sp1->_a2++;


    SmartPtr<A>sp2(sp1);

    return 0;
}

在这里插入图片描述

解决方案
1.深拷贝?不能,因为违背了功能需求。智能指针只是像指针一样,帮你托管空间。
但是像下面这种情况我们就需要的是浅拷贝。

list<int> lt;
auto it=lt.begin();

为什么迭代器浅拷贝没有问题呢?
因为迭代器并不负责资源的管理!迭代器只是为了封装底层的细节,以统一的方式来遍历资源。它不管迭代器中资源的释放。

#include<iostream>
#include <memory>
using namespace std;

class A
{
public:
    ~A(){
        cout<<"~A()"<<endl;
    }
//private:
    int _a1=0;
    int _a2=0;
};
int main()
{
    auto_ptr<A>sp1(new A);
    sp1->_a1++;
    sp1->_a2++;


    auto_ptr<A>sp2(sp1);

    return 0;
}

库里面的是没有问题的。
因为库里面的行为就是将sp1的资源转移给了sp2
是资源管理权转移,不负责任地拷贝,会导致被拷贝对象的悬空问题
在这里插入图片描述
在这里插入图片描述

手动实现一个auto_ptr,了解上述的C++98的智能指针的底层

#include <iostream>
#include <string>
using namespace std;
namespace zhuyuan
{
    template<class T>
    class auto_ptr
    {
    public:
        auto_ptr(T* ptr)
                :_ptr(ptr)
        {}

        ~auto_ptr(){
            cout<<"delete"<<endl;
            delete _ptr;
        }

        auto_ptr(auto_ptr<T>&ap)
            :_ptr(ap._ptr)
        {
            ap._ptr=nullptr;
        }

        auto_ptr<T>& operator=(auto_ptr<T>& ap)
        {
            if(this!=&ap)
            {
                if(_ptr)
                {
                    delete _ptr;
                }
                _ptr=ap._ptr;
                ap._ptr= nullptr;
            }
            return *this;
        }

        T&operator*()
        {
            return *_ptr;
        }

        T* operator->()
        {
            return _ptr;
        }
    private:
        T* _ptr;
    };

}


class A
{
public:
    ~A()
    {
        cout<<"~A()"<<endl;
    }
//private:
    int _a1=0;
    int _a2=0;
};

int main()
{
    zhuyuan::auto_ptr<A> ap1(new A);
    ap1->_a1++;
    ap1->_a2++;
    cout<<ap1->_a1<<" "<<ap1->_a2<<endl;

    zhuyuan::auto_ptr<A> ap2(ap1);

    return 0;
}


在这里插入图片描述

这个时候如果我们再去调用sp1指针,我们的程序就会发生崩溃。
很多公司明确要求不能使用C++98的智能指针

boost库
智能指针首先从boost社区中发展起来的。
scoped_ptr
shared_ptr
weak_ptr

C++11
unique_ptr
shared_ptr
weak_ptr

1.unique_ptr

不允许拷贝,只要有拷贝就会报错

void test_unique_ptr()
{
    std::unique_ptr<A> up2(new A);
    std::unique_ptr<A> up1(up2);
}

在这里插入图片描述

手动实现

namespace zhuyuan
{
    template<class T>
    class unique_ptr
    {
    public:
        unique_ptr(T* ptr)
                :_ptr(ptr)
        {}

        ~ unique_ptr(){
            cout<<"delete"<<endl;
            delete _ptr;
        }

		//防止拷贝C++11
        unique_ptr( unique_ptr<T>&ap)=delete;
        unique_ptr<T>& operator=( unique_ptr<T>& ap)=delete;
		
		//防止拷贝C++98
		//只声明,不实现
        T&operator*()
        {
            return *_ptr;
        }

        T* operator->()
        {
            return _ptr;
        }
    private:
        T* _ptr;
    };

}
void test_unique_ptr()
{
    zhuyuan::unique_ptr<A> up2(new A);
    zhuyuan::unique_ptr<A> up1(up2);
    up1->_a1++;
    up1->_a2++;
    zhuyuan::unique_ptr<A>up3=up2;
}

在这里插入图片描述

unique_ptr:简单粗暴,不让拷贝,只适用于不需要拷贝的一些场景

那如果我们就是需要拷贝呢?

2.shared_ptr

void test_shared_ptr()
{
    std::shared_ptr<A> up2(new A);
    std::shared_ptr<A> up1(up2);
    up1->_a1++;
    up1->_a2++;
    cout<<up1->_a1<<" "<<up1->_a2<<endl;
    shared_ptr<A>up3=up2;
}

在这里插入图片描述

也就是说,它们共同管理了同一份数据。也就是三个指针指向的都是up2中的资源。
想要实现的话,我们就需要引用计数。
因为多个对象管理同一份资源,析构的时候就会出问题。但是,如果我们这时引入一个计数,也就是表示当前有多少个对象正在管理这份资源。当对象被析构的时候,我们就将这个计数–,当有新的对象引用这份资源的时候,我们就将这个计数++,当最后一个析构的对象释放时,释放这份资源。也就是说只需要析构一次就可以了。

这里使用静态计数对象是不可以滴。
因为一个资源就需要配一个计数,多个智能指针对象共管。
如果是静态对象的话,是所有资源都是有一个计数,因为静态成员属于整个类,类的所有对象。

每个资源需要管理时,会给构造函数,构造new一个计数。

namespace zhuyuan
{
    template<class T>
    class shared_ptr
    {
    public:
        void Release()
        {
        	//如果这里的引用计数先--,然后等于0了之后,也就是说没有对象使用这份资源了
        	//并且我们的要析构的资源的指针不为nullptr的时候
        	//我们就进行析构。
        	//同时清空我们这里的计数和我们的指向的资源。
            if (--(*_pCount) == 0 && _ptr)
            {
                cout << "delete" << _ptr << endl;
                delete _ptr;
                _ptr = nullptr;

                delete _pCount;
                _pCount = nullptr;
            }
        }

        // RAII思想
        shared_ptr(T* ptr)
        //使用初始化列表进行初始化
        //初始化我们这里的资源
                :_ptr(ptr)
                //初始化我们的计数
                //创建一个类对象的时候,默认计数就是1
                , _pCount(new int(1))
        {}

        ~shared_ptr()
        {
        //调用release进行析构
            Release();
        }

        //拷贝构造
        //sp1(sp2)
        shared_ptr(const shared_ptr<T>& sp)
        //将指针和计数都进行拷贝
                :_ptr(sp._ptr)
                , _pCount(sp._pCount)
        {
            //将计数++,表示又有一个对象对其进行了引用
            (*_pCount)++;
        }


		//赋值
        // sp1 = sp3
        shared_ptr<T>& operator=(const shared_ptr<T>& sp)
        {
            //if (this != &sp)
            //防止自己给自己赋值
            if (_ptr != sp._ptr)
            {
            	//这里我们需要先把我们sp1的对象的资源的计数给释放掉
            	//然后再将sp1拷贝给sp3
            	//否则我们sp1所指向的资源的计数就会比真实的多出一份
            	//就会导致内存泄漏的问题!!
                Release();
				
				//将指针赋值
                _ptr = sp._ptr;
                //将计数也进行赋值
                _pCount = sp._pCount;
                //将计数++
                ++(*_pCount);
            }

            return *this;
        }


        // 像指针一样
        T& operator*()
        {
            return *_ptr;
        }

        T* operator->()
        {
            return _ptr;
        }

        T* get()
        {
            return _ptr;
        }

    private:
    	//指向的资源的地址
        T* _ptr;
        //引用计数
        int* _pCount;
    };
}


class A
{
public:
    ~A()
    {
        cout<<"~A()"<<endl;
    }
//private:
    int _a1=0;
    int _a2=0;
};
void test_shared_ptr()
{
    zhuyuan::shared_ptr<A> up2(new A);
    zhuyuan::shared_ptr<A> up1(up2);
    up1->_a1++;
    up1->_a2++;
    cout<<up1->_a1<<" "<<up1->_a2<<endl;
    zhuyuan::shared_ptr<A>up3=up2;

    zhuyuan::shared_ptr<int>up4(new int);
    zhuyuan::shared_ptr<A> sp5(new A);
    zhuyuan::shared_ptr<A> sp6(sp5);

}

在这里插入图片描述
在这里插入图片描述
重点注意
这里的赋值问题

// sp1 = sp3
        shared_ptr<T>& operator=(const shared_ptr<T>& sp)
        {
       		 //防止自己给自己赋值
			//如果自己给自己赋值,首先就会释放自己的资源,然后再进行赋值
			//就会崩溃。
            //if (this != &sp)
            
			//采用下面的方法更好,这样我们知道如果我们拷贝的对象和我们被拷贝的对象
			//所持有的是同一块空间的话,我们就不会进行拷贝。
			//比方说我们的sp1和sp2指向的是同一块资源
			//然后我们这里运行的时候,知道了sp1的ptr和sp2的ptr指向的是同一块空间
			//就不再会进行拷贝。
            if (_ptr != sp._ptr)
            {
            // 我们这里拿sp1 = sp3距离
            	
            	//因为我们的sp1之后不再持有原来的资源了,而是指向sp3的资源
            	//这里我们需要先把我们sp1的对象的资源的计数给释放掉
            	//然后再将sp3拷贝给sp1
            	//否则我们原先sp1所指向的资源的计数就会比真实的多出一份
            	//就会导致内存泄漏的问题!!
                Release();
				
				//共同管理新的资源,++计数
				//将指针赋值
                _ptr = sp._ptr;
                //将计数也进行赋值
                _pCount = sp._pCount;
                //将计数++
                ++(*_pCount);
            }

            return *this;
        }
		void Release()
        {
        	//--被赋值对象的计数,如果是最后一个对象,需要释放资源
            if (--(*_pCount) == 0 && _ptr)
            {
                cout << "delete" << _ptr << endl;
                delete _ptr;
                _ptr = nullptr;

                delete _pCount;
                _pCount = nullptr;
            }
        }

shared_ptr的问题

如果两个智能指针指向同一块资源给多线程用的时候,里面可能会存在线程安全的风险。

循环引用的问题

struct Node
{
    int _val;
    //自定义类型的对象不能赋值给原生指针,
    //我们需要将其转换成智能指针
    std::shared_ptr<Node> _next;
    std::shared_ptr<Node> _prev;

    ~Node()
    {
        cout<<"~Node"<<endl;
    }
};
void test_shared_ptr2()
{
    //不支持隐式类型转换,所以我们需要使用()来进行赋值
    std::shared_ptr<Node> n1(new Node);
    std::shared_ptr<Node> n2(new Node);

    
    //循环引用问题
    n1->_next=n2;
    n2->_prev=n1;
}
int main()
{
    test_shared_ptr2();
    return 0;
}

没有打印析构的信息,我们的资源没有被正确释放,这就出现了内存泄漏

在这里插入图片描述

如何理解这个问题?

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

在这里插入图片描述
当这个函数(test_shared_ptr2)结束了之后n2先析构,n1再析构,因为n2后定义,先析构
在这里插入图片描述
在这里插入图片描述
然后就结束了。
我们的两个结点都没有正常释放!!

进一步分析
n1的_next管着右边节点的内存块,n2的_prev管着左边结点的内存块。
所以n1的_next析构,右边的结点就释放了(delete),
n2的_next析构,左边的结点就释放了(delete)。

那么_next什么时候释放呢?_prev什么时候释放呢?
右边结点什么时候delete呢?
左边的结点被delete,调用析构函数,_next作为成员才会析构
左边结点什么时候delete呢?
右边的结点被delete,调用析构函数,_prev作为成员才会析构
这就是一个循环等待对方释放的过程!

share_ptr内部无法解决这个问题!所以我们需要使用weak_ptr!

3.weak_ptr

weak_ptr不是常规智能指针,没有RAII,不支持直接管理资源
weak_ptr主要用shared_ptr构造,用来解决shared_ptr的循环引用的问题
在这里插入图片描述
可以使用shared_ptr进行构造,为了不增加引用计数,也就是不参与资源管理

struct Node
{
    int _val;
    std::weak_ptr<Node> _next;
    std::weak_ptr<Node> _prev;

    ~Node()
    {
        cout<<"~Node"<<endl;
    }
};
void test_shared_ptr2()
{
    //不支持隐式类型转换,所以我们需要使用()来进行赋值
    std::shared_ptr<Node> n1(new Node);
    std::shared_ptr<Node> n2(new Node);

    n1->_next=n2;
    n2->_prev=n1;
}
int main()
{
    test_shared_ptr2();
    return 0;
}

这里当_next和_prev是weak_ptr的时候,它不参与资源的释放管理,但是可以访问和修改资源,不增加计数,不存在循环引用的问题。

可以使用use_count来看一下这里使用weak_ptr前后的计数是多少

void test_shared_ptr2()
{
    //不支持隐式类型转换,所以我们需要使用()来进行赋值
    std::shared_ptr<Node> n1(new Node);
    std::shared_ptr<Node> n2(new Node);

    cout<<n1.use_count()<<endl;
    cout<<n2.use_count()<<endl;
    //自定义类型的对象不能赋值给原生指针
    n1->_next=n2;
    n2->_prev=n1;
    cout<<n1.use_count()<<endl;
    cout<<n2.use_count()<<endl;
}

在这里插入图片描述

内存泄漏的危害

内存泄漏,在进程结束的时候,操作系统都是会回收的,我们为什么要解决内存泄漏?
因为我们很多服务器中的进程都是不会关掉的!除非停服维修!并且有一些进程不能够正常释放

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

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

相关文章

哈希散列表hlist_head - linux内核经典实例

hlist_head和hlist_node用于散列表&#xff0c;分别表示列表头&#xff08;数组中的一项&#xff09;和列表头所在双向链表中的某项&#xff0c;两者结构如下: include/linux/types.h(line 190) struct hlist_head {struct hlist_node *first; };struct hlist_node {struct h…

护眼灯真的可以保护眼睛吗?2022双十二选哪款护眼灯对孩子眼睛好

传统的台灯只是单一色光&#xff0c;无法调节台灯的照度和色温&#xff0c;长时间使用不但不可以护眼&#xff0c;而且还会导致近视、散光等各种问题的发生。现在的护眼台灯大多都是使用led灯珠作为发光源&#xff0c;不但本身比较高效节能&#xff0c;而且光线可调控&#xff…

react--redux

此篇文章非学习使用&#xff0c;学习勿入 redux 文档&#xff1a; http://www.redux.org.cn 用于做状态管理的js库 集中管理react中多个组件共享的状态 安装&#xff1a; cnpm i redux 给形参赋值&#xff0c;表示形参的默认值 错误&#xff1a; 对象不能作为一个dom元素…

NPDP认证|制造业产品经理日常工作必备技能,快来学习提升吧!

不同阶段的产品经理对技能的掌握程度要求不同&#xff0c;侧重点也不同&#xff0c;一般包括需求分析、数据分析、竞品分析、商业分析、行业分析、需求收集、产品设计、版本管理、用户调研等。这些技能&#xff0c;是我们必须要掌握的专业技能。 比如&#xff1a;对于刚入行的…

异常检测算法分类总结(含常用开源数据集)

作者&#xff1a;云智慧算法工程师 Chris Hu 异常检测是识别与正常数据不同的数据&#xff0c;与预期行为差异大的数据。本文详细介绍了异常检测的应用领域以及总结梳理了异常检测的算法模型分类。文章最后更是介绍了常用的异常算法数据集。 异常的概念与类型 目前异常检测主…

硝酸根离子深度去除树脂

普通的阴离子交换树脂对阴离子的交换次序是&#xff1a;SO42-&#xff1e;NO3-&#xff1e;HCO3-&#xff0c;对硝酸盐没有选择性&#xff0c;优先交换水中硫酸根&#xff0c;造成树脂再生频繁&#xff0c;产水中氯离子含量增高&#xff0c;出水水质稳定性差&#xff0c;树脂交…

[注塑]各种进胶方式优缺点分析

[注塑]各种进胶方式优缺点分析1.直接进胶2.测胶口3.搭接式浇口4.扇形浇口5.潜胶6.弧线浇口7.针形浇口结构设计的时&#xff0c;分析浇口的进胶方式尤为重要&#xff0c;为了简便我们的设计&#xff0c;常常需要将一些常用的标准形式&#xff0c;以下是我们常见的一些浇口形式。…

死磕sparkSQL源码之TreeNode

InternalRow体系 学习TreeNode之前&#xff0c;我们先了解下InternalRow。 对于我们一般接触到的数据库关系表来说&#xff0c;我们对于数据库中的数据操作都是按照“行”为单位的。在spark sql内部实现中&#xff0c;InternalRow是用来表示这一行行数据的类。看下源码中的解…

Spring Cloud(十二):Spring Cloud Security

主要内容 Spring Security 模块使用设置用户名密码基于内存基于UserDetailsService 接口基于配置类WebSecurityConfigurerAdapter基于DB 用户-角色-权限自定义登录页面登录认证流程自定义成功、自定义失败会话管理&#xff08;Session)会话控制会话超时会话并发控制集群sessio…

【Webpack】webpack的基础使用详细总结 下(建议收藏)

1- 前言 昨天已经介绍了weback的基础使用了&#xff0c;详细总结看这边博客&#xff01;&#xff01;&#xff01; 【Webpack】webpack的基础使用详细总结 上&#xff08;建议收藏&#xff09; 今天来总结一下剩余的常用 &#xff01;&#xff01;&#xff01;&#xff01; …

微信抽奖活动有什么作用_分享微信抽奖小程序开发的好处

在H5游戏中&#xff0c;抽奖是最受消费者喜爱的模式之一。将H5微信抽奖活动结合到营销中&#xff0c;可以带来意想不到的效果&#xff0c;带流量和曝光率&#xff0c;所以许多企业也会在做活动时添加上不同类型的H5微信抽奖活动。 那么&#xff0c;新手怎么搭建微信抽奖活动&am…

01背包、完全背包、多重背包、分组背包总结

文章目录一、01背包问题二、完全背包问题三、多重背包问题四、分组背包一、01背包问题 n个物品&#xff0c;每个物品的重量是wiw_iwi​&#xff0c;价值是viv_ivi​&#xff0c;背包的容量是mmm 若每个物品最多只能装一个&#xff0c;且不能超过背包容量&#xff0c;则背包的最…

【ABAP】SAP发送消息至RabbitMQ

SAP发送消息至RabbitMQ ——以下关于RabbitMQ的内容大致转载于朱忠华老师的《RabbitMQ实战指南》一书 【基础知识】 消息队列中间件(Message Queue Middleware,即MQ)也可以称之为消息队列或者消息中间件,是指利用高效可靠的消息传递机制进行与平台无关的数据交流,并基于数…

面试官: B 树和 B+ 树有什么区别?

问各位小可爱一个问题&#xff1a;MySQL 中 B 树和 B 树的区别&#xff1f; 请自己先思考5秒钟&#xff0c;看看是否已经了然如胸&#xff1f; 好啦&#xff0c;时间到&#xff01; B 树和 B 树是两种数据结构&#xff0c;构建了磁盘中的高速索引结构&#xff0c;因此不仅 …

上海亚商投顾:沪指窄幅震荡 “中字头”概念股又暴涨

上海亚商投顾前言&#xff1a;无惧大盘大跌&#xff0c;解密龙虎榜资金&#xff0c;跟踪一线游资和机构资金动向&#xff0c;识别短期热点和强势个股。 市场情绪沪指今日窄幅震荡&#xff0c;深成指、创业板指盘中跌超1%&#xff0c;午后探底回升一度翻红。光伏、储能等赛道午后…

[Spring Cloud] GateWay自定义过滤器/结合Nacos服务注册中心

✨✨个人主页:沫洺的主页 &#x1f4da;&#x1f4da;系列专栏: &#x1f4d6; JavaWeb专栏&#x1f4d6; JavaSE专栏 &#x1f4d6; Java基础专栏&#x1f4d6;vue3专栏 &#x1f4d6;MyBatis专栏&#x1f4d6;Spring专栏&#x1f4d6;SpringMVC专栏&#x1f4d6;SpringBoot专…

DocuWare Workflow Manager(工作流管理器)

DocuWare Workflow Manager 公司是按流程运转的。销售、人力资源、财务等部门需要流畅、可靠的信息传输&#xff0c;以便在正确的时间做出正确的决策。订单管理、员工入职和发票审批等流程可以根据您的精确需求进行设计和自动化&#xff0c;避免时间浪费。 适用于复杂业务的简…

Mysql数据库相关面试题

1.关系型和非关系型数据库的区别是什么? 关系型和非关系型数据库的主要差异是数据存储的方式,关系型数据库天然就是表格存储,因此存储在数据表的行和列中,数据表可以彼此关联协作存储,很容易提取数据. 优点: 易于维护:都是使用表结构,格式一致,使用方便:sql语言通用,可以用于复…

MyBatis逆向工程和分页插件

1、分页插件 MyBatis 通过提供插件机制&#xff0c;让我们可以根据自己的需要去增强MyBatis 的功能。需要注意的是&#xff0c;如果没有完全理解MyBatis 的运行原理和插件的工作方式&#xff0c;最好不要使用插件&#xff0c; 因为它会改变系底层的工作逻辑&#xff0c;给系统带…

2022年全国职业院校技能大赛:网络系统管理项目-模块B--Windows样题7

初始化环境1.默认账号及默认密码 Username: Administrator Password: ChinaSkill22! Username: demo Password: ChinaSkill22! 注:若非特别指定,所有账号的密码均为 ChinaSkill22! 项目任务描述你作为技术工程师,被指派去构建一个公司的内部网络,要为员工提供便捷、安…