C++11中的智能指针unique_ptr、shared_ptr和weak_ptr详解

目录

1、引言

2、什么是智能指针?

3、在Visual Studio中查看智能指针的源码实现

4、独占式指针unique_ptr

4.1、查看unique_ptr的源码实现片段

4.2、为什么unique_ptr的拷贝构造函数和复制函数被delete了?(面试题)

4.3、使用unique_ptr独占式智能指针的实例

5、共享式指针shared_ptr 

5.1、查看shared_ptr的源码实现片段

5.2、shared_ptr的类图说明

5.3、shared_ptr循环引用问题(面试题)

5.4、使用shared_ptr的实例

6、弱指针weak_ptr

6.1、查看weak_ptr的源码实现片段

6.2、使用weak_ptr的实例

7、使用智能指针是否会影响到程序的执行效率?

8、有必要学习C++11标准中的新特性

9、最后


VC++常用功能开发汇总(专栏文章列表,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/124272585C++软件异常排查从入门到精通系列教程(专栏文章列表,欢迎订阅,持续更新...)https://blog.csdn.net/chenlycly/article/details/125529931C++软件分析工具案例集锦(正在更新中...)https://blog.csdn.net/chenlycly/category_12279968.htmlC/C++基础与进阶(正在更新中...)https://blog.csdn.net/chenlycly/category_11931267.html       C++11引入了unique_ptr、shared_ptr和weak_ptr三个智能指针,给我们编写C++代码带来了很大的便利,今天我们就来详细讲讲这三个智能指针的相关内容。

1、引言

       在C++程序中,大部分异常问题都是与内存相关的,都是内存操作异常引起的。对于动态申请的堆内存,C++没有内存回收机制,动态申请的内存需要程序员自己去释放。如果不去释放,则会造成内存泄漏。内存泄漏是个很常见的问题,在日常开发过程中会时不时地遇到。发生内存泄漏的代码如果频繁执行,则会导致持续的内存泄漏,长时间运行之后就会使得程序占用的虚拟内存接近或超过进程的虚拟内存上限,就会导致Out of memory内存耗尽的异常,引发程序发生闪退或崩溃。

以32位程序为例,程序启动时系统会给程序进程分配4GB的虚拟内存空间,其中内核态和用户态内存各占一半,即用户态内存为2GB,如果用户态的代码占用的用户态虚拟内存达到或超过2GB,就会触发Out of memory内存耗尽的异常。

       C++11标准引入三个智能指针:unique_ptr、shared_ptr和weak_ptr,使用这些智能指针就能很好地解决内存泄漏的问题。

2、什么是智能指针?

       其实早在1998年发布的C++98标准中就引入了智能指针auto_ptr,不过auto_ptr有一些缺陷,现在已经被废弃了。

       C++智能指针是存储指向动态分配(堆)对象指针的类,用于C++类对象的生存期的控制,确保在离开指针所在作用域时,自动地销毁动态分配的对象,防止内存泄露。智能指针的核心实现技术是引用计数,每引用C++对象1次,内部引用计数就加1;智能指针每析构1次,内部的引用计数就减1,当引用计数减为0时,就会删除指向的C++类对象(释放类对象的堆内存)。

        C++11标准引入三个智能指针unique_ptr、shared_ptr和weak_ptr,位于C++标准STL库中,在 <memory> 头文件中的 std 命名空间中定义的。

这个地方需要注意一下,有的朋友可能会以为STL只包含vector、list和map等容器,其实大家平常使用的字符串类string、输入输出iostream、unique_ptr等智能指针,都是STL标准模板库中的。STL模板库不仅仅包含容器和迭代器。C++标准库主要由C库、C++库和STL标准模板库构成,其中STL标准模板库在C++标准库中比重占了80%左右,即C++标准库中一大半都是STL库。

       C++11标准中主要使用unique_ptr和shared_ptr两智能指针,weak_ptr则主要用来辅助shared_ptr,避免出现循环引用问题的。在使用unique_ptr和shared_ptr两个智能指针类时,可以使用指针运算符(-> 和 *)访问指向的对象,因为智能指针类重载了->和*运算符,以返回指向的对象(指针)。

_NODISCARD add_lvalue_reference_t<_Ty> operator*() const
    {    // return reference to object
        return (*get());
    }

_NODISCARD pointer operator->() const noexcept
    {    // return pointer to class object
        return (this->_Myptr());
    }

_NODISCARD pointer get() const noexcept
    {    // return pointer to object
        return (this->_Myptr());
    }

3、在Visual Studio中查看智能指针的源码实现

        C++11引入的unique_ptr、shared_ptr和weak_ptr,可以直接在Visual Studio中查看源码实现。这三个智能指针位于STL库中,而Visual Studio采用的是P.J. STL版本。

       注意一下,要在Visual Studio中查看unique_ptr和shared_ptr实现源码,需要使用Visual Studio 2015或以上版本,低版本的Visual Studio可能不支持C++11标准或者支持了部分C++11新特性。比如Visual Studio 2010中只支持了部分C++11的新特性,比如匿名函数(lamda表达式),但不支持unique_ptr和shared_ptr智能指针(会显示未定义)。

       目前,STL主要有5个版本:

1)HP 原始版本
       HP STL 是 Alexandar Stepanov(STL 标准模板库之父)在惠普 Palo Alto 实验室工作时,与 Meng Lee 合作完成的。本着开源精神,他们声明允许任何人任意运用、拷贝、修改、传播、商业使用这些代码,无需付费。唯一要遵守的是,必修在文件中加上HP的版本声明和运用权限声明。 
2)P. J. 实现版本
       由 P. J. Plauger 开发,继承自 HP 版本,该版本不开源,不能公开、修改或贩卖。该版本不开源也是合法的,因为HP没要求强迫要求其衍生产品必须开源。该版本被微软 Visual C++ 采用,缺陷是,可读性比较低,符号命名也比较怪异。但我们在Visual Studio中阅读该版本的实现源码时,感觉还好,也没传说中那么难读。
3)RW 实现版本
       由 Rouge Wage 公司开发,继承自 HP 版本,被Borland公司的 C+ + Builder 采用。该版本也不是开源的,不能公开、修改或贩卖,这个版本的可读性还不错。
由 Rouge Wage 公司开发,继承自 HP 版本,被Borland公司的 C+ + Builder 采用。该版本也不是开源的,不能公开、修改或贩卖,这个版本的可读性还不错。值得一提的是,尽管 Rouge Wave STL 的性能不是很好,但 C++ Builder 对 C++ 语言标准的支持还算不错,所以在一定程度上使 Rouge Wave STL 的表现得以改善。但遗憾的是,由于 Rouge Wave STL 长期没有更新且不完全符合标准,因此 Rouge Wave STL 在 6.0 版本时改用了 STLport 版本(之后的版本也都采用了 STLport),不过考虑到和之前版本的兼容,6.0 版本中依旧保留了 Rouge Wave STL。
4)SGI 实现版本
        由 Silicon Graphics Computer Systems,Inc 公司开发,继承自 HP 版本。该版本被 Linux GCC 采用,可移植性好, 在 Linux 平台上的性能非常出色。该版本是开源的,可公开、修改甚至贩卖。 无论是符号命名,还是编程风格,这个版本的可读性非常高。如果大家要学习 STL源码,推荐大家看这个版本的源码实现。侯捷老师的经典书籍《STL源码剖析》,也是基于这个版本展开的。
5)STLport 实现版本
        为了使 SGI STL 的基本代码都适用于 VC++ 和 C++ Builder 等多种编译器,俄国人 Boris Fomitchev 建立了一个 free 项目来开发 STLport,此版本 STL 是开放源码的。由于 Rouge Wave STL 长期没有更新且不完全符合标准,因此 Rouge Wave STL 在 6.0 版本时改用了 STLport 版本,之后的版本也都采用了 STLport。

4、独占式指针unique_ptr

        unique_ptr是独占式智能指针,它拥有对其所指向对象的唯一所有权。unique_ptr 指针被销毁时,它所指向的对象也会被销毁。由于其具有独占性,所以unique_ptr 不能被拷贝,只能被转移所有权。将对象的所有权转移到新的unique_ptr对象中,原先的unique_ptr对象不再指向原来的对象。

4.1、查看unique_ptr的源码实现片段

        可以在Visual Studio中输入unique_ptr,然后go到unique_ptr的定义处查看unique_ptr的源码实现,都是基于模板实现的:(下面给出部分unique_ptr的源码)

    // CLASS TEMPLATE unique_ptr SCALAR
template<class _Ty,
    class _Dx>    // = default_delete<_Ty>
    class unique_ptr
        : public _Unique_ptr_base<_Ty, _Dx>
    {    // non-copyable pointer to an object
public:
    typedef _Unique_ptr_base<_Ty, _Dx> _Mybase;
    typedef typename _Mybase::pointer pointer;
    typedef _Ty element_type;
    typedef _Dx deleter_type;

    using _Mybase::get_deleter;

    template<class _Dx2 = _Dx,
        _Unique_ptr_enable_default_t<_Dx2> = 0>
        constexpr unique_ptr() noexcept
            : _Mybase(pointer())
        {    // default construct
        }

    template<class _Dx2 = _Dx,
        _Unique_ptr_enable_default_t<_Dx2> = 0>
        constexpr unique_ptr(nullptr_t) noexcept
            : _Mybase(pointer())
        {    // null pointer construct
        }

    unique_ptr& operator=(nullptr_t) noexcept
        {    // assign a null pointer
        reset();
        return (*this);
        }

    template<class _Dx2 = _Dx,
        _Unique_ptr_enable_default_t<_Dx2> = 0>
        explicit unique_ptr(pointer _Ptr) noexcept
            : _Mybase(_Ptr)
        {    // construct with pointer
        }
        
        // ...(余下源码省略)
    }

源码就不在此解读了,感兴趣可以去详细看看。源码中到处都是模板,需要有一定的模板编程基础才能看懂。

4.2、为什么unique_ptr的拷贝构造函数和复制函数被delete了?(面试题)

       现在C++岗位面试时,很多公司都喜欢问C++11新标准中的若干特性,所以大家在面试前需要详细看看C++11新特性。

       之前有个同事出去面试时,就被问到为什么unique_ptr的拷贝构造函数和复制函数被delete了?在unique_ptr的实现代码中可以看到:

unique_ptr(const unique_ptr&) = delete;
unique_ptr& operator=(const unique_ptr&) = delete;

其实很简单,因为unique_ptr是独占式的,不能进行拷贝,只能进行对象所有权的转移。拷贝构造函数与赋值函数进行的是拷贝操作,所以要将这两个函数禁用掉。

注意此处在函数后面添加“= delete”,是C++11新标准中新增的,是用来禁用对应的函数的。

4.3、使用unique_ptr独占式智能指针的实例

        使用unique_ptr智能指针的简单示例如下: 

#include <iostream>
#include <memory>

class LargeObject
{
public:
    void DoSomething(){}
};

void SmartPointerDemo()
{
    // Create the object and pass it to a smart pointer
    std::unique_ptr<LargeObject> pLarge(new LargeObject());

    //Call a method on the object
    pLarge->DoSomething();

    // Free the memory before we exit function block.
    pLarge.reset();

    // Do some other work...
}

5、共享式指针shared_ptr 

        shared_ptr是共享式智能指针,一个对象可以被多个shared_ptr共享。每个shared_ptr内部都维护一个引用计数,当引用计数为 0 时,所指向的对象会被销毁。std::shared_ptr 可以被拷贝和移动。

5.1、查看shared_ptr的源码实现片段

       可以在Visual Studio中输入shared_ptr,然后go到shared_ptr的定义处查看shared_ptr的源码实现,都是基于模板实现的:(下面给出部分shared_ptr的源码)

    // CLASS TEMPLATE shared_ptr
template<class _Ty>
    class shared_ptr
        : public _Ptr_base<_Ty>
    {    // class for reference counted resource management
private:
    using _Mybase = _Ptr_base<_Ty>;

public:
    using typename _Mybase::element_type;

#if _HAS_CXX17
    using weak_type = weak_ptr<_Ty>;
#endif /* _HAS_CXX17 */

    constexpr shared_ptr() noexcept
        {    // construct empty shared_ptr
        }

    constexpr shared_ptr(nullptr_t) noexcept
        {    // construct empty shared_ptr
        }

    template<class _Ux,
        enable_if_t<conjunction_v<conditional_t<is_array_v<_Ty>, _Can_array_delete<_Ux>, _Can_scalar_delete<_Ux>>,
            _SP_convertible<_Ux, _Ty>>, int> = 0>
        explicit shared_ptr(_Ux * _Px)
        {    // construct shared_ptr object that owns _Px
        _Setp(_Px, is_array<_Ty>{});
        }

    template<class _Ux,
        class _Dx,
        enable_if_t<conjunction_v<is_move_constructible<_Dx>,
            _Can_call_function_object<_Dx&, _Ux *&>,
            _SP_convertible<_Ux, _Ty>>, int> = 0>
        shared_ptr(_Ux * _Px, _Dx _Dt)
        {    // construct with _Px, deleter
        _Setpd(_Px, _STD move(_Dt));
        }

    template<class _Ux,
        class _Dx,
        class _Alloc,
        enable_if_t<conjunction_v<is_move_constructible<_Dx>,
            _Can_call_function_object<_Dx&, _Ux *&>,
            _SP_convertible<_Ux, _Ty>>, int> = 0>
        shared_ptr(_Ux * _Px, _Dx _Dt, _Alloc _Ax)
        {    // construct with _Px, deleter, allocator
        _Setpda(_Px, _STD move(_Dt), _Ax);
        }

    template<class _Dx,
        enable_if_t<conjunction_v<is_move_constructible<_Dx>,
            _Can_call_function_object<_Dx&, nullptr_t&>
        >, int> = 0>
        shared_ptr(nullptr_t, _Dx _Dt)
        {    // construct with nullptr, deleter
        _Setpd(nullptr, _STD move(_Dt));
        }

    template<class _Dx,
        class _Alloc,
        enable_if_t<conjunction_v<is_move_constructible<_Dx>,
            _Can_call_function_object<_Dx&, nullptr_t&>
        >, int> = 0>
        shared_ptr(nullptr_t, _Dx _Dt, _Alloc _Ax)
        {    // construct with nullptr, deleter, allocator
        _Setpda(nullptr, _STD move(_Dt), _Ax);
        }

    template<class _Ty2>
        shared_ptr(const shared_ptr<_Ty2>& _Right, element_type * _Px) noexcept
        {    // construct shared_ptr object that aliases _Right
        this->_Alias_construct_from(_Right, _Px);
        }

    shared_ptr(const shared_ptr& _Other) noexcept
        {    // construct shared_ptr object that owns same resource as _Other
        this->_Copy_construct_from(_Other);
        }
        
        // ...(余下源码省略)
}

5.2、shared_ptr的类图说明

       shared_ptr类内部实现类图如下:

shared_ptr类继承于_Ptr_base类,_Ptr_base类内部包含了指向外部对象的成员_Ptr和_Ref_count_base计数对象。根据外部调用shared_ptr的哪个构造函数,确定到底是new出_Ref_count_obj、_Ref_count或者_Ref_count_resource中哪个计数对象。类图很重要,类图可以表明各个相关类的关系,无论是写设计文档,还是学习源码,都需要使用到类图。

5.3、shared_ptr循环引用问题(面试题)

       使用shared_ptr可能会出现循环引用问题,场景是两个类中都包含了指向对方的shared_ptr对象,这样会导致new出来的两个类没有走析构,引发内存泄漏问题。

       循环引用问题的示意图如下:

相关代码如下:

#include <iostream>
#include<memory>
 
using namespace std;
 
class B;
class A{
    public:
    shared_ptr<B> bptr;
    ~A(){cout<<"~A()"<<endl;}
}
 
class B
{
    public:
    shared_ptr<A> aptr;
    ~B( ){cout<<"~B()"<<endl;}
}

 

int main() {
    shared_ptr<A> pa(new A()); // 引用加1
    shared_ptr<B> pb(new B()); // 引用加1
    pa->bptr = pb; // 引用加1
    pa->aptr = pa; // 引用加1
    return 0;
}

       执行到上述return 0这句代码时,指向A和B两个对象的引用计数都是2。当退出main函数时,先析构shared_ptr<B> pb对象,B对象的引用计数减1,B对象的引用计数还为1,所以不会delete B对象,不会进入B对象析构函数,所以B类中的shared_ptr<A> aptr成员不会析构,所以此时A对象的引用计数还是2。当析构shared_ptr<A> pa时,A的引用计数减1,A对象的引用计数变为1,所以不会析构A对象。所以上述代码会导致A和B两个new出的对象都没释放,导致内存泄漏。

       为了解决上述问题,引入了weak_ptr,可以将类中包含的shared_ptr成员换成weak_ptr,如下:

相关代码如下:

#include <iostream>
#include<nemory>
 
using namespace std;
 
class B;
class A{
    public:
    weak_ptr<B> bptr;  // 使用weak_ptr替代shared_ptr
    ~A(){cout<<"~A()"<<endl;}
}
 
class B
{
    public:
    weak_ptr<A> aptr; // 使用weak_ptr替代shared_ptr
    ~B( ){cout<<"~B()"<<endl;}
}

int main() {
    shared_ptr<A> pa(new A());
    shared_ptr<B> pb(new B());
    pa->bptr = pb;
    pa->aptr = pa;
    return 0;
}

5.4、使用shared_ptr的实例

        使用shared_ptr的实例代码如下:

#include <iostream>
#include <memory>
 
int main() 
{ 
    std::shared_ptr<int> sharedPtr1 = std::make_shared<int>(10);
    std::shared_ptr<int> sharedPtr2 = sharedPtr1;
    std::cout << *sharedPtr1 << std::endl; // 输出 10
    std::cout << *sharedPtr2 << std::endl; // 输出 10
 
    // 使用智能指针自动管理内存,不需要手动释放
    // 智能指针会自动更新引用计数
    sharedPtr1.reset();
 
    // 只有当所有引用都被释放后,内存才会被自动释放
    std::cout << "sharedPtr2 use count: " << sharedPtr2.use_count() << std::endl; // 输出 1
}

6、弱指针weak_ptr

        weak_ptr是为了配合shared_ptr而引入的一种智能指针,它不具有普通指针的行为,没有重载*和->两个操作符,它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况。

        weak_ptr可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。使用weak_ptr的成员函数use_count()可以观测资源的引用计数,另一个成员函数expired()的功能等价于use_count()==0,但更快,表示被观测的资源 (也就是shared_ptr的管理的资源)已经不复存在。weak_ptr可以使用一个非常重要的成员函数lock()从被观测的shared_ptr 获得一个可用的shared_ptr对象,从而操作资源。但当expired()==true的时候,lock()函数将返回一个存储空指针的shared_ptr。

6.1、查看weak_ptr的源码实现片段

       到Visual Studio中可以看到weak_ptr的源码实现:

// CLASS TEMPLATE weak_ptr
template<class _Ty>
    class weak_ptr
        : public _Ptr_base<_Ty>
    {    // class for pointer to reference counted resource
public:
    constexpr weak_ptr() noexcept
        {    // construct empty weak_ptr object
        }

    weak_ptr(const weak_ptr& _Other) noexcept
        {    // construct weak_ptr object for resource pointed to by _Other
        this->_Weakly_construct_from(_Other);
        }

    template<class _Ty2,
        enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>
        weak_ptr(const shared_ptr<_Ty2>& _Other) noexcept
        {    // construct weak_ptr object for resource owned by _Other
        this->_Weakly_construct_from(_Other);
        }

    template<class _Ty2,
        enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>
        weak_ptr(const weak_ptr<_Ty2>& _Other) noexcept
        {    // construct weak_ptr object for resource pointed to by _Other
        this->_Weakly_construct_from(_Other.lock());
        }

    weak_ptr(weak_ptr&& _Other) noexcept
        {    // move construct from _Other
        this->_Move_construct_from(_STD move(_Other));
        }

    template<class _Ty2,
        enable_if_t<_SP_pointer_compatible<_Ty2, _Ty>::value, int> = 0>
        weak_ptr(weak_ptr<_Ty2>&& _Other) noexcept
        {    // move construct from _Other
        this->_Weakly_construct_from(_Other.lock());
        _Other.reset();
        }

    ~weak_ptr() noexcept
        {    // release resource
        this->_Decwref();
        }

    weak_ptr& operator=(const weak_ptr& _Right) noexcept
        {    // assign from _Right
        weak_ptr(_Right).swap(*this);
        return (*this);
        }

    template<class _Ty2>
        weak_ptr& operator=(const weak_ptr<_Ty2>& _Right) noexcept
        {    // assign from _Right
        weak_ptr(_Right).swap(*this);
        return (*this);
        }
        
        // ...(余下源码省略)
}

6.2、使用weak_ptr的实例

#include <iostream>  
using namespace std;  
#include <memory>  
  
class B;  
  
class A  
{  
public:  
    weak_ptr<B> ptrA_B;  // 弱类型指针
public:  
    A()  
    {  
        cout << "调用class A的构造函数" << endl;  
    }  
    ~A()  
    {  
        cout << "调用class A的析构函数" << endl;  
    }  
};  
  
class B  
{  
public:  
    weak_ptr<A> ptrB_A;  // 弱类型指针
public:  
    B()  
    {  
        cout << "调用class B的构造函数" << endl;  
    }  
    ~B()  
    {  
        cout << "调用class B的析构函数" << endl;  
    }  
};  
  
int main()  
{  
    shared_ptr<A> ptrA = make_shared<A>();
    shared_ptr<B> ptrB = make_shared<B>();  
    ptrA->ptrA_B = ptrB;  
    ptrB->ptrB_A = ptrA;  
}  

上述代码运行的输出结果是:

调用class B的构造函数
调用class A的构造函数
调用class A的析构函数
调用class B的析构函数

7、使用智能指针是否会影响到程序的执行效率?

       使用智能指针不会影响到程序的执行效率。智能指针的设计原则是在内存和性能上尽可能高效。 例如,unique_ptr中的唯一数据成员是封装的指针。 从内存占用上看,unique_ptr 与该指针的大小完全相同,不是四个字节就是八个字节。从性能上看,使用重载了 * 和 ->运算符的智能指针访问封装指针的速度不会明显慢于直接访问原始指针的速度。

8、有必要学习C++11标准中的新特性

       2011年发布的C++11新标准,给C++引入了大量的新特性,是C++发展史上一次里程碑式的更新,开启了现代C++的时代!unique_ptr、shared_ptr和weak_ptr智能指针就是在这次更新中引入的!

       为什么说我们很有必要学习C++11标准中的新特性呢?主要从两点来看。一方面,现在很多公司在招聘C++开发人员时会频繁地问到C++11的新特性另一方面,很多开源代码在频繁地使用C++11的新特性,比如很多公司都在用的开源WebRTC库,就大量地使用到了C++11及C++14中的新特性,我们要阅读这些开源代码,必须要了解C++新特性。

       C++新标准引入的新特性,使得C++变得更加灵活,但也使得C++变得更加臃肿,更加难以驾驭!C++这些新特性,能真正驾驭的人并不多,所以为了保证代码的可读性与可维护性,在一般公司的项目代码中C++新特性用的并不多,一般只会用到少部分特性,比如一些新增的关键字和匿名函数(lamda表达式)等。

9、最后

       本文详细地介绍了C++11标准中引入的智能指针unique_ptr、shared_ptr和weak_ptr基础部分的内容,希望能给大家提供的一定的借鉴和参考。后面将介绍在代码中如何有效地使用这些智能指针。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java网络开发(Tomcat)—— Servlet学习 Web相关背景知识 JavaWeb项目初步

本文目录 引出〇、域名、IP、端口一、软件架构BS和CS二、实现Web服务的条件和步骤三、Tomcat搭建Web项目初步1.pom.xml文件配置2.web.xml文件更新3.Tomcat运行环境配置4.项目文件层级解析 四、JavaWeb项目文件分类&#xff08;1&#xff09;静态文件—存放位置&#xff08;2&am…

今天面了个字节跳动拿30k出来的测试大佬,让我见识到了什么是天花板

2022年堪称大学生就业最难的一年&#xff0c;应届毕业生人数是1076万。失业率超50%&#xff01; 但是我观察到一个数据&#xff0c;那就是已经就业的毕业生中&#xff0c;计算机通信等行业最受毕业生欢迎&#xff01; 计算机IT行业薪资高&#xff0c;平均薪资是文科其他岗位的…

【Linux】常用命令的汇总学习

文章目录 1.目录切换命令2.目录操作命令3.把ls -l中包含字母file&#xff08;不区分大小写&#xff09;的内容输出4.统计txt中的某个字符串5.grep命令的使用6.linux查找当前目录下所有txt文件7.linux中的find命令8.查看系统所有的进程信息9.如何确定文件的类型10.tar解压缩11.U…

Java数据驱动:CData JDBC Drivers 2022 Crack

JDBC 驱动程序 易于使用的 JDBC 驱动程序&#xff0c;具有强大的企业级功能 无与伦比的性能和可扩展性。 对实时数据的简单 JDBC/SQL 访问。 从流行的 BI 工具访问实时数据。 集成到流行的 IDE 中。 CData JDBC Drivers Software 是领先的数据访问和连接解决方​​案提供商。我…

如何更改 Linux 文件和目录权限?

在Linux系统中&#xff0c;文件和目录权限是安全性和访问控制的关键组成部分。正确设置文件和目录的权限可以确保只有授权的用户能够读取、写入或执行这些文件和目录。 本文将详细介绍如何在Linux系统中更改文件和目录的权限。 1. 文件和目录权限概述 在Linux系统中&#xff…

【sentinel】漏桶算法在Sentinel中的应用

漏桶算法介绍 漏桶算法&#xff0c;又称leaky bucket。 从图中我们可以看到&#xff0c;整个算法其实十分简单。首先&#xff0c;我们有一个固定容量的桶&#xff0c;有水流进来&#xff0c;也有水流出去。对于流进来的水来说&#xff0c;我们无法预计一共有多少水会流进来&am…

AUTOSAR通信篇 - CAN网络通信(二:CanIf)

目录 初始化 数据发送 请求发送 发送数据流 发送缓存 发送确认 数据接收 数据接收提醒 读取接收数据 CAN控制器模式 控制器模式转换 唤醒 PDU通道模式控制 PDU通道组 PDU通道模式 总结 在上一篇&#xff0c;我们介绍了CAN模块&#xff0c;接下来我们介绍在CAN模…

基于html+css的图展示96

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

ts报错“this“ 隐式具有类型 “any“,因为它没有类型注释。解决方案

序 1、参考博文》①严格模式 - 知乎&#xff0c;②ts的tsconfig.son中文说明③TypeScript Number | 菜鸟教程 2、解决&#xff08;ts报错“this“ 隐式具有类型 “any“&#xff0c;因为它没有类型注释。&#xff09; 3、解决&#xff08;函数内this是undefined 的问题&#xf…

STM32CubeIDE + HAL + STM32f103C8T6 系列教程1 ---板载PC13LED闪烁

STM32CubeIDE HAL STM32f103C8T6 系列教程1 --- 板载PC13LED闪烁 引言硬件关于开发板[^2]控制器内置存储器原理图 硬件连线硬件连接表硬件连线图 软件STM32CubeIDE下载及安装Stm32CubeIDE设置补全快捷键和主题新建一个工程选择开发板核心芯片型号设置工程相关参数STM32CubeMX…

《程序员面试金典(第6版)》面试题 02.08. 环路检测(哈希法,双指针,检测链表是否有环)

题目描述 给定一个链表&#xff0c;如果它是有环链表&#xff0c;实现一个算法返回环路的开头节点。若环不存在&#xff0c;请返回 null。 题目传送门&#xff1a;面试题 02.08. 环路检测 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链…

【笔记整理】常见聚类算法

【笔记整理】常见聚类算法 文章目录 【笔记整理】常见聚类算法一、均值偏移 - Mean-shift&#xff08;★★★★&#xff09;1、概述 & 图解&#xff08;“偏心”&#xff09;2、公式 & 步骤1&#xff09;基本公式&#xff08;“偏移量更新圆心”&#xff09;2&#xff…

4:File类与IO流

文章目录 File类1&#xff1a;引入&#xff1a;2&#xff1a;对文件进行操作3&#xff1a;对目录/文件夹进行操作 IO流1&#xff1a;引入&#xff1a;2&#xff1a;字符输入 / 出流FileReader 与 FileWriter3&#xff1a;用try - catch - finally 处理异常4&#xff1a;几个常见…

【Android-JetpackCompose】13、实战在线课程 App

文章目录 一、BottomNavigation 底部导航1.1 底部导航栏的布局、点击1.2 设置 bottomBar 的颜色1.3 设置顶部 actionBar 的颜色 二、主页 StudyScreen2.1 顶部状态栏2.2 一、BottomNavigation 底部导航 1.1 底部导航栏的布局、点击 首先&#xff0c;构造 NavigationItem 的 d…

c++—断言、异常

一、 断言&#xff0c;主要用于在函数入口处进行参数检查&#xff0c;是否符合参数设置要求&#xff1b; &#xff08;1&#xff09;true&#xff1a;继续执行&#xff1b;false&#xff1a;终止运行&#xff1b; &#xff08;2&#xff09;特点&#xff1a;在程序运行时才能起…

【20】SCI易中期刊推荐——计算机信息系统工程电子与电气(中科院3区)

💖💖>>>加勒比海带,QQ2479200884<<<💖💖 🍀🍀>>>【YOLO魔法搭配&论文投稿咨询】<<<🍀🍀 ✨✨>>>学习交流 | 温澜潮生 | 合作共赢 | 共同进步<<<✨✨ 📚📚>>>人工智能 | 计算机视觉…

zookeeper学习笔记

zookeeper Zookeeper 入门概述Zookeeper工作机制特点数据结构应用场景统一命名服务统一配置管理统一集群管理服务器动态上下线软负载均衡 zookeeper安装本地模式安装配置参数解读 Zookeeper 集群操作集群操作集群安装 选举机制节点类型客户端命令行操作命令语法znode 节点数据信…

mysql 是否包含 返回索引 截取字符串

是否包含返回索引 原文链接&#xff1a;https://www.cnblogs.com/shoshana-kong/p/16474175.html 方法1&#xff1a;使用通配符%。 通配符也就是模糊匹配&#xff0c;可以分为前导模糊查询、后导模糊查询和全导匹配查询&#xff0c;适用于查询某个字符串中是否包含另一个模糊…

Redis的全局命令及相关误区

Redis中所说的数据结构是针对key-value中的value而言的。主要的结构包括String、哈希表、列表、集合等等在redis中存在16个库&#xff0c;涉及到后期的集群搭建只能使用0号库最为方便 查看所有键&#xff08;支持通配符&#xff09; keys * keys S*返回当前数据库中的键总数 …

智能出行更安全,亚马逊云科技携手木卫四助汽车客户安全合规出海

木卫四&#xff08;北京&#xff09;科技有限公司在汽车网络安全领域拥有独特专业知识&#xff0c;其融合人工智能算法的安全检测引擎可以不依赖车辆中安装的代理软件&#xff0c;只需几周即可快速部署实施&#xff0c;是汽车网络安全领域的技术领先者。 在亚马逊云科技初创团…