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

news2025/1/9 1:57:48

目录

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、使用shared_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、使用shared_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基础部分的内容,希望能给大家提供的一定的借鉴和参考。后面将介绍在代码中如何有效地使用这些智能指针。

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

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

相关文章

【C++】——vector的介绍及模拟实现

文章目录 1. 前言2. vector的介绍3. vector的常用接口3.1 vector对象的常见构造函数3.2 iterator的使用3.3 vector的空间管理3.4 vector的增删查改 4. vector迭代器失效的问题4.1 底层空间改变的操作4.2 指定位置元素的删除操作 5. vector模拟实现6. 结尾 1. 前言 上一篇文章我…

K210入门-环境搭建与点灯测试(一)

目录 1、简介 2、资质查找 3、IDE下载安装 4、测试程序 4.1 测序复制 4.2 开发板选择 4.3 链接 4.4 效果展示 1、简介 本文主要针对小白使用K210进行入门&#xff0c;以及自己学习的总结与笔记使用。本文主要进行环境搭建与点灯测试。 2、资质查找 首先去官网进行资料下…

Flume系列:Flume数据监控Ganglia

目录 Apache Hadoop生态-目录汇总-持续更新 安装说明 1&#xff09;安装 ganglia 2&#xff09;在 worker213 修改配置文件 3&#xff09;在 所有服务器 修改配置文件/etc/ganglia/gmond.conf 4&#xff09;启动 ganglia 5&#xff09;打开网页浏览 ganglia 页面 6&…

《UVM 实战》 代码下载, 无需注册

法一&#xff1a; https://www.hzcourse.com/web/refbook/detail/5651/229 法二&#xff1a; https://www.hzcourse.com/oep/resource/access/L29wZW5yZXNvdXJjZXMvdGVhY2hfcmVzb3VyY2UvZmlsZS8yMDE3LzEwL2IyMDE0OTFmMmUxMjdkNTM2YjhmMjBmNWUzMTRhMjE3Lmd6JGV4YW1wbGVfYW5kX3…

如何在华为OD机试中获得满分?Java实现【报数游戏】一文详解!

✅创作者:陈书予 🎉个人主页:陈书予的个人主页 🍁陈书予的个人社区,欢迎你的加入: 陈书予的社区 🌟专栏地址: Java华为OD机试真题(2022&2023) 文章目录 1. 题目描述2. 输入描述3. 输出描述4. Java算法源码5. 测试6.解题思路1. 题目描述 100个人围成一圈,每个人…

Redis数据库简介

1.Redis数据库介绍 Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库&#xff0c;并提供多种语言的API。 2.Redis数据库特性 Redis支持数据的持久化&#xff0c;可以将内存中的数据保存在磁盘中&#xff0c;重启的时候可以再次加…

2023 华为 Datacom-HCIE 真题题库 09--含解析

单项选择题 1.[试题编号&#xff1a;190485] &#xff08;单选题&#xff09;华为交换机MAC地址表的老化时间默认是多少秒? A、500 B、5 C、300 D、400 答案&#xff1a;C 解析&#xff1a;无 2.[试题编号&#xff1a;190484] &#xff08;单选题&#xff09;如图所示&#…

数据分析之Pandas--数据检索

数据分析之Pandas&#xff08;03&#xff09;--数据检索 pandas的数据检索功能是其最基础也是最重要的功能之一。 pandas中最常用的几种数据过滤方式如下&#xff1a; 1. 行列过滤&#xff1a;选取指定的行或者列 2. 条件过滤&#xff1a;对列的数据设置过滤条件 3. 函数过…

提升PostGIS大范围、大数据量分区几何裁剪统计查询速度技巧

PostGIS是在GIS系统开发中常用的开源空间数据库&#xff0c;使用PostGIS进行大范围、大数据量的几何裁剪操作时&#xff0c;耗时较长。 当我遇到需要按区县或选中的乡镇&#xff0c;计算展示林规、土地报批等多个规划数据的面积等&#xff0c;此时需要使用规划数据叠加行政界线…

几句命令搞定一个es:docker安装elasticsearch+可视化kibana

docker安装elasticsearch可视化kibana 写在前面es安装&#xff1a;docker安装elasticsearches搜索&#xff1a;安装elasticsearch插件IK分词器es可视化&#xff1a;docker安装kibana最后 写在前面 从自己知道es开始到写这篇文章差不多也有5年左右的时间了吧&#xff0c;之前总…

FastReport.Net FastReport.Core 2023.2.15 Crack

快速报告.NET .NET 7 的报告和文档创建库 FastReport.Net & FastReport.Core适用于 .NET 7、.NET Core、Blazor、ASP.NET、MVC 和 Windows 窗体的全功能报告库。它可以在 Microsoft Visual Studio 2022 和 JetBrains Rider 中使用。 快速报告.NET 利用 .NET 7、.NET Core、…

C++程序设计基础【一】

C程序设计基础【一】 一、一个程序的开发步骤1.编辑程序2.编译程序3.链接程序4.执行程序5.测试 2.基础代码解读1.预处理指令(#include <iostream>)2.块注释(/* */)3.行注释(//)4.using namespace std5.int main()6.{}7.std::cin、std::cout、std::endl8.return 0 二、变量…

云上高校导航 开发指引 与 注意事项

&#x1f52c; 注意事项 大部分数据存储在utils.js中的&#xff0c;页面通过引入utils.js方式渲染数据 图标全部存储在项目images文件夹里,均下载自 iconfont网站&#xff08;自行替换&#xff09; 部分图片引用自 免费图床 - CDN加速图床&#xff08;自行替换&#xff09; …

七年程序员的三四月总结:三十岁、准备婚礼、三次分享

你好&#xff0c;我是 shixin&#xff0c;一名工作七年的安卓开发。 每两个月我会做一次总结&#xff0c;记下这段时间里有意义的事和值得反复看的内容&#xff0c;为的是留一些回忆、评估自己的行为、沉淀有价值的信息。 一转眼 2023 年过去了三分之一&#xff0c;这两个月经…

响应式编程实战:Spring WebFlux集成MongoDB和Swagger

1 缘起 新的项目&#xff0c;快速迭代&#xff0c; 技术选型&#xff1a;Spring WebFlux&#xff0c; 非Spring MVC&#xff0c; 之前没有接触过Spring WebFlux&#xff0c;项目中都是使用Spring MVC&#xff0c; 这次学到了新的知识Spring WebFlux&#xff0c;记录下。 2 Sp…

C++实现哈希表

文章目录 前言1.哈希表的相关介绍2.哈希表的实现1.开放定址法实现哈希表1.插入2.查找3.删除 2.链地址法(开链法)实现哈希表1.插入节点2.查找3.删除4.相关的一些补充 3.封装unordered_map与unordered_set1.封装前的改造2.迭代器的实现3.unordered_map和unordered_set复用 前言 …

60题学会动态规划系列:动态规划算法第一讲

坚持就是胜利 - - 文章目录 1.第N个泰波那切数 2.三步问题 3.使用最小花费爬楼梯 4.解码方法 1.第N个泰波那切数 力扣链接&#xff1a;力扣 泰波那契序列 Tn 定义如下&#xff1a; T0 0, T1 1, T2 1, 且在 n > 0 的条件下 Tn3 Tn Tn1 Tn2 给你整数 n&#xff0c…

多线程 -- 线程安全问题(3)

本篇重点: 总结线程安全问题的原因以及解决办法 目录 synchronized 加锁关键字join 和 synchronized 的区别volatile 关键字 在上一篇中我们介绍了Thread类的基本使用方法, 本篇将会介绍有关于线程的安全问题 线程不安全的原因: 抢占式执行(罪魁祸首, 万恶之源) 多个线程修改同…

搜索推荐系统[10]项目实战系列Z5:汽车说明书跨模态智能问答系统,针对汽车说明书(可自定义文档)进行自动问答,采用了OCR、RocketQA等技术

搜索推荐系统专栏简介:搜索推荐全流程讲解(召回粗排精排重排混排)、系统架构、常见问题、算法项目实战总结、技术细节以及项目实战(含码源) 专栏详细介绍:搜索推荐系统专栏简介:搜索推荐全流程讲解(召回粗排精排重排混排)、系统架构、常见问题、算法项目实战总结、技术…

小程序之页面通信派发通知

文章目录 1. 介绍小程序页面通信的概念解释小程序页面通信的意义和必要性介绍小程序页面通信的方法 2. 小程序页面通信的实现示例通过事件传递数据实现页面之间通信通过全局变量实现页面之间通信 3. 实现小程序页面之间的消息通知介绍小程序发布订阅模式的概念使用事件订阅-发布…