C++ 11智能指针

本文深入探讨了C++11中的智能指针,包括shared_ptr的用法、共享所有权原理、自定义deleter、与普通指针的对比,以及unique_ptr的独占性、创建和使用。文章通过实例展示了如何避免内存泄露和循环引用问题,并解释了为何在特定情况下使用weak_ptr。
摘要由CSDN通过智能技术生成

本文没有翻译版权,不选不让发布。如涉及侵权,请联系作者删除(5207798@qq.com)。

Part 1: shared_ptr Tutorial and Examples

原文链接:https://thispointer.com/learning-shared_ptr-part-1-usage-details/

这篇文章中,我们讨论C++11中的智能指针shared_ptr的用法。

什么是std::shared_ptr<> ?

shared_ptr是C++11提供的智能指针之一,它能在没有任何引用的时候自动释放持有的裸指针。因此能帮助开发人员避免内存泄露和悬空指针问题。

shared_ptr和共享所有权

多个shared_ptr共享一个底层裸指针的所有权,是通过引用计数的方式实现的。

每一个shared_ptr对象内部有两个指针

1)一个指向持有的对象;

2)另外一个指向引用计数数据;

共享所有权是如何通过引用计数实现的?
  • 当一个新的shared_ptr对象持有(关联)一个裸指的时候,这个shared_ptr的构造函数增加裸指针的引用计数+1。
  • 当一个shared_ptr对象超出作用域,它的析构函数将裸指针的引用计数-1。如果引用计数变为0,说明没有任何shared_ptr对象持有该(关联)这个裸指针了,将会使用deleted释放裸指针指向的内存。

创建一个shared_ptr对象

将一个shared_ptr对象和裸指针关联:

std::shared_ptr<int> p1(new int());

上边这行代码会在堆上分配两块内存:

1)一块内存用于保存int

2)一块内存用于保存引用计数,初始值为1

检查一个shared_ptr对象的引用计数

可以使用如下方法获取shared_prt对象的引用计数:

p1.use_count();
std::make_shared

如何把一个裸指针和一个shared_ptr关联起来?

std::shared_ptr<int> p1 = new int(); // Compile error

因为shared_ptr的构造函数使用了Explicit形式的参数(不支持隐式的转换数据类型),上边的代码需要使用隐式数据类型转换的构造函数。所以会发生编译错误。

Part 2: shared_ptr and Custom Deletor

原文链接:https://thispointer.com/shared_ptr-and-custom-deletor/

这篇文章讨论如何在std::shared_ptr中使用自对应的deleter。

当一个shared_ptr对象超出作用域以后,它的析构函数会将引用计数减1,如果引用计数变为0,将会delete关联的裸指针。

默认的,就是调用delete删除指针

delete Pointer;

但是,有时候我们不想用默认的delete函数删除指针,比如:

一个shared_ptr对象关联的裸指针是指向数组的而不是指向单一对象的

std::shared_ptr<int> p3(new int[12]);

shared_ptr在析构的时候,仍然会调用delete方法。然而,正确的释放指针方式是:

delete []
使用自定义的deleter

可以在shared_ptr对象的构造函数中传入一个回调函数,然后在shared_ptr对象析构的时候, 如果需要释放对象,调用这个回调函数来回收内存。

使用函数指针来定义deleter
// function that calls the delete [] on received pointer
void deleter(Sample * x)
{
    std::cout << "DELETER FUNCTION CALLED\n";
    delete[] x;
}

在shared_ptr对象构造函数中传入这个函数指针

// Creating a shared+ptr with custom deleter
std::shared_ptr<Sample> p3(new Sample[12], deleter);

整个例子代码如下:

#include <iostream>
#include <memory>
struct Sample
{
    Sample()
    {
        std::cout << "CONSTRUCTOR\n";
    }
    ~Sample()
    {
        std::cout << "DESTRUCTOR\n";
    }
};
// function that calls the delete [] on received pointer
void deleter(Sample * x)
{
    std::cout << "DELETER FUNCTION CALLED\n";
    delete[] x;
}
int main()
{
    // Creating a shared+ptr with custom deleter
    std::shared_ptr<Sample> p3(new Sample[12], deleter);
    return 0;
}

输出

CONSTRUCTOR
CONSTRUCTOR
CONSTRUCTOR
CONSTRUCTOR
CONSTRUCTOR
CONSTRUCTOR
CONSTRUCTOR
CONSTRUCTOR
CONSTRUCTOR
CONSTRUCTOR
CONSTRUCTOR
CONSTRUCTOR
DELETER FUNCTION CALLED
DESTRUCTOR
DESTRUCTOR
DESTRUCTOR
DESTRUCTOR
DESTRUCTOR
DESTRUCTOR
DESTRUCTOR
DESTRUCTOR
DESTRUCTOR
DESTRUCTOR
DESTRUCTOR
DESTRUCTOR
使用lambda表达式或者函数对象定义deleter

shared_ptr构造函数中的deleter还可以传入lambda表达式或者函数对象。

class Deleter
{
    public:
    void operator() (Sample * x) {
        std::cout<<"DELETER FUNCTION CALLED\n";
        delete[] x;
    }
};
// Function Object as deleter
std::shared_ptr<Sample> p3(new Sample[12], Deleter());
// Lambda function as deleter
std::shared_ptr<Sample> p4(new Sample[12], [](Sample * x){
    std::cout<<"DELETER FUNCTION CALLED\n";
        delete[] x;
});

还有些情况,资源释放的方式不是delete

下边是一个内存池使用shared_ptr的例子:

#include <iostream>
#include <memory>
struct Sample
{
};
// Memory Pool Dummy Kind of Implementation
template<typename T>
class MemoryPool
{
public:
    T * AquireMemory()
    {
        std::cout << "AQUIRING MEMORY\n";
        return (new T());
    }
    void ReleaseMemory(T * ptr)
    {
        std::cout << "RELEASE MEMORY\n";
        delete ptr;
    }
};
int main()
{
    std::shared_ptr<MemoryPool<Sample> > memoryPoolPtr = std::make_shared<
            MemoryPool<Sample> >();
    std::shared_ptr<Sample> p3(memoryPoolPtr->AquireMemory(),
            std::bind(&MemoryPool<Sample>::ReleaseMemory, memoryPoolPtr,
                    std::placeholders::_1));
    return 0;
}

输出

AQUIRING MEMORY
RELEASE MEMORY

Part 3: shared_ptr vs Pointer

原文链接:https://thispointer.com//how-shared_ptr-object-is-different-from-a-raw-pointer/

这篇文章比较了C++11中的智能指针shared_ptr和普通指针。

没有++,–以及[]运算符

shared_ptr只提供以下两种运算符

  1. ->, *

  2. ==

没有:

  1. 算数运算符如:+, -,++, –

  2. 下标运算符[]

下边是例子:

#include<iostream>
#include<memory>
struct Sample
{
    void dummyFunction()
    {
        std::cout << "dummyFunction" << std::endl;
    }
};
int main()
{
    std::shared_ptr<Sample> ptr = std::make_shared<Sample>();
    (*ptr).dummyFunction(); // Will Work
    ptr->dummyFunction(); // Will Work
    // ptr[0]->dummyFunction(); // This line will not compile.
    // ptr++;  // This line will not compile.
    //ptr--;  // This line will not compile.
    std::shared_ptr<Sample> ptr2(ptr);
    if (ptr == ptr2) // Will work
        std::cout << "ptr and ptr2 are equal" << std::endl;
    return 0;
}

输出:

dummyFunction
dummyFunction
ptr and ptr2 are equal

检查NULL

如果创建一个shared_ptr,但是没有关联裸指针,它就是空的。

注意,如果裸指针是一个悬空指针,shared_ptr是没法判断出来的。

可以使用以下3种方式判断一个shared_ptr为空:

std::shared_ptr<Sample> ptr3;
if(!ptr3)
    std::cout<<"Yes, ptr3 is empty" << std::endl;
if(ptr3 == NULL)
    std::cout<<"ptr3 is empty" << std::endl;
if(ptr3 == nullptr)
    std::cout<<"ptr3 is empty" << std::endl;

从shared_ptr中获取裸指针

std::shared_ptr<Sample> ptr = std::make_shared<Sample>();
Sample * rawptr = ptr.get();

一般来说,不应该获取shared_ptr中的裸指针。因为可能会破坏shared_ptr中的引用计数,从而造成crash。即使不对指针做任何delete,当shared_ptr中引用计数为0的时候,将会删除裸指针指向的对象,那么通过get方法的到的指针就是一个悬空指针,也是很危险的。

Part 4: Create shared_ptr objects carefully

原文链接:https://thispointer.com/create-shared_ptr-objects-carefully/

我们在创建shared_ptr对象的时候要非常小心。

特别是以下两种情况:

1)不要试图使用一个裸指针创建两个shared_ptr对象。因为不同的shared_ptr对象互相不知道对方的存在,分别独立的引用计数。

下边我们看下这样做会引起什么问题?

假设两个shared_ptr对象都是用一个裸指针创建的:

int * rawPtr = new int();
std::shared_ptr<int> ptr_1(rawPtr);
std::shared_ptr<int> ptr_2(rawPtr);

然后ptr_2超出了作用域,讲裸指针指向的内存释放了。此时ptr_1中的裸指针就是一个悬空指针。

然后当ptr_2超出作用域后,再次释放内存,就会引起Crash。

代码示例:

#include<iostream>
#include<memory>
typedef struct Sample {
Sample() {
    internalValue = 0;
    std::cout<<"Constructor"<<std::endl;
}
~Sample() {
    std::cout<<"Destructor"<<std::endl;
}
}Sample;
int main()
{
    {
    Sample * rawPtr = new Sample();
    std::shared_ptr<Sample> ptr_1(rawPtr);
        {
        std::shared_ptr<Sample> ptr_2(rawPtr);
        }
// As ptr_2 dont know that the same raw pointer is used by another shared_ptr i.e. ptr_1, therefore
// here when ptr_2 goes out of scope and it deletes the raw pointer associated with it.
// Now ptr_1 is internally containing a dangling pointer. Therefore when we it
// will go out of scope it will again try to delete the already deleted raw pointer and application
// will crash.
    }
return 0;
}
  1. 不要使用一个指向栈上对象的指针创建shared_ptr

代码示例:

#include<iostream>
#include<memory>
int main()
{
	int x = 12;
	std::shared_ptr<int> ptr(&x);
	return 0;
}

shared_ptr的实现假定其中的裸指针是指向堆中的对象的。如果将一个栈上的对象指针传入,在超出作用的时候,仍然会造成重复释放指针的问题,引发Crash。

鉴于以上两个原因,建议不要通过裸指针直接创建shared_ptr对象。而是使用make_shared<>方法

std::shared_ptr<int> ptr_1 = make_shared<int>();
std::shared_ptr<int> ptr_2 (ptr_1);

编码规范:先创建shared_ptr,再赋值。直接避免裸指针的定义出现。

Part 5: shared_ptr, Binary trees and the problem of Cyclic References

原文链接:https://thispointer.com/shared_ptr-binary-trees-and-the-problem-of-cyclic-references/

shared_ptr的最大好处是在没有人使用指针的时候,能自动释放内存。

但是如果不小心使用,优点可能变成缺点,我们看例子,

假设我们需要设计一个二叉树,节点中有两个指针,一个指向左子节点,一个指向右子节点。

#include <iostream>
#include <memory>
class Node
{
    int value;
    public:
    std::shared_ptr<Node> leftPtr;
    std::shared_ptr<Node> rightPtr;
    Node(int val) : value(val) {
         std::cout<<"Contructor"<<std::endl;
    }
    ~Node() {
         std::cout<<"Destructor"<<std::endl;
    }
};
int main()
{
    std::shared_ptr<Node> ptr = std::make_shared<Node>(4);
    ptr->leftPtr = std::make_shared<Node>(2);
    ptr->rightPtr = std::make_shared<Node>(5);
    return 0;
}

在上边的例子中,一切都很正常。构造函数被调用了3次,析构函数也被调用了3次。没有内存泄露。

但是如果增加一个小需求,每个node添加一个指向父节点的指针,shared_ptr就会出问题了。

代码示例:

#include <iostream>
#include <memory>
class Node
{
    int value;
    public:
    std::shared_ptr<Node> leftPtr;
    std::shared_ptr<Node> rightPtr;
    std::shared_ptr<Node> parentPtr;
    Node(int val) : value(val)     {
         std::cout<<"Contructor"<<std::endl;
    }
    ~Node()     {
         std::cout<<"Destructor"<<std::endl;
    }
};
int main()
{
    std::shared_ptr<Node> ptr = std::make_shared<Node>(4);
    ptr->leftPtr = std::make_shared<Node>(2);
    ptr->leftPtr->parentPtr = ptr;
    ptr->rightPtr = std::make_shared<Node>(5);
    ptr->rightPtr->parentPtr = ptr;
    std::cout<<"ptr reference count = "<<ptr.use_count()<<std::endl;
    std::cout<<"ptr->leftPtr reference count = "<<ptr->leftPtr.use_count()<<std::endl;
    std::cout<<"ptr->rightPtr reference count = "<<ptr->rightPtr.use_count()<<std::endl;
    return 0;
}

构造函数仍然被调用了3次,然而析构函数一次也没有被调用。

造成这种现象的原因是循环引用。如果两个shared_ptr互相引用了对方,当它们超出作用域的时候,都不会释放其中的内存。

因为在shared_ptr的析构函数中,首先将引用计数-1,然后检查引用计数是否为0,如果为0将会释放内存,否则说明还有其他的shared_ptr在使用其中的对象。在上边的场景中,这些shared_ptrs互相引用,所以引用计数始终不为0.

让我们重放一下这个过程:

ptr超出作用域的时候,析构函数被调用;

在ptr的析构函数中,Node(4)的引用计数-1;

检查Node(4)的引用计数,发现是2,因为leftPtr中的Node(2)的parent和rightPtr中的Node(5) 的parent在使用,所以Node(4)不会被删除;

Node(4)不被删除,那么Node(4)中使用的leftPtr和rightPtr就不会超出生命周期,所以也不会被析构;

于是,没有任何析构函数被调用;

如何解决这个问题?

答案是使用weak_ptr

weak_ptr允许共享但是不持有对象。对象来自shared_ptr。

std::shared_ptr<int> ptr = std::make_shared<int>(4);
std::weak_ptr<int> weakPtr(ptr); 

weak_ptr对象不能直接使用*和->运算符来操作内部对象。必须先从weak_ptr中使用lock()方法创建一个shared_ptr对象出来。

代码示例:

#include <iostream>
#include <memory>
int main()
{
    std::shared_ptr<int> ptr = std::make_shared<int>(4);
    std::weak_ptr<int> weakPtr(ptr);
    std::shared_ptr<int> ptr_2 =  weakPtr.lock();
    if(ptr_2)
        std::cout<<(*ptr_2)<<std::endl;
    std::cout<<"Reference Count :: "<<ptr_2.use_count()<<std::endl;   
    if(weakPtr.expired() == false)
        std::cout<<"Not expired yet"<<std::endl;   
    return 0;
}

注意:如果shared_ptr中的对象已经被释放掉,lock()方法会返回一个空的shared_ptr;

使用weak_ptr改进上边的二叉树的例子:

将循环引用的其中一方改成weak_ptr引用就可以打破循环。

#include <iostream>
#include <memory>
class Node
{
    int value;
    public:
    std::shared_ptr<Node> leftPtr;
    std::shared_ptr<Node> rightPtr;
    // Just Changed the shared_ptr to weak_ptr
    std::weak_ptr<Node> parentPtr;
    Node(int val) : value(val)     {
         std::cout<<"Contructor"<<std::endl;
    }
    ~Node()     {
         std::cout<<"Destructor"<<std::endl;
    }
};
int main()
{
    std::shared_ptr<Node> ptr = std::make_shared<Node>(4);
    ptr->leftPtr = std::make_shared<Node>(2);
    ptr->leftPtr->parentPtr = ptr;
    ptr->rightPtr = std::make_shared<Node>(5);
    ptr->rightPtr->parentPtr = ptr;
    std::cout<<"ptr reference count = "<<ptr.use_count()<<std::endl;
    std::cout<<"ptr->leftPtr reference count = "<<ptr->leftPtr.use_count()<<std::endl;
    std::cout<<"ptr->rightPtr reference count = "<<ptr->rightPtr.use_count()<<std::endl;
    std::cout<<"ptr->rightPtr->parentPtr reference count = "<<ptr->rightPtr->parentPtr.lock().use_count()<<std::endl;
    std::cout<<"ptr->leftPtr->parentPtr reference count = "<<ptr->leftPtr->parentPtr.lock().use_count()<<std::endl;
    return 0;
}

上边的代码解决了内存泄露的问题。

Part 6 : unique_ptr<> Tutorial and Examples

原文链接:https://thispointer.com/c11-unique_ptr-tutorial-and-examples/

这篇文章中,我们讨论C++11中的智能指针std::unique<>的用法。

什么是std::unique_str?

std::unique<>是C++11为了避免内存泄露问题,实现的智能指针之一。一个unique_ptr对象包装了一个裸指针,并负责这个裸指针的生命周期。当unique_ptr对象析构的时候,它的析构函数会释放其中的裸指针的内存。

unique_ptr重载了->和*操作符,因此可以像使用裸指针一样使用unique_ptr。

下边是一个使用unique_ptr的例子:

#include <iostream>
#include <memory>

struct Task {
    int mId;

    Task(int id ) : mId(id) {
        std::cout<<"Task::Constructor"<<std::endl;
    }

    ~Task() {
        std::cout<<"Task::Destructor"<<std::endl;
    }
};


int main(){
    // Create a unique_ptr object through raw pointer
    std::unique_ptr<Task> taskPtr(new Task(23));

    //Access the element through unique_ptr
    int id = taskPtr->mId;

    std::cout<<id<<std::endl;
    return 0;
}

输出

Task::Constructor
23
Task::Destructor

taskPtr是一个unique_ptr类型的对象,接收一个Task类型的裸指针作为构造函数。当函数退出的时候,taskPtr超出了生命周期,析构函数被调用,并释放了裸指针指向的Task对象。

因此,即使函数非正常退出(包括抛出异常),taskPtr的析构函数都会被调用,裸指针指向的Task始终都会被释放,从而避免了内存泄露。

unique pointer的独占性

一个unique_ptr对象始终独占其中的裸指针。不能复制一个unique_ptr对象,只能移动裸指针的所有权。因为独占性,unique_ptr对象总是可以安全的删除其中的裸指针,不用担心引起崩溃,也不用使用引用计数。

创建一个空的unique_ptr对象

一个创建空**unique_ptr**对象的例子:

// Empty unique_ptr object
std::unique_ptr<int> ptr1;

ptr1中没有持有(关联)任何裸指针,所以是空的。

检查一个unique_ptr对象是否为空

有两种方法检查一个unique_ptr对象是否为空:

方法一:

// Check if unique pointer object is empty
if(!ptr1)
    std::cout<<"ptr1 is empty"<<std::endl;

方法二:

// Check if unique pointer object is empty
if(ptr1 == nullptr)
    std::cout<<"ptr1 is empty"<<std::endl;
使用裸指针创建一个unique_ptr对象

将一个对应类型的裸指针传入unique_ptr对象的构造函数即可:

// Create a unique_ptr object through raw pointer
std::unique_ptr<Task> taskPtr(new Task(23));

注意,我们不能通过**=**的方式创建一个unique_ptr对象,会引发编译错误。

// std::unique_ptr<Task> taskPtr2 = new Task(); // Compile Error
重置一个unique_ptr对象

通过调用unique_ptr对象的reset()函数可以重置一个unique_ptr对象。重置对象会将内部的裸指针删除,然后unique_ptr对象变成空的。

// Reseting the unique_ptr will delete the associated
// raw pointer and make unique_ptr object empty
taskPtr.reset();
unique_ptr对象是不能复制的

unique_ptr对象是不能复制的,只能移动所有权。因此,不能创建一个unique_ptr对象的副本,无论是使用复制构造函数还是使用赋值运算符=。

// Create a unique_ptr object through raw pointer
std::unique_ptr<Task> taskPtr2(new Task(55));

// Compile Error : unique_ptr object is Not copyable
std::unique_ptr<Task> taskPtr3 = taskPtr2; // Compile error

// Compile Error : unique_ptr object is Not copyable
taskPtr = taskPtr2; //compile error

unique_ptr<> 类型禁用了复制构造函数和赋值运算符。

移动unique_ptr对象的所有权

不能复制一个unique_ptr对象,但是可以移动它对裸指针的所有权。意思就是一个unique_ptr对象可以把持有的裸指针的所有权交给另外一个unique_ptr对象。我们可以通过例子来理解:

创建一个unique_ptr对象:

// Create a unique_ptr object through raw pointer
std::unique_ptr<Task> taskPtr2(new Task(55));

现在,taskPtr2不是空的。

然后把taskPtr2对裸指针的所有权移动给另外一个unique_ptr对象。

{
    // Transfer the ownership
    std::unique_ptr<Task> taskPtr4 = std::move(taskPtr2);
    if(taskPtr2 == nullptr)
        std::cout<<"taskPtr2 is  empty"<<std::endl;
    // ownership of taskPtr2 is transfered to taskPtr4
    if(taskPtr4 != nullptr)
        std::cout<<"taskPtr4 is not empty"<<std::endl;
    std::cout<<taskPtr4->mId<<std::endl;
    //taskPtr4 goes out of scope and deletes the associated raw pointer
}

std::move() 会将taskPtr2变成一个右值引用,因此unique_ptr的移动构造函数被调用,并且将裸指针的所有权转移到taskPtr4中。

此后,taskPtr2为空。

不太理解变成右值引用这句,以后回头看看

原文:

std::move() will convert the taskPtr2 to a RValue Reference. So that move constructor of unique_ptr is invoked and associated raw pointer can be transferred to taskPtr4.

解除对裸指针的持有

通过调用unique_ptr对象的release()方法可以解除对裸指针的持有,方法返回裸指针本身。

// Create a unique_ptr object through raw pointer
std::unique_ptr<Task> taskPtr5(new Task(55));
if(taskPtr5 != nullptr)
    std::cout<<"taskPtr5 is not empty"<<std::endl;
// Release the ownership of object from raw pointer
Task * ptr = taskPtr5.release();
if(taskPtr5 == nullptr)
    std::cout<<"taskPtr5 is empty"<<std::endl;

以下是完整例子代码

#include <iostream>
#include <memory>
struct Task
{
    int mId;
    Task(int id ) :mId(id)
    {
        std::cout<<"Task::Constructor"<<std::endl;
    }
    ~Task()
    {
        std::cout<<"Task::Destructor"<<std::endl;
    }
};
int main()
{
    // Empty unique_ptr object
    std::unique_ptr<int> ptr1;
    // Check if unique pointer object is empty
    if(!ptr1)
        std::cout<<"ptr1 is empty"<<std::endl;
    // Check if unique pointer object is empty
    if(ptr1 == nullptr)
        std::cout<<"ptr1 is empty"<<std::endl;
    // can not create unique_ptr object by initializing through assignment
    // std::unique_ptr<Task> taskPtr2 = new Task(); // Compile Error
    // Create a unique_ptr object through raw pointer
    std::unique_ptr<Task> taskPtr(new Task(23));
    // Check if taskPtr is empty or it has an associated raw pointer
    if(taskPtr != nullptr)
        std::cout<<"taskPtr is  not empty"<<std::endl;
    //Access the element through unique_ptr
    std::cout<<taskPtr->mId<<std::endl;
    std::cout<<"Reset the taskPtr"<<std::endl;
    // Reseting the unique_ptr will delete the associated
    // raw pointer and make unique_ptr object empty
    taskPtr.reset();
    // Check if taskPtr is empty or it has an associated raw pointer
    if(taskPtr == nullptr)
        std::cout<<"taskPtr is  empty"<<std::endl;
    // Create a unique_ptr object through raw pointer
    std::unique_ptr<Task> taskPtr2(new Task(55));
    if(taskPtr2 != nullptr)
        std::cout<<"taskPtr2 is  not empty"<<std::endl;
    // unique_ptr object is Not copyable
    //taskPtr = taskPtr2; //compile error
    // unique_ptr object is Not copyable
    //std::unique_ptr<Task> taskPtr3 = taskPtr2;
    {
        // Transfer the ownership
        std::unique_ptr<Task> taskPtr4 = std::move(taskPtr2);
        if(taskPtr2 == nullptr)
            std::cout<<"taskPtr2 is  empty"<<std::endl;
        // ownership of taskPtr2 is transfered to taskPtr4
        if(taskPtr4 != nullptr)
            std::cout<<"taskPtr4 is not empty"<<std::endl;
        std::cout<<taskPtr4->mId<<std::endl;
        //taskPtr4 goes out of scope and deletes the assocaited raw pointer
    }
    // Create a unique_ptr object through raw pointer
    std::unique_ptr<Task> taskPtr5(new Task(55));
    if(taskPtr5 != nullptr)
        std::cout<<"taskPtr5 is not empty"<<std::endl;
    // Release the ownership of object from raw pointer
    Task * ptr = taskPtr5.release();
    if(taskPtr5 == nullptr)
        std::cout<<"taskPtr5 is empty"<<std::endl;
    std::cout<<ptr->mId<<std::endl;
    delete ptr;
    return 0;
}

输出:

ptr1 is empty
ptr1 is empty
Task::Constructor
taskPtr is  not empty
23
Reset the taskPtr
Task::Destructor
taskPtr is  empty
Task::Constructor
taskPtr2 is  not empty
taskPtr2 is  empty
taskPtr4 is not empty
55
Task::Destructor
Task::Constructor
taskPtr5 is not empty
taskPtr5 is empty
55
Task::Destructor
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值