sssssssssssssssshare_ptrrrrrrrrrrrrrrrrrrrrrrrrr

智能指针——shared_ptr的原理及仿写


shared_ptr的原理及仿写


共享指针允许多个指针指向同一份数据,因为它使用了引用计数,每多一个指针指向这个数据,引用技术加一,每销毁一个指针,引用技术减一,如果引用计数为0,则delete这个数据。
但是共享指针也不能将同一个裸指针赋值给多个智能指针,因为这样会是两个独立的共享指针,它们会分别计数,也就是意味着到时候它们会重复释放。
第一个没有计数器设计的版本:
template<typename T>
class MyShared_ptr {
private:
    //内部指针
    T* m_ptr;
    //计数器
    long* m_count;
public:
    //以裸指针构造共享指针,默认为nullptr
    //禁止隐式构造
    explicit MyShared_ptr(T* ptr = nullptr) : m_ptr(ptr) {
        //计数器初始化为1
        m_count = new long(1);
    }
    //以MyUnique_ptr右值构造共享指针
    MyShared_ptr(MyUnique_ptr<T>&& src) {
        //销毁独占指针,取出裸指针
        m_ptr = src.release();
        //计数器初始化为1
        m_count = new long(1);
    }
    //拷贝构造函数
    MyShared_ptr(const MyShared_ptr& src) {
        m_ptr = src.m_ptr;
        //拷贝计数器指针
        m_count = src.m_count;
        //计数器加一
        (*m_count)++;
    }
    //拷贝赋值
    MyShared_ptr& operator=(const MyShared_ptr& src) {
        if (&src == this) return *this;
        //如果自身不为空,先调用析构函数
        if (m_ptr != nullptr) {
            this->~MyShared_ptr();
        }
        m_ptr = src.m_ptr;
        m_count = src.m_count;
        (*m_count)++;
        return *this;
    }
    //以MyUnique_ptr右值 拷贝赋值
    MyShared_ptr& operator=(const MyUnique_ptr<T>&& src) {
        if (m_ptr != nullptr) {
            this->~MyShared_ptr();
        }
        //销毁独占指针,取出裸指针
        m_ptr = src.release();
        m_count = new long(1);
        return *this;
    }
    //析构函数
    ~MyShared_ptr() {
        //计数器减一
        (*m_count)--;
        //如果计数器为0,则释放指针空间和计数器空间
        if (0 == *m_count) {
            delete m_ptr;
            delete m_count;
            m_ptr = nullptr;
            m_count = nullptr;
        }
    }
    //获得内部指针地址
    T* get() {
        return m_ptr;
    }
    //重置指针
    void reset(T* ptr = nullptr) {
        //先调用析构函数
        this->~MyShared_ptr();
        m_ptr = ptr;
        m_count = new long(1);
    }
    //返回当前引用计数
    long use_count() {
        return *m_count;
    }
    //转换函数 bool
    operator bool() const {
        return m_ptr != nullptr;
    }
    //当前共享指针是否唯一
    bool unique() {
        if (1 == *m_count) return true;
        else return false;
    }
    //成员函数交换指针和计数器
    void swap(MyShared_ptr<T>& src) {
        T* temp_ptr;
        long* temp_count;
        temp_ptr = src.m_ptr;
        temp_count = src.m_count;
        src.m_ptr = m_ptr;
        src.m_count = m_count;
        m_ptr = temp_ptr;
        m_count = temp_count;
    }
    //重载*
    T& operator*() const {
        return *m_ptr;
    }
    //重载->
    T* operator->() const {
        return (&**this);
    }
};
//全局函数,有右值引用make_MyShared
template<class T>
MyShared_ptr<T> make_MyShared(MyShared_ptr<T>&& src) {
    return MyShared_ptr<T>(forward<MyShared_ptr<T>>(src));
}

有计数器设计的版本
//计数器设计
template<typename T>
class MyCount {
private:
    //分别存储引用计数和弱指针计数
    long use_count;
    long weak_count;
public:
    //计数器的初始化
    MyCount(long u,long w) : use_count(u), weak_count(w) {}
    //弱指针计数++
    void Increase_weak_count() {
        ++weak_count;
    }
    //弱指针计数--
    void Decrease_weak_count() {
        --weak_count;
    }
    //引用计数++
    void Increase_use_count() {
        ++use_count;
    }
    //引用计数--
    void Decrease_use_count() {
        --use_count;
    }
    //获得引用计数
    long get_use_count() {
        return use_count;
    }
    //释放传入对象的内存
    void DestroyPtr(T* ptr) {
        delete ptr;
    }
    //若没有弱指针,则释放计数器
    void DestroyThis() {
        if(0 == weak_count)
            delete this;
    }
};

template<typename T>
class MyShared_ptr {
private:
    //内部指针
    T* m_ptr;
    //计数器指针
    MyCount<T>* m_count;
public:
    //以裸指针构造共享指针
    explicit MyShared_ptr(T* ptr = nullptr) : m_ptr(ptr) {
        //初始化计数器为 引用1 弱指针0
        m_count = new MyCount<T>(1,0);
    }
    //以独占指针构造共享指针
    MyShared_ptr(MyUnique_ptr<T>&& src) {
        //销毁独占指针并取出裸指针
        m_ptr = src.release();
        //初始化计数器为 引用1 弱指针0
        m_count = new MyCount<T>(1, 0);
    }
    //拷贝构造
    MyShared_ptr(const MyShared_ptr& src) {
        m_ptr = src.m_ptr;
        m_count = src.m_count;
        //引用计数++
        m_count->Increase_use_count();
    }
    //拷贝赋值
    MyShared_ptr& operator=(const MyShared_ptr& src) {
        if (&src == this) return *this;
        if (m_ptr != nullptr) {
            this->~MyShared_ptr();
        }
        m_ptr = src.m_ptr;
        m_count = src.m_count;
        //引用计数++
        m_count->Increase_use_count();
        return *this;
    }
    //拷贝独占指针赋值
    MyShared_ptr& operator=(const MyUnique_ptr<T>&& src) {
        if (m_ptr != nullptr) {
            this->~MyShared_ptr();
        }
        m_ptr = src.release();
        m_count = new MyCount<T>(1, 0);
        return *this;
    }
    //析构函数
    ~MyShared_ptr() {
        //引用计数--
        m_count->Decrease_use_count();
        //如果引用计数为零
        if (0 == m_count->get_use_count()) {
            //释放指针
            m_count->DestroyPtr(m_ptr);
            //销毁计数器(计数器内部判断有无弱指针)
            m_count->DestroyThis();
            m_ptr = nullptr;
            m_count = nullptr;
        }
    }
    //获得内部指针地址
    T* get() {
        return m_ptr;
    }
    //重置共享指针为新指针
    void reset(T* ptr = nullptr) {
        this->~MyShared_ptr();
        m_ptr = ptr;
        m_count = new MyCount<T>(1, 0);
    }
    //返回引用计数
    long use_count() {
        return m_count->get_use_count();
    }
    //转换函数
    operator bool() const {
        return m_ptr != nullptr;
    }
    //是否唯一
    bool unique() {
        if (1 == m_count->get_use_count()) return true;
        else return false;
    }
    //交换指针和计数器
    void swap(MyShared_ptr<T>& src) {
        T* temp_ptr;
        MyCount<T>* temp_count;
        temp_ptr = src.m_ptr;
        temp_count = src.m_count;
        src.m_ptr = m_ptr;
        src.m_count = m_count;
        m_ptr = temp_ptr;
        m_count = temp_count;
    }
    //重载*
    T& operator*() const {
        return *m_ptr;
    }
    //重载->
    T* operator->() const {
        return (&**this);
    }
    //设置弱指针类为友元类
    template<typename T>
    friend class MyWeak_ptr;
};

//全局函数,有右值引用make_MyShared
template<class T>
MyShared_ptr<T> make_MyShared(MyShared_ptr<T>&& src) {
    return MyShared_ptr<T>(forward<MyShared_ptr<T>>(src));
}
222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222

C++ 11 智能指针shared_ptr类成员函数

李小虎

李小虎

关注自动驾驶/机器人,RPC,深度学习,Linux

​关注他

2 人赞同了该文章

C++ 11 模板库的 <memory> 头文件中定义的智能指针,即 shared_ptr 模板类,用来管理指针的存储,提供有限的内存回收函数,可同时与其他对象共享该管理功能,从而帮助彻底消除内存泄漏和悬空指针的问题。

shared_ptr 类型的对象能够获得指针的所有权并共享该所有权:一旦他们获得所有权,指针的所有者组就会在最后一个释放该所有权时负责删除该指针。

shared_ptr 对象一旦它们自己被销毁,或者它们的值因赋值操作或显式调用 shared_ptr::reset 而改变时,就会释放它们共同拥有的对象的所有权。一旦通过指针共享所有权的所有 shared_ptr 对象都释放了该所有权,则删除托管对象(通常通过调用 ::delete,也可以在构造时指定不同的删除器)。

  1. 同一个shared_ptr被多个线程读,是线程安全的;
  2. 同一个shared_ptr被多个线程写,不是线程安全的;
  3. 共享引用计数的不同的shared_ptr被多个线程写,是线程安全的。

shared_ptr 对象只能通过复制它们的值来共享所有权:如果两个 shared_ptr 是从同一个(非shared_ptr)指针构造(或制造)的,它们都将拥有该指针而不共享它,当其中一个释放时会导致潜在的访问问题它(删除其托管对象)并将另一个指向无效位置。

此外,shared_ptr 对象可以共享一个指针的所有权,同时指向另一个对象。这种能力被称为别名(参见构造函数),通常用于在拥有成员对象时指向成员对象。因此,一个 shared_ptr 可能与两个指针相关:

1)一个存储的指针,即它所指向的指针,以及它用 operator* 取消引用的指针。

2)一个所有者的指针(可能是共享的),它是所有权组负责在某个时间点删除的指针,并计为使用。

通常,存储指针和所有者指针指向同一个对象,但别名 shared_ptr 对象(使用别名构造函数及其副本构造的对象)可能指向不同的对象。不拥有任何指针的 shared_ptr 称为null shared_ptr。不指向任何对象的 shared_ptr 称为null shared_ptr 并且不应取消引用。请注意,空的 shared_ptr 不一定是null shared_ptr,null shared_ptr 也不一定是空的 shared_ptr。shared_ptr 对象通过提供对它们通过运算符 * 和 -> 指向的对象的访问来复制有限的指针功能。出于安全原因,它们不支持指针算术。类似weak_ptr,能够与 shared_ptr 对象共享指针,而无需拥有它们。shared_ptr 有以下成员函数:

(1)构造函数

shared_ptr的构造函数根据使用的参数类型构造 shared_ptr 对象:

1) 默认构造函数:constexpr shared_ptr() noexcept;

2) 从空指针构造:constexpr shared_ptr(nullptr_t) : shared_ptr() {}

3) 从指针构造:template <class U> explicit shared_ptr (U* p);

auto sp_sub = std::shared_ptr<Sub<Message>>(sub, del(addr)); //sub为new创建的对象

4) 从指针 + 删除器构造:template <class U, class D> shared_ptr (U* p, D del); template <class D> shared_ptr (nullptr_t p, D del);

5) 从指针 + 删除器 + 分配器构造:template <class U, class D, class Alloc> shared_ptr (U* p, D del, Alloc alloc); template <class D, class Alloc> shared_ptr (nullptr_t p, D del, Alloc alloc);

6) 复制构造函数:shared_ptr (const shared_ptr& x) noexcept; template <class U> shared_ptr (const shared_ptr<U>& x) noexcept;

7) 从weak_ptr 复制:template <class U> explicit shared_ptr (const weak_ptr<U>& x);

8) 移动构造函数:shared_ptr (shared_ptr&& x) noexcept; template <class U> shared_ptr (shared_ptr<U>&& x) noexcept;

9) 从其他类型的托管指针移动:template <class U> shared_ptr (auto_ptr<U>&& x); template <class U, class D> shared_ptr (unique_ptr<U,D>&& x);

10) 别名构造函数:template <class U> shared_ptr (const shared_ptr<U>& x, element_type* p) noexcept;

默认构造函数 1) 和 2)对象为空(不拥有指针,使用计数为零)。从指针构造3)该对象拥有 p,将使用计数设置为 1。从指针 + 删除器构造 4)与 3) 相同,但该对象还拥有删除器 del 的所有权(并在某些时候需要删除 p 时使用它)。从指针 + 删除器 + 分配器构造 5)与 4) 相同,但内部使用所需的任何内存都是使用 alloc 分配的(对象保留一份副本,但不取得所有权)。复制构造函数 6)如果 x 不为空,则对象共享 x 资产的所有权并增加使用次数。如果 x 为空,则构造一个空对象(如同默认构造)。从weak_ptr 7) 复制同上6),除了如果 x 已经过期,则抛出 bad_weak_ptr 异常。移动构造函数 8)该对象获取由 x 管理的内容,包括其拥有的指针。 x 变成一个空对象(就像默认构造的一样)。从其他类型的托管指针移动 9)对象获取由 x 管理的内容并将使用计数设置为 1。放弃的对象变为空,自动失去指针的所有权。别名构造函数 10)同6),除了存储的指针是p。该对象不拥有 p,也不会管理其存储。相反,它共同拥有 x 的托管对象并算作 x 的一种额外使用。它还将在发布时删除 x 的指针(而不是 p)。它可以用来指向已经被管理的对象的成员。

1) p: 其所有权被对象接管的指针。此指针值不应已由任何其他托管指针管理(即,此值不应来自托管指针上的调用成员 get)。U* 应隐式转换为 T*(其中 T 是 shared_ptr 的模板参数)。

2) del: 用于释放拥有的对象的删除器对象。这应该是一个可调用对象,将指向 T 的指针作为其函数调用的参数(其中 T 是 shared_ptr 的模板参数)。

3) alloc:用于分配/取消分配内部存储的分配器对象。

4) X: 托管指针类型的对象。U* 应隐式转换为 T*(其中 T 是 shared_ptr 的模板参数)。
#include <iostream> 
#include <memory>  
struct C {int* data;};  
int main () {   
std::shared_ptr<int> p1;   
std::shared_ptr<int> p2 (nullptr);   
std::shared_ptr<int> p3 (new int);   
std::shared_ptr<int> p4 (new int, std::default_delete<int>());   
std::shared_ptr<int> p5 (new int, [](int* p){delete p;}, std::allocator<int>());   
std::shared_ptr<int> p6 (p5);   std::shared_ptr<int> p7 (std::move(p6));   
std::shared_ptr<int> p8 (std::unique_ptr<int>(new int));   
std::shared_ptr<C> obj (new C);   
std::shared_ptr<int> p9 (obj, obj->data);    
std::cout << "use_count:\n";   
std::cout << "p1: " << p1.use_count() << '\n';   std::cout << "p2: " << p2.use_count() << '\n';   std::cout << "p3: " << p3.use_count() << '\n';   std::cout << "p4: " << p4.use_count() << '\n';   std::cout << "p5: " << p5.use_count() << '\n';   std::cout << "p6: " << p6.use_count() << '\n';   std::cout << "p7: " << p7.use_count() << '\n';   std::cout << "p8: " << p8.use_count() << '\n';   std::cout << "p9: " << p9.use_count() << '\n';   return 0; }

(2)析构函数

析构函数的作用是销毁shared_ptr对象。 但是,在此之前,根据成员 use_count 的值,它可能会产生以下副作用:

1)如果 use_count 大于 1(即该对象与其他 shared_ptr 对象共享其托管对象的所有权):与其共享所有权的其他对象的使用计数减 1。
2)如果 use_count 为 1(即对象是托管指针的唯一所有者):删除其拥有指针所指向的对象(如果 shared_ptr 对象是用特定的删除器构造的,则调用此函数;否则,函数使用运算符 删除)。
3)如果 use_count 为零(即对象为空),则该析构函数没有副作用。

用法举例:
#include <iostream> #include <memory> int main() { auto deleter = [](int* p) { std::cout << "[deleter called]\n"; delete p; }; std::shared_ptr<int> foo(new int, deleter); std::cout << "use_count: " << foo.use_count() << '\n'; return 0; // [deleter called] }

(3)赋值运算“=”

1) 复制:shared_ptr& operator= (const shared_ptr& x) noexcept; template <class U> shared_ptr& operator= (const shared_ptr<U>& x) noexcept;

2) 移动:shared_ptr& operator= (shared_ptr&& x) noexcept; template <class U> shared_ptr& operator= (shared_ptr<U>&& x) noexcept;

3) 从...移动:template <class U> shared_ptr& operator= (auto_ptr<U>&& x); template <class U, class D> shared_ptr& operator= (unique_ptr<U,D>&& x);

复制分配1) 将对象添加为 x 资产的共享所有者,从而增加它们的 use_count。移动分配 2) 将所有权从 x 转移到 shared_ptr 对象而不改变 use_count。 x 变成一个空的 shared_ptr(就像默认构造的一样)。同样,来自其他托管指针类型 3) 的移动分配也会转移所有权,并使用 set a use count of 1 进行初始化。

此外,在上述所有情况下,对该函数的调用与在其值更改之前调用了 shared_ptr 的析构函数具有相同的副作用(如果此 shared_ptr 是唯一的,则包括删除托管对象)。不能将指针的值直接分配给 shared_ptr 对象。您可以改用 make_shared 或成员重置。

托管指针类型的对象。U* 应隐式转换为 T*(其中 T 是 shared_ptr 的模板参数)。

用法举例:
#include <iostream> #include <memory> int main () { std::shared_ptr<int> foo; std::shared_ptr<int> bar (new int(10)); foo = bar; // copy bar = std::make_shared<int> (20); // move std::unique_ptr<int> unique (new int(30)); foo = std::move(unique); // move from unique_ptr std::cout << "*foo: " << *foo << '\n'; std::cout << "*bar: " << *bar << '\n'; return 0; }

(4)swap函数

函数声明:void swap (shared_ptr& x) noexcept; 参数x: 另一个相同类型的 shared_ptr 对象(即,具有相同的类模板参数 T)。作用是将 shared_ptr 对象的内容与 x 的内容交换,在它们之间转移任何托管对象的所有权,而不会破坏或改变两者的使用计数。

用法举例:
#include <iostream> #include <memory> int main () { std::shared_ptr<int> foo (new int(10)); std::shared_ptr<int> bar (new int(20)); foo.swap(bar); std::cout << "*foo: " << *foo << '\n'; std::cout << "*bar: " << *bar << '\n'; return 0; }

(5)reset函数

重置shared_ptr,对于声明1) 对象变为空(如同默认构造)。在所有其他情况下,shared_ptr 以使用计数为 1 获取 p 的所有权,并且 - 可选地 - 使用 del 和/或 alloc 作为删除器 和分配器。另外,调用这个函数有同样的副作用,就像在它的值改变之前调用了shared_ptr 的析构函数一样(包括删除托管对象,如果这个shared_ptr 是唯一的)。

1) void reset() noexcept;
2) template <class U> void reset (U* p);
3) template <class U, class D> void reset (U* p, D del);
4) template <class U, class D, class Alloc> void reset (U* p, D del, Alloc alloc);

用法举例:
#include <iostream> #include <memory> int main () { std::shared_ptr<int> sp; // empty sp.reset (new int); // takes ownership of pointer *sp=10; std::cout << *sp << '\n'; sp.reset (new int); // deletes managed object, acquires new pointer *sp=20; std::cout << *sp << '\n'; sp.reset(); // deletes managed object return 0; }

(6)get函数

函数声明:element_type* get() const noexcept; get()返回存储的指针。存储的指针指向shared_ptr对象解引用的对象,一般与其拥有的指针相同。存储的指针(即这个函数返回的指针)可能不是拥有的指针(即对象销毁时删除的指针)如果 shared_ptr 对象是别名(即,别名构造的对象及其副本)。

用法举例:
#include <iostream> #include <memory> int main () { int* p = new int (10); std::shared_ptr<int> a (p); if (a.get()==p) std::cout << "a and p point to the same location\n"; // three ways of accessing the same address: std::cout << *a.get() << "\n"; std::cout << *a << "\n"; std::cout << *p << "\n"; return 0; }

(7)取对象运算“*”

函数声明:element_type& operator*() const noexcept; 取消引用对象。返回对存储指针指向的对象的引用。等价于:*get()。如果shared_ptr的模板参数为void,则该成员函数是否定义取决于平台和编译器,以及它的返回类型 在这种情况下。

用法举例:
#include <iostream> #include <memory> int main() { std::shared_ptr<int> foo(new int); std::shared_ptr<int> bar(new int(100)); *foo = *bar * 2; std::cout << "foo: " << *foo << '\n'; std::cout << "bar: " << *bar << '\n'; return 0; }

(8)“->”操作符

函数声明:element_type* operator->() const noexcept; 取消引用对象成员。返回一个指向存储指针指向的对象的指针,以便访问其成员之一。如果存储的指针是空指针,则不应调用该成员函数,它返回与 get() 相同的值。

用法举例:

#include <iostream> #include <memory> struct C { int a; int b; }; int main() { std::shared_ptr<C> foo; std::shared_ptr<C> bar(new C); foo = bar; foo->a = 10; bar->b = 20; if (foo) std::cout << "foo: " << foo->a << ' ' << foo->b << '\n'; if (bar) std::cout << "bar: " << bar->a << ' ' << bar->b << '\n'; return 0; }

(9)use_count函数

函数声明:long int use_count() const noexcept; use_count 返回与此对象(包括它)在同一指针上共享所有权的 shared_ptr 对象的数量。如果这是一个空的 shared_ptr,则该函数返回零。库实现不需要保留任何特定所有者集的计数 ,因此调用此函数可能效率不高。 要具体检查 use_count 是否为 1,也可以使用 member unique 代替,这样可能更快。

(10)unique函数

函数声明:bool unique() const noexcept; 检查是否唯一 返回 shared_ptr 对象是否不与其他 shared_ptr 对象共享其指针的所有权(即,它是唯一的)。 空指针从来都不是唯一的(因为它们不拥有任何指针)。 如果唯一的 shared_ptr 对象释放此所有权,则它们负责删除其托管对象(请参阅析构函数)。 此函数应返回与 (use_count()==1) 相同的值,尽管它可能以更有效的方式执行此操作。 如果这是唯一的 shared_ptr,则返回值 true,否则返回 false。

用法举例:


#include <iostream> #include <memory> int main() { std::shared_ptr<int> foo; std::shared_ptr<int> bar(new int); std::cout << "foo unique?\n" << std::boolalpha; std::cout << "1: " << foo.unique() << '\n'; // false (empty) foo = bar; std::cout << "2: " << foo.unique() << '\n'; // false (shared with bar) bar = nullptr; std::cout << "3: " << foo.unique() << '\n'; // true return 0; }

(11)“bool”操作

函数声明:explicit operator bool() const noexcept; 检查是否为 null。 返回存储的指针是否为空指针。 存储的指针指向 shared_ptr 对象解除引用的对象,通常与其拥有的指针相同(销毁时删除的指针)。 如果 shared_ptr 对象是别名(即别名构造的对象及其副本),它们可能会有所不同。该函数返回的结果与 get()!=0 相同。 请注意,空的 shared_ptr(即此函数返回 false 的指针)不一定是空的 shared_ptr。 别名可能拥有某个指针但指向空,或者所有者组甚至可能拥有空指针(参见构造函数 4 和 5)。

用法举例:

#include <iostream> #include <memory> 
 int main() {     
std::shared_ptr<int> foo;    
 std::shared_ptr<int> bar(new int(34));      
if (foo) 
    std::cout << "foo points to " << *foo << '\n';     
else 
    std::cout << "foo is null\n";      
if (bar) 
    std::cout << "bar points to " << *bar << '\n';     
else std::cout << "bar is null\n";      return 0; }

(12)owner_before函数

函数声明:

template <class U> bool owner_before (const shared_ptr<U>& x) const;
template <class U> bool owner_before (const weak_ptr<U>& x) const;

基于所有者的排序。根据严格的弱基于所有者的顺序返回是否认为对象在 x 之前。与 operator< 重载不同,此排序考虑了 shared_ptr 的拥有指针,而不是存储的指针,使得两个 如果它们都共享所有权,或者它们都为空,即使它们存储的指针值不同,这些对象中的一个被认为是等效的(即,无论操作数的顺序如何,该函数都返回 false)。 如果 shared_ptr 对象是一个别名(别名构造的对象及其副本),则 shared_ptr 对象解引用)可能不是拥有的指针(即对象销毁时删除的指针)。该函数由 owner_less 调用以确定其结果。

用法举例:



#include <iostream> #include <memory> int main() { int* p = new int(10); std::shared_ptr<int> a(new int(20)); std::shared_ptr<int> b(a, p); // alias constructor std::cout << "comparing a and b...\n" << std::boolalpha; std::cout << "value-based: " << (!(a < b) && !(b < a)) << '\n'; std::cout << "owner-based: " << (!a.owner_before(b) && !b.owner_before(a)) << '\n'; delete p; return 0; }

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

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

相关文章

拓展欧几里得和裴蜀定理

裴蜀定理&#xff08;或贝祖定理&#xff09;说明了对任何整数a、b和它们的最大公约数d&#xff0c;关于未知数x和y的线性不定方程&#xff08;称为裴蜀等式&#xff09;&#xff1a;若a,b是整数,且gcd(a,b)d&#xff0c;那么对于任意的整数x,y,axby都一定是d的倍数&#xff0c…

【安全攻防】网络安全中的序列化与反序列

1.序列化与反序列化 首先要了解序列化与反序列化的定义&#xff0c;以及序列化反序列化所用到的基本函数。 序列化&#xff1a;把对象转换为字节序列的过程称为对象的序列化&#xff0c;相当于游戏中的存档。 PHP中的序列化函数serialize() **serialize()**函数用于序列化对…

jsqlparse工具拦截sql处理和拓展

前置知识 访问者模式 &#xff08;Visitor Pattern&#xff09;是一种行为设计模式&#xff0c;它允许你定义在不改变被访问元素的类的前提下&#xff0c;扩展其功能。通过将操作&#xff08;操作或算法&#xff09;从对象结构中提取出来&#xff0c;可以在不修改这些对象的前…

6.基于SpringBoot的SSMP整合案例-业务层开发

目录 1.业务层标准开发 1.1接口定义 1.2实现类定义 1.3测试类定义 1.4小结&#xff1a; 2.业务层快速开发 2.1使用MyBatisP1us提供有业务层通用接口(ISerivce)与业务层通用实现类(ServiceImpl),t> 接口定义&#xff1a; 实现类定义&#xff1a; 测试类&#xff1a; …

MergeBus封装模块

当模型有很多信号进行交互的时候&#xff0c;并且已经无法回避线条交叉的时候&#xff0c;我们会选择 From和Goto模块来提高模型的可读性&#xff0c;假如你拿到的模型如下图&#xff1a; 像不像芯片的电路&#xff0c;错综而复杂 该如何整理呢&#xff1f; 我相信有很多模型…

2024年移动手游趋势:休闲类手游收入逆势增长,欧美玩家成为主力

移动手游广告情报平台Sensor Tower近期发布的报告显示&#xff0c;从宏观数据来看&#xff0c;尽管2023年对于移动游戏市场来说是艰难的一年&#xff0c;无论是总下载量亦或是总收入都较去年有所下降&#xff0c;尤其是Google Play。但在总体下降的大趋势下&#xff0c;休闲游戏…

mac如何压缩视频大小不改变画质,mac怎么压缩视频软件

在数字时代&#xff0c;视频已成为信息传递和娱乐消遣的重要媒介。然而&#xff0c;视频带来的愉悦体验背后&#xff0c;是日益增长的存储和分享压力。大视频文件不仅占用大量存储空间&#xff0c;上传和下载也变得异常缓慢。那么&#xff0c;如何才能有效压缩视频&#xff0c;…

SAP中的 UPDATA TASK 和 BACKGROUND TASK

前言&#xff1a; 记录这篇文章起因是调查生产订单报工问题引申出来的一个问题&#xff0c;后来再次调查后了解了其中缘由&#xff0c;大概记录以下&#xff0c;如有不对&#xff0c;欢迎指正。问题原贴如下&#xff1a; SAP CO11N BAPI_PRODORDCONF_CREATE_TT连续报工异步更…

LoadRunner-Virtual User Generator组件学习(录制不上内容)

重点知识 LR工具是拿C写的&#xff0c;所以它的脚本默认也是C&#xff0c;但是最终生成的脚本不止是C&#xff0c;它是支持C和Java语言的&#xff0c;这个大家要清楚&#xff0c;对本身懂代码的就很友好&#xff0c;你了解java&#xff0c;那就可以把脚本改成java&#xff0c;…

不看后悔!国内AI大比拼的精彩看点全汇总

至2022年AI爆发后&#xff0c;在中国已催生了上千个AI产品。 这些产品涵盖了从头部大厂到高等院校&#xff0c;再到初创企业的广泛阵容。 如&#xff1a; 大厂&#xff1a;百度文心、阿里通义、腾讯元宝、字节豆包、讯飞星火等高校&#xff1a;清华大学、北京大学等初创&…

.NET 漏洞分析 | 某ERP系统存在SQL注入

01阅读须知 此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等&#xff08;包括但不限于&#xff09;进行检测或维护参考&#xff0c;未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失&#xf…

JAVA导出数据库字典到Excel

文章目录 1、查询某张表字段信息2、TableVo接收sql查询得到的数据3、excel导出4、导出案例 1、查询某张表字段信息 select column_name as columnName, -- 字段名 COLUMN_DEFAULT as colDefault, -- 默认值 column_key as columnKey, -- PRI-主键&#xff0c;UNI-唯一键&…

机器学习原理之 -- 朴素贝叶斯分类器:由来及原理详解

朴素贝叶斯&#xff08;Naive Bayes&#xff09;分类器是一类基于贝叶斯定理&#xff08;Bayes Theorem&#xff09;的简单而有效的概率分类算法。由于其假设特征之间的条件独立性&#xff0c;因此被称为“朴素”贝叶斯分类器。尽管这种独立性假设在现实中很少完全成立&#xf…

VSCode使用ipynb文件高效地进行功能测试

一、ipynb是什么文件 .ipynb文件是Jupyter Notebook的专用格式&#xff0c;它允许用户在一个网页应用中混合编写Markdown文本、执行代码、查看输出结果及图表。Jupyter Notebook的本质是一个Web应用程序&#xff0c;支持运行40多种编程语言&#xff0c;包括Python。它的主要用…

Elasticsearch运维系列_ES之max_result_window 含义-对性能影响及参数调整

如果你觉得这篇文章能给你带来收获&#xff0c;请关注我公众号: 这篇文章主要给大家介绍max_result_window参数及其对性能影响。 Part1 背景描述 当前某个业务xxxdb单个索引值较大&#xff0c;每日单个索引大小在二三百G&#xff0c;当前索引保留15天&#xff0c;如果拉取一个…

初入Node.js必备知识

Node.js因什么而生&#xff0c;作用是干什么&#xff1f; Node.js是一个用c和c打造的一个引擎&#xff0c;他能够读懂JavaScript&#xff0c;并且让JavaScript能够和操作系统打交道的能力 JavaScript 原本只能在浏览器中运行,但随着Web应用程序越来越复杂,仅靠客户端JavaScri…

零基础入门怎么学习老挝语字母表?《老挝语翻译通》App真人发音教学,学习老挝语字母发音和词汇句子!

这段老挝文字翻译成中文是什么意思&#xff1f;有什么好用的老挝语翻译工具推荐吗&#xff1f; 快速翻译&#xff1a;中老语言无缝转换&#xff0c;实时翻译&#xff0c;让沟通更流畅。 学习工具&#xff1a;零基础入门到流利对话&#xff0c;老挝语真人发音&#xff0c;让你的…

MacOS 安装 mtr 网络检测工具

Install sudo brew install mtr sudo chown root $(which mtr) sudo chmod us $(which mtr) sudo chown root $(which mtr-packet) sudo chmod us $(which mtr-packet) Test mtr google.com

Build a Large Language Model (From Scratch)附录E(gpt-4o翻译版)

来源&#xff1a;https://github.com/rasbt/LLMs-from-scratch?tabreadme-ov-file https://www.manning.com/books/build-a-large-language-model-from-scratch