【C++】21.智能指针

news2024/12/27 1:36:46

1.为什么需要智能指针

C++无gc new/malloc出来的资源 是需要我们去手动释放

1.忘记释放

2.发生异常安全问题

new/malloc

fun()://throw 异常

delete/free

最终都导致资源的泄漏

利用智能指针更好的去解决此类问题

2.智能指针

1°RAII

RAII(Resource Acquisition Is Initialization)是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。

在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此,我们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:

  • 不需要显式地释放资源。
  • 采用这种方式,对象所需的资源在其生命期内始终保持有效。

2°智能指针的原理

SmartPtr.h

//RAII + 像指针一样
template<class T>
class SmartPtr
{
public:
    SmartPtr(T* ptr)
        :_ptr(ptr)
    {}

    ~SmartPtr()
    {
        if (_ptr)
        {
            cout << "delete:" << _ptr << endl;
            delete _ptr;
            //制空解决不了
            //sp2还是正常指向
        }
    }

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

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

private:
    T* _ptr;
};
#include "SmartPtr.h"

//-------------------
int div()
{
    int a, b;
    cin >> a >> b;
    if (b == 0)
        throw invalid_argument("除0错误");

    return a / b;
}

void f1()
{
    //智能指针
    //法1:
    //int* p = new int;
    //SmartPtr<int> sp(p);
    //--------------------
    //无论是函数正常结束 还是抛异常 都会导致sp对象的
    //生命周期到了以后 调用析构函数
    //相当于类帮我们管理资源的释放
    //RAII是一种利用对象生命周期来控制程序资源
    //RAII与智能指针的关系
    //RAII是一种托管资源的思考 智能指针是依靠这种RAII实现的
    //unique_lock/lock_guard也是
    //法2:
    //需要重载operator*和operator-> 像指针一样
    SmartPtr<int> sp1(new int);
    *sp1 = 10;

    SmartPtr<pair<int, int>> sp2(new pair<int, int>);
    sp2->first = 20;
    sp2->second = 30;

    cout << div() << endl;
}
//抛异常
int main()
{
    try
    {
        f1();
    }

    catch (exception& e)
    {
        cout << e.what() << endl;
    }

    return 0;
}

智能指针有坑

#include "SmartPtr.h"

int main()
{
    //指向同一块资源就被释放两次
    SmartPtr<int> sp1(new int);
    SmartPtr<int> sp2 = sp1;
    return 0;
}

同一块空间被释放两次

有三种解决方法:

  • 管理权转移
  • 防拷贝
  • 引用计数

3°auto_ptr

管理权转移 早期设计缺陷 一般公司都明令禁止使用它

缺陷:ap2 = ap1场景下ap1就制空了 访问就会报错 如果不熟悉它的特性就会被坑

说白了就是ap2指向了ap1的空间 然后ap1制空

namespace szh
{
    template<class T>
    class auto_ptr
    {
    public:
        auto_ptr(T* ptr)
            :_ptr(ptr)
        {}

        auto_ptr(auto_ptr<T>& ap)
            :_ptr(ap._ptr)
        {
            ap._ptr = nullptr;
            //给你了 我不管了 直接制空
        }

        //赋值 sp1 = sp2
        auto_ptr<T>& operator=(const auto_ptr <T>& ap)
        {
            if (this != &ap)
            {
                if (_ptr)
                    delete _ptr;

                //赋给你后 我就制空
                _ptr = ap._ptr;
                ap._ptr = nullptr;
            }
        }

        ~auto_ptr()
        {
            if (_ptr)
            {
                cout << "delete:" << _ptr << endl;
                delete _ptr;
                //制空解决不了
                //sp2还是正常指向
            }
        }

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

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

    private:
        T* _ptr;
    };
}
#include "SmartPtr.h"

int main()
{
    szh::auto_ptr<int> ap1(new int);
    szh::auto_ptr<int> ap2 = ap1;
}

但是 如果访问ap1的话就会崩溃

#include "SmartPtr.h"

int main()
{
    szh::auto_ptr<int> ap1(new int);
    szh::auto_ptr<int> ap2 = ap1;
    *ap1 = 1; //悬空崩溃
    return 0;
}

闪光标

auto_ptr的缺陷挺大的 基本不会使用

4°unique_ptr

C++11 unique_ptr

防拷贝 推荐使用 简单粗暴

缺陷:如果有需要拷贝的场景 就没法使用

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

    //拷贝构造和operator=直接搞成删除函数
        unique_ptr(unique_ptr<T>& up) = delete;
        unique_ptr<T>& operator=(unique_ptr<T>& up) = delete;

        ~unique_ptr()
        {
            if (_ptr)
            {
                cout << "delete:" << _ptr << endl;
                delete _ptr;
                //制空解决不了
                //sp2还是正常指向
            }
        }

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

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

    private:
        T* _ptr;
    };
}
#include "SmartPtr.h"

int main()
{
    szh::unique_ptr<int> up1(new int);
    szh::unique_ptr<int> up2(up1);//拷贝的时候直接编译不过
    return 0;
}

5°shared_ptr

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

shared_ptr在其内部,给每个资源都维护了一份计数,用来记录该份资源被几个对象共享。

在对象被销毁时(也就是析构函数调用),就说明自己不使用该资源了,对象的引用计数减1

如果引用计数是0,就说明自己是最后一个使用该资源的对象,必须释放该资源;

如果不是0,就说明除了自己还有其他对象在使用该份资源,不能释放该资源,否则其他对象就成野指针了。

计数:

  • 如果使用普通count 不会释放 sp1 sp2都会减到1 希望的是引用一个计数

  • 如果使用静态的 static count可以正常释放 但遇到下面情况还是有问题

    szh::shared_ptr sp1(new int);

    szh::shared_ptr sp2(sp1);

    szh::shared_ptr sp3(new int);

    应该释放两次 但只释放一次

    此时s3自己管理一块资源空间 希望有两个计数

    因为静态只有一个计数

  • 最后使用指针可以解决问题

namespace szh
{
    template<class T>
    class shared_ptr
    {
    public:
        shared_ptr(T* ptr = nullptr)
            :_ptr(ptr)
            ,_pcount(new int(1))
            ,_pmtx(new mutex)
        {}

        shared_ptr(shared_ptr<T>& sp)
            :_ptr(sp._ptr)
            , _pcount(sp._pcount)
            ,_pmtx(sp._pmtx)
        {
            //++(*_pcount);
            add_ref_count();
        }
        
        //sp1 = sp4
        shared_ptr<T>& operator=(shared_ptr<T>& sp)
        {
            if (this != &sp)
            {
                //--引用计数 如果我是最后一个管理资源的对象 则释放资源
                //if (--(*_pcount) == 0)
                //{
                //	delete _pcount;
                //	delete _ptr;
                //}
                release();

                //共同管理后++
                _ptr = sp._ptr;
                _pcount = sp._pcount;
                _pmtx = sp._pmtx;
                //++(*_pcount);
                add_ref_count();
            }
            
            return *this;
        }

        void add_ref_count()
        {
            _pmtx->lock();
            ++(*_pcount);
            _pmtx->unlock();
        }

        void release()
        {
            _pmtx->lock();
            bool flag = false;
            if (--(*_pcount) == 0)
            {
                if (_ptr)
                {
                    cout << "delete:" << _ptr << endl;
                    delete _ptr;
                    //制空解决不了
                    //sp2还是正常指向
                    _ptr = nullptr;
                }
                //注意释放
                delete _pcount;
                _pcount = nullptr;
                flag = true;
            }
            _pmtx->unlock();

            if (flag == true)
            {
                delete _pmtx;
                _pmtx = nullptr;
            }
        }

        ~shared_ptr()
        {
            //if (--(*_pcount) == 0 && _ptr)
            //{
            //	cout << "delete:" << _ptr << endl;
            //	delete _ptr;
            //	//制空解决不了
            //	//sp2还是正常指向
            //	_ptr = nullptr;

            //	//注意释放
            //	delete _pcount;
            //	_pcount = nullptr;
            //}
            release();
        }

        int use_count()
        {
            return *_pcount;
        }

        T* get_ptr() const
        {
            return _ptr;
        }

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

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

    private:
        T* _ptr;

        int* _pcount;//记录有多少个对象一起共享管理资源 最后一个析构释放资源

        mutex* _pmtx;//解决线程安全问题
    };
}
#include "SmartPtr.h"

int main()
{
    szh::shared_ptr<int> sp1(new int);
    szh::shared_ptr<int> sp2(sp1);
    szh::shared_ptr<int> sp3(new int);
    szh::shared_ptr<int> sp4(sp3);
    szh::shared_ptr<int> sp5(sp3);

    sp1 = sp4;
    //sp1不再管理原来的资源 与sp4共同管理一个资源
    //sp1管理资源的计数-1 sp4管理资源的计数+1
    return 0;
}

 

shared_ptr有线程安全问题

智能指针对象中引用计数是多个智能指针对象共享的,两个线程中智能指针的引用计数同时++或--,这个操作不是原子的,引用计数原来是1,++了两次,可能还是2.这样引用计数就错乱了。会导致资源未释放或者程序崩溃的问题。所以只能指针中引用计数++、--是需要加锁的,也就是说引用计数的操作是线程安全的。

智能指针管理的对象存放在堆上,两个线程中同时去访问,会导致线程安全问题。

需要加锁

shared+str是否是线程安全的

答:注意这里问题的shared_ptr对象拷贝和析构++/--引用计数是否是安全的

库的实现中是安全的

#include "SmartPtr.h"

#include <thread>

int main()
{
    szh::shared_ptr<int> sp(new int);
    cout << sp.use_count() << endl;
    int n = 10000;
    
    //一次拷贝构造问题不大 多了就有问题了
    std::thread t1([&]() {
        //szh::shared_ptr<int> sp1(sp);
        for (int i = 0; i < n; ++i)
        {
            szh::shared_ptr<int> sp1(sp);
        }
    });

    std::thread t2([&]() {
        //szh::shared_ptr<int> sp2(sp);
        for (int i = 0; i < n; ++i)
        {
            szh::shared_ptr<int> sp2(sp);
        }
    });

    t1.join();
    t2.join();

    //析构后回到1
    cout << sp.use_count() << endl;
    return 0;
}

 

shared_ptr会有循环引用问题 使用弱指针来解决

循环引用:

  • node1和node2两个智能指针对象指向两个节点,引用计数变成1,我们不需要手动delete。
  • node1的_next指向node2,node2的_prev指向node1,引用计数变成2。
  • node1和node2析构,引用计数减到1,但是_next还指向下一个节点。但是_prev还指向上一个节点。
  • 也就是说_next析构了,node2就释放了。
  • 也就是说_prev析构了,node1就释放了。
  • 但是_next属于node的成员,node1释放了,_next才会析构,而node1由_prev管理,_prev属于node2成员,所以这就叫循环引用,谁也不会释放。

此时引入弱指针

当调用拷贝构造的时候 直接返回 不进行++

namespace szh
{
    template<class T>
    class shared_ptr
    {
    public:
        shared_ptr(T* ptr = nullptr)
            :_ptr(ptr)
            ,_pcount(new int(1))
            ,_pmtx(new mutex)
        {}

        shared_ptr(shared_ptr<T>& sp)
            :_ptr(sp._ptr)
            , _pcount(sp._pcount)
            ,_pmtx(sp._pmtx)
        {
            //++(*_pcount);
            add_ref_count();
        }
        
        //sp1 = sp4
        shared_ptr<T>& operator=(shared_ptr<T>& sp)
        {
            if (this != &sp)
            {
                //--引用计数 如果我是最后一个管理资源的对象 则释放资源
                //if (--(*_pcount) == 0)
                //{
                //	delete _pcount;
                //	delete _ptr;
                //}
                release();

                //共同管理后++
                _ptr = sp._ptr;
                _pcount = sp._pcount;
                _pmtx = sp._pmtx;
                //++(*_pcount);
                add_ref_count();
            }
            
            return *this;
        }

        void add_ref_count()
        {
            _pmtx->lock();
            ++(*_pcount);
            _pmtx->unlock();
        }

        void release()
        {
            _pmtx->lock();
            bool flag = false;
            if (--(*_pcount) == 0)
            {
                if (_ptr)
                {
                    cout << "delete:" << _ptr << endl;
                    delete _ptr;
                    //制空解决不了
                    //sp2还是正常指向
                    _ptr = nullptr;
                }
                //注意释放
                delete _pcount;
                _pcount = nullptr;
                flag = true;
            }
            _pmtx->unlock();

            if (flag == true)
            {
                delete _pmtx;
                _pmtx = nullptr;
            }
        }

        ~shared_ptr()
        {
            //if (--(*_pcount) == 0 && _ptr)
            //{
            //	cout << "delete:" << _ptr << endl;
            //	delete _ptr;
            //	//制空解决不了
            //	//sp2还是正常指向
            //	_ptr = nullptr;

            //	//注意释放
            //	delete _pcount;
            //	_pcount = nullptr;
            //}
            release();
        }

        int use_count()
        {
            return *_pcount;
        }

        T* get_ptr() const
        {
            return _ptr;
        }

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

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

    private:
        T* _ptr;

        int* _pcount;//记录有多少个对象一起共享管理资源 最后一个析构释放资源

        mutex* _pmtx;//解决线程安全问题
    };
    
    //弱指针
    //严格来说weak_ptr不是智能指针 因为他没有RAII资源管理机制
    //专门解决shared_ptr的循环引用问题

    template<class T>
    class weak_ptr
    {
    public:
        weak_ptr() = default;

        weak_ptr(const shared_ptr<T>& sp)
            :_ptr(sp.get_ptr())
        {}
        
        weak_ptr<T>& operator=(const shared_ptr<T>& sp)
        {
            _ptr = sp.get_ptr();
            return *this;
        }

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

        T* operator->()
        {
            return _ptr;
        }
    private:
        T* _ptr;
    };
}
#include "SmartPtr.h"

struct ListNode
{
    int val;
    //ListNode* _prev;
    //ListNode* _next;
    //szh::shared_ptr<ListNode> _spnext;
    //szh::shared_ptr<ListNode> _spprev;
    //弱指针
    szh::weak_ptr<ListNode> _spprev;
    szh::weak_ptr<ListNode> _spnext;

    ~ListNode()
    {
        cout << "~ListNode()" << endl;
    }
};

int main()
{
    //可正常释放
    szh::shared_ptr<ListNode> spn1(new ListNode);
    szh::shared_ptr<ListNode> spn2(new ListNode);

    //为什么编译不过
    //spn1->_next = spn2;
    //spn2->_prev = spn1;
    //这里next prev是原生指针
    //next和prev改成智能指针即可
    //-------------------
    //改了之后 加上这两句 无法正常释放
    //spn1->_spnext = spn2;
    //spn2->_spprev = spn1;
    //此时发生了循环引用问题
    //引用计数都变成了2
    //spn1 spn2析构后 引用计数都变成1
    //我的释放由你管理着 你的释放由我管理着
    //互相管理对方的空间
    //就像我抓着你的头发 你抓着我的头发 两人互相抓着 
    //谁也不愿意放手 最后就互相伤害
    //那么如何解决?
    //使用weak_ptr 不增加引用计数

    cout << spn1.use_count() << endl;
    cout << spn2.use_count() << endl;

    spn1->_spnext = spn2;
    spn2->_spprev = spn1;

    cout << spn1.use_count() << endl;
    cout << spn2.use_count() << endl;

    return 0;
}

6°智能指针的发展历史

智能指针的历史

C++没有gc(垃圾回收器) 申请的资源需要释放是一个问题 尤其是碰到异常安全问题 特别难处理

稍不注意就会出现内存泄漏 内存泄漏导致程序可用的内存越来越少 程序中很多操作都是需要内存的

那么会导致程序基本处于瘫痪状态 所以我们尽量要杜绝内存泄漏问题

所以就发展处于基于RAII思想的智能指针 但是由于没有gc的坑 引入智能指针

而智能指针经历了十几年发展的坑爹血泪史

第一阶段:

C++98中首次退出了auto_ptr 但是auto_ptr的设计存在重大缺陷 不建议使用

第二阶段:

C++官方在接下来的十几年中没有作为 有一帮牛人就生气了 觉得C++的库太简陋了 所以自己搞一个非官方社区 写了一个库叫boost boost库中就重新写了智能指针(注意boost库中其他很多其他实现的东西)_

scoped_ptr/scoped_array 防拷贝版本

scoped_ptr/scoped_array 引用计数版本

scoped/shared->new 析构delete

scoped_array/shared_array-> new[] 析构 delete[] 重载的是operator[]

weak_ptr

第三阶段:

C++11中引入智能指针 参考了boost的实现 微改了一下

其实C++11其他类似右值引用移动语句等等也是参考boost

unique_ptr(参考的scoped_ptr)

shared_ptr

weak_ptr

7°定制删除器

分析:C++11中没有xxx_array版本

那么如果是new[]出来的对象 怎么办?(了解)

定制删除器--(了解)

传一个实现释放方式的仿函数对象进去给智能指针

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

class A
{
public:
    ~A()
    {
        cout << "~A()" << endl;
    }

private:
    int _a1;
    int _a2;
};

template<class T>
struct DeleteArray
{
    void operator()(T* pa)
    {
        cout << "delete[] pa" << endl;
        delete[] pa;
     }
};

struct Free
{
    void operator()(void* pa)
    {
        cout << "free(p)" << endl;
        free(pa);
    }
};

struct Fclose
{
    void operator()(FILE* p)
    {
        cout << "fclose(p)" << endl;
        fclose(p);
    }
};


int main()
{
    std::shared_ptr<A> sp1(new A);
    //默认delete就是delete 无delete[]
    //此时需要传仿函数调delete[] 仿函数拿到对象 对象调用operator()
    std::shared_ptr<A> sp2(new A[10], DeleteArray<A>());//直接给一个匿名对象 释放10次
    std::shared_ptr<A> sp3((A*)malloc(sizeof(A)), Free());
    std::shared_ptr<FILE> sp4(fopen("test.txt","w"),Fclose());
    return 0;
}

8°智能指针与RAII

智能指针是RAII思想的一种应用的体现

本质RAII就是借助构造函数和析构函数来搞事情

因为析构函数和构造函数的特点都是自动调用

使用RAII思想设计的锁管理守卫 防止死锁出现

template<class Lock>
class LockGuard
{
public:
    LockGuard(Lock& lock)
        :_lk(lock)//锁不支持拷贝
    {
        _lk.lock();
    }
    //解锁也是解拷贝后的锁 但变成引用后 你解锁的锁就是原来的锁

    ~LockGuard()
    {
        cout << "解锁" << endl;
        _lk.unlock();
    }

private:
    //Lock _lk;
    Lock& _lk;//解决锁不能拷贝
    //注意这里是引用
};

void f()
{
    mutex mtx;
    mtx.lock();
    
    //func() //假设func函数有可能抛异常

    mtx.unlock();
    //t1线程先进来 t2线程锁住
    //此时t1抛异常 t1没有往下走 t2成了死锁
}

int main()
{
    try
    {
        f();
    }

    catch (exception& e)
    {
        cout << e.what() << endl;
    }
    return 0;
}

如果t1抛异常 t2就会成死锁 因此加守卫锁

f换成下面

int div()
{
    int a, b;
    cin >> a >> b;
    if (b == 0)
        throw invalid_argument("除0错误");

    return a / b;
}


void f()
{
    mutex mtx;
    LockGuard<mutex> lg(mtx);

    cout << div() << endl; //假设div可能抛异常
}

 

3°内存泄漏

  • 什么是内存泄漏?

内存泄漏一般是我们申请了资源 忘记释放 或者因为异常安全等问题没有释放

虚拟进程地址空间

mm_struct { void* _heap_start; void* _heap_end; ... }

物理地址

用页表按页为单位进行映射

多个进程的虚拟地址空间按页为单位跟物理地址建立映射关系

  • 内存泄漏的危害是什么?

如果我们申请了内存没有释放 如果进程正常结束 那么这个内存也会释放

一般程序碰到内存泄漏 重启后就OK了 长期运行 不能随便重启的程序 碰到内存泄漏危害非常大

比如操作系统 服务器上的服务 危害是:这些程序长期运行 不用的内存没有释放 内存泄漏

可用内存越来越少 导致服务很多操作失败(因为容器存数据 打开文件 创建套接字 发送数据等等都是需要内存的)

ps:一般后台服务器开发 如果出现内存泄漏 都是事故

  • 如何解决内存泄漏相关问题?

a.写C/C++代码时小心谨慎一点

b.不好处理的地方多用智能指针等等去管理(事前预防)

c.如果怀疑存在内存泄漏 或者已经出现 可以使用内存泄漏工具去检测(事后解决)

比如valgrind是一个linux下的强大工具

4°特殊类设计

  • 这个类只能在堆上创建对象

创建出的类对象只能在堆上

class HeapOnly
{
public:
    static HeapOnly* GetObj()
    {
        return new HeapOnly;
    }
    //静态的不需要对象就可以调用
private:
    //...
    HeapOnly()
    {}

    //C++98 声明成私有
    HeapOnly(const HeapOnly& );

    //C++11 delete 公有私有都ok
    HeapOnly(const HeapOnly&) = delete;
};
    
int main()
{
    //HeapOnly hp;
    //HeapOnly* p = new HeapOnly;
    //调不动private
    //HeapOnly* p = HeapOnly::GetObj();//都是new出来的 在堆上
    //内存泄漏
    //改为shared_ptr
    std::shared_ptr<HeapOnly> sp1(HeapOnly::GetObj());	
    
    //HeapOnly copy(*sp1);
    //拷贝构造 还是在栈上
    //因此拷贝构造也要封掉

    //三步
    //1:构造函数封死
    //2.写一个GetObj只new出对象
    //3.拷贝构造也封死
    return 0;
}
  • 只在栈上创建出对象的类
class StackOnly
{
public:
    static StackOnly GetObj()
    {
        return StackOnly();
    }

    /*void* operator new(size_t size) = delete;*/
    //禁掉operator new
private:
    StackOnly()
    {}
};

int main()
{
    StackOnly so = StackOnly::GetObj();
    //StackOnly* p = new StackOnly;//new不出来
    //或者禁掉operator new
    //但有缺陷
    //static StackOnly sso;在静态区 
    return 0;
}
  • 设计一个类 不能被拷贝

只在私有里声明 不定义

  • 设计一个类 不能被继承

a.构造函数私有化 父类的私有在子类中不可见

b.C++11 final

【C++】21.智能指针 完

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

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

相关文章

javaIO之各种流的分类与实际应用

目录 1、初识 Java IO2、传输方式划分3、操作对象划分3.1文件3.2数组&#xff08;内存&#xff09;3.3管道3.4基本数据类型3.5缓冲3.6打印3.7对象序列化/反序列化3.8转换 1、初识 Java IO IO&#xff0c;即in和out&#xff0c;也就是输入和输出&#xff0c;指应用程序和外部设备…

Java BIO 和 NIO 使用,有什么区别

Java 中的 I/O 操作主要有两种方式&#xff1a;BIO 和 NIO。BIO&#xff08;Blocking I/O&#xff09;是同步阻塞 I/O 模型&#xff0c;而 NIO&#xff08;Non-Blocking I/O&#xff09;是异步非阻塞 I/O 模型。这两种 I/O 模型在编写网络应用程序时有着不同的优缺点&#xff0…

Intel® ZTNA RA 23.03 release

摘要 传统的防火墙、入侵检测系统都是基于物理边界的&#xff0c;默认墙内安全&#xff0c;墙外不安全。随着应用程序和用户现在更可能在外围而不是内部&#xff0c;这种安全模型已经不再适用于当今复杂的网络场景。而“零信任”脱离了这种传统的安全模型&#xff0c;不再区别对…

论文浅尝 | 常识问答中的忠诚知识图解释

笔记整理&#xff1a;邹铭辉&#xff0c;天津大学硕士&#xff0c;研究方向为知识图谱 链接&#xff1a;https://aclanthology.org/2022.emnlp-main.743 动机 知识图谱通常被用作常识问答的信息来源&#xff0c;同时也可以用来解释模型对答案的选择。纳入图谱中事实信息的一个常…

诺贝尔奖得主Warshel:用计算化学揭开生命底层分子运行机制|智源大会嘉宾风采...

导读 复杂化学系统的多尺度建模可以用于计算机辅助药物设计、疾病致病机制、早期诊断生物标记、创新药物开发&#xff0c;这些具有划时代意义的研究成果&#xff0c;都凝结着计算化学研究先驱Arieh Warshel夜以继日的努力。 Warshel的传奇人生始于以色列一家公社的鱼塘&#xf…

【强烈推荐】3dMax自动展UV神器UV-Packer插件

UV-Packer是一款快速、精确的UV自动展开工具。这是一个一键式的解决方安&#xff0c;可以解决将展开的多边形排序和压缩成UV片的艰巨工作。 【适用版本】 3dMax2015-2024 【主要特性】 最小的UV区域浪费 确定良好 UV 包装的第一条规则是未覆盖的 UV 区域有多少。 浪费的空间…

操作系统的发展史(DOS/Windows篇)

操作系统的最强入门科普&#xff08;Unix/Linux篇&#xff09; 上一篇文章&#xff0c;小枣君介绍了Unix和Linux操作系统的诞生和发展。今天这篇&#xff0c;我再来说说微软的DOS和Windows系列。 █ DOS操作系统 上期提到&#xff0c;20世纪70年代&#xff0c;伴随着计算机技术…

360QPaaS参编信通院《组装式应用开发平台研究报告》| 应用前沿

在数字化转型的大背景下&#xff0c;“组装式应用” 成为行业重要战略趋势之一。数字化相较于信息化&#xff0c;强调基于信息数据反哺业务&#xff0c;业务进一步促进系统的迭代优化。组装式应用平台就是一种以业务为中心的模块化组件构成。组装式应用协力提供更灵活的组装式部…

Apache Iceberg 中引入索引提升查询性能

动手点关注 干货不迷路 ‍ ‍Apache Iceberg 是一种开源数据 Lakehouse 表格式&#xff0c;提供强大的功能和开放的生态系统&#xff0c;如&#xff1a;Time travel&#xff0c;ACID 事务&#xff0c;partition evolution&#xff0c;schema evolution 等功能。 本文将讨论火山…

治病如救火,怎样让新药研发更快、更省、更准?

说起医疗与生命科学行业&#xff0c;许多人可能都会想到一句俗语——“治病如救火”&#xff0c;可见其分秒必争的时效性。 然而&#xff0c;如果与日新月异的科技行业相比&#xff0c;医疗与生命科学行业在研发上的速度则慢得惊人。来自《自然》杂志的数据显示&#xff0c;一款…

搭建企业级ESB:让接口管理高效

目 录 01 接口管理现状分析‍‍‍‍‍ 02 ESB对接口的管理‍‍‍‍‍‍ 03 ESB接口管理的发展‍‍‍‍ 04 总结 01 接口管理现状分析‍ 随着社会的发展企业中建立了许多系统&#xff0c;系统中提供了许多接口作为业务解耦的重要手段。随着业务关系越来越复杂、依赖越来越多&…

Krpano之一全景图中嵌入可闪烁的热点图片

效果 步骤 1、打开ptgui软件加载全景图 2、镜头参数设置 3、编辑全景图 4、设置编辑模式 5、拖动鼠标和划动两个方向来调整全景图范围 调整后大概是这样的,我只要在这个区域画一个面即可,尽量让调整后是俯视图,这样在这基础上画的面会比较正一些

拼多多买家如何导出“个人中心”订单信息

经常在拼多多买东西&#xff0c;有时候需要把订单的物流信息导出来&#xff0c;方便记录和统计。现介绍如何使用dumuz工具来实现批量下载拼多多订单。 应用功能描述 模拟人工操作拼多多"个人中心-我的订单”订单网页&#xff0c;批量查询获取拼多多自己买的商品的订单数…

Istio 微服务架构的演变

微服务架构的演变 单体模式下面一个应用通常会有一个app server&#xff0c;这个app server里面会有不同的子模块&#xff0c;每一个模块都写在同一个应用包里面&#xff0c;模块和模块之间的边界有些时候设计的不是特别清晰&#xff0c;特别早期代码混合在一起那么意味着互相的…

PCL学习之滤波算法

前言 点云滤波作为常见的点云处理算法&#xff0c;一般是点云处理的第一步&#xff0c;对后续处理有很重要作用。滤波 有很多方面也有很多种功能&#xff0c;比如去除噪声点、离群点、点云平滑以及空洞、数据压缩等 原始点云数据往往包含大量散列点、孤立点&#xff0c;在获取…

CS 224N总结

CS 224N网址&#xff1a;Stanford CS 224N | Natural Language Processing with Deep Learning Lecture1 PPT网址&#xff1a;PowerPoint Presentation (stanford.edu) 这一讲主要讲了NLP研究的对象&#xff0c;我们如何表示单词的含义&#xff0c;以及Word2Vec方法的基本原…

Ubuntu22 k8s 1.27.1 安装及集群搭建教学(2023.5.16 k8s 最新版本教学,只看这一篇就够了哦!保姆级教程!不行你来找我!)

Ubuntu22 k8s 1.27.1 安装及集群搭建教学&#xff08;2023.5.16 k8s 最新版&#xff0c;只看这一篇就够了哦&#xff01;保姆级教程&#xff01;&#xff01;不行你来找我&#xff01;&#xff09; 温馨提示请仔细阅读&#xff1a;❤️❤️❤️❤️❤️❤️❤️❤️ 1. 由于新版…

Linux系统学习须牢记这几点

工欲善其事须先利其器&#xff0c;想了解Linux技术&#xff0c;先要有一套教学平台,请教同行或者老师来为我们解答&#xff0c;当然也可以下载Cygwin进行学习。但是自主学习的这一过程很困难&#xff0c;因为没有别人的帮助&#xff0c;我们或许会感到迷茫&#xff0c;也会出现…

Spring整合Mybatis、Junit

文章目录 1 Spring整合Mybatis思路分析1.1 环境准备步骤1:准备数据库表步骤2:创建项目导入jar包步骤3:根据表创建模型类步骤4:创建Dao接口步骤5:创建Service接口和实现类步骤6:添加jdbc.properties文件步骤7:添加Mybatis核心配置文件步骤8:编写应用程序步骤9:运行程序 1.2 整合…

STM32F1定时器(TIM1~TIM8)

一、stm32f1定时器简介 1.1、定时器分类 STM32共11个定时器&#xff0c;2个高级控制定时器TIM1和TIM8&#xff0c;4个通用定时器TIM2~TIM5&#xff0c;两个基本定时器TIM6和TIM7&#xff0c;两个看门狗定时器和一个系统滴答定时器Systick. 高级定时器TIM1和TIM8的时钟由APB1产…