c+智能指针源码分析

In this article, we’ll take a look at how we can use Smart Pointers in C++.

在本文中,我们将研究如何在C ++中使用智能指针

Smart Pointers are an abstract interface to actual pointers (also called raw pointers), but with the additional benefit of automatically managing resources and freeing memory.

智能指针是实际指针(也称为原始指针 )的抽象接口,但具有自动管理资源和释放内存的其他好处。

Smart Ptr Example
Smart Ptr示例

The design logic for a smart pointer is to implement it as a class.

智能指针的设计逻辑是将其实现为类。

This is because a class has a destructor, which will trigger automatically when an object is at the end of its scope. Smart Pointers use the destructors to free up resources automatically.

这是因为类具有析构函数 ,当对象位于其作用域的末尾时,它将自动触发。 智能指针使用析构函数自动释放资源。

You don’t need to free smart pointers explicitly, and hence they are “smart” pointers since they know when they need to free resources.

您无需显式释放智能指针,因此它们是“智能”指针,因为它们知道何时需要释放资源。

Let’s understand more about these types of smart pointers in C++, using examples.

通过示例,让我们更多地了解C ++中的这些智能指针类型。



在C ++中使用智能指针 (Using Smart Pointers in C++)

These pointers are defined in the standard C++ library, in the std namespace. To include some of these pointers, we also need the <memory> header file.

这些指针是在标准C ++库的std名称空间中定义的。 要包括其中一些指针,我们还需要<memory>头文件。

There are three commonly used smart pointers, called:

有三种常用的智能指针,称为:

  • unique_ptrunique_ptr
  • shared_ptrshared_ptr
  • weak_ptrweak_ptr

Let’s go through them one by one.

让我们一一介绍。



unique_ptr – C ++中的智能指针 (unique_ptr – Smart pointer in C++)

This is a smart pointer that will only permit one owner of the pointer. That is, unique_ptr can contain, at maximum, only a single raw pointer (“unique” pointer) that points to a single memory location.

这是一个智能指针,将只允许该指针的一个所有者。 也就是说, unique_ptr最多只能包含一个指向单个内存位置的原始指针 (“唯一”指针)。

Keep this as your default choice, unless you’re working with multiple threads, or whenever you need multiple owners to the same raw pointer.

除非您正在使用多个线程,或者需要多个所有者使用同一原始指针,否则请将其保留为默认选择。

To create a smart pointer using unique_ptr, there are two ways of achieving this:

要使用unique_ptr创建智能指针,有两种方法可以实现:

情况1:从对象创建唯一的智能指针 (Case 1: Creating a Unique Smart Pointer from an object)

If you have an object and want to create a smart pointer in C++ which points to this object, use the below syntax:

如果您有一个对象,并且想要在C ++中创建指向该对象的智能指针,请使用以下语法:


std::unique_ptr<myType> my_ptr(new MyType());

The above line creates a new raw pointer of type MyTyp. We pass this to create our smart pointer using std::unique_ptr<myType>.

上一行创建了一个MyTyp类型的新原始指针。 我们通过使用std::unique_ptr<myType>来创建智能指针。

You can now call methods on the object using the smart pointer handle, using the -> operator.

现在,您可以使用智能指针手柄,通过->运算符在对象上调用方法。


// Call a method on the object
my_ptr->ObjectMethod();

You can also free the memory that my_ptr owns, using:

您还可以使用以下方法释放my_ptr拥有的内存:


my_ptr.reset();

We use the . operator, since we apply this to the smart pointer directly.

我们使用. 运算符,因为我们将其直接应用于智能指针。

情况2:构造一个指向存储位置的新的唯一指针 (Case 2: Construct a new unique pointer to a memory location)

If you want to construct a new unique pointer, simply use the make_unique() function.

如果要构造一个新的唯一指针,只需使用make_unique()函数。


std::unique_ptr<std::string> = std::make_unique<std::string>("Hello from JournalDdev");

This creates a unique pointer to the string “Hello from JournalDev”.

这将创建一个指向字符串“ Hello from JournalDev”的唯一指针。

Other operations remain the same.

其他操作保持不变。

移动唯一的指针 (Move a unique pointer)

Since a unique_ptr is unique for any memory location, we cannot have a unique pointer to multiple objects. So we can only move a unique_ptr.

由于unique_ptr对于任何内存位置都是唯一的,因此我们不能拥有指向多个对象的唯一指针。 因此,我们只能移动unique_ptr。


std::unique_ptr<int> unique_ptr_1(new int(1000));
// Move the raw pointer from unique_ptr_1 TO unique_ptr_2
std::unique_ptr<int> unique_ptr_2 = std::move(unique_ptr_1);// Now, the data is pointed to by unique_ptr_2
unique_ptr_2->doSomething();

Let’s now look at an example to see how the whole thing works:

现在让我们看一个示例,以了解整个过程如何工作:


#include <iostream>
#include <memory>class MyClass{public:int a;MyClass() { std::cout << "Default Constructor\n"; a = 10; }MyClass(int value = 100) { std::cout << "Parametrized Constructor\n"; a = value; }
};int main() {// Object of MyClassMyClass my_obj(250);// Construct the pointer using make_unique()std::unique_ptr<MyClass> my_ptr_1 = std::make_unique<MyClass>(my_obj);// Create a new object and get the unique_ptr to itstd::unique_ptr<MyClass> my_ptr_2(new MyClass(500));std::cout << "Value of a (my_ptr_1): " << my_ptr_1->a << std::endl;std::cout << "Value of a (my_ptr_2): " << my_ptr_2->a << std::endl;// Both the pointers will automatically get destroyed, since the objects are at the end of their scopes// But if we want to explicitly free the raw pointer, we can use unique_ptr.reset()std::cout << "Freeing the pointers...\n";my_ptr_1.reset();my_ptr_2.reset();return 0;
}

Output

输出量


Parametrized Constructor
Parametrized Constructor
Value of a (my_ptr_1): 250
Value of a (my_ptr_2): 500
Freeing the pointers...


shared_ptr (shared_ptr)

The shared_ptr is another choice for a smart pointer in C++, if you want to deal with multiple owners. This also maintains a reference count of all the pointers which point to the object.

如果要与多个所有者打交道, shared_ptr是C ++中智能指针的另一种选择。 这还将维护指向该对象的所有指针的引用计数。

Similar to unique_ptr, the syntax is almost the same, except that you now return a shared pointer instead. You are also now allowed to pass multiple objects to the invocation, so that the shared_ptr points to all of them.

unique_ptr相似,语法几乎相同,除了现在返回一个共享指针。 现在还允许您将多个对象传递给调用,以便shared_ptr指向所有这些对象。

To construct a new shared pointer, use the make_shared() function.

要构造一个新的共享指针,请使用make_shared()函数。


// my_ptr points to two objects
std::unique_ptr<myType> my_ptr(new MyType(1), new MyType(2));// A shared pointer to 2 strings
std::unique_ptr<string> my_str_ptr = std::make_shared<string>("Hello", "How are you?");

There is no requirement of using std::move, since a shared pointer can point to multiple locations.

不需要使用std::move ,因为共享指针可以指向多个位置。

To get the current reference count for any/all of the shared pointers, use my_shared_ptr.use_count(). This will automatically update when we create or free a shared pointer to the common object.

要获取任何/所有共享指针的当前引用计数,请使用my_shared_ptr.use_count() 。 当我们创建或释放指向公共对象的共享指针时,它将自动更新。

We’ll look at an example regarding shared_ptr.

我们将看一个有关shared_ptr的示例。


#include <iostream>
#include <memory>class MyClass{public:int a;MyClass() { std::cout << "Default Constructor\n"; a = 10; }MyClass(int value = 100) { std::cout << "Parametrized Constructor\n"; a = value; }
};int main() {// Objects of MyClassMyClass my_obj(250);// Construct a pointer using make_sharedstd::shared_ptr<MyClass> my_ptr_1 = std::make_shared<MyClass>(my_obj);std::cout << "Current Reference Count of my_ptr_1 = " << my_ptr_1.use_count() << std::endl;// And another shared pointer to the same location!std::shared_ptr<MyClass> my_ptr_2 = my_ptr_1;std::cout << "Current Reference Count of my_ptr_1 = " << my_ptr_1.use_count() << std::endl;std::cout << "Current Reference Count of my_ptr_2 = " << my_ptr_2.use_count() << std::endl;std::cout << "Value of a (from dereferencing my_ptr_1): " << my_ptr_1->a << std::endl;std::cout << "Value of a (from dereferencing my_ptr_2): " << my_ptr_2->a << std::endl;// Both the pointers will automatically get destroyed, since the objects are at the end of their scopes// But if we want to explicitly free the raw pointer, we can use unique_ptr.reset()std::cout << "Freeing the pointers...\n";my_ptr_1.reset();std::cout << "Freed my_ptr_1\n";std::cout << "Current Reference Count of my_ptr_1 = " << my_ptr_1.use_count() << std::endl;std::cout << "Current Reference Count of my_ptr_2 = " << my_ptr_2.use_count() << std::endl;my_ptr_2.reset();std::cout << "Freed my_ptr_2\n";std::cout << "Current Reference Count of my_ptr_1 = " << my_ptr_1.use_count() << std::endl;std::cout << "Current Reference Count of my_ptr_2 = " << my_ptr_2.use_count() << std::endl;return 0;
}

Output

输出量


Parametrized Constructor
Current Reference Count of my_ptr_1 = 1
Current Reference Count of my_ptr_1 = 2
Current Reference Count of my_ptr_2 = 2
Value of a (from dereferencing my_ptr_1): 250
Value of a (from dereferencing my_ptr_2): 250
Freeing the pointers...
Freed my_ptr_1
Current Reference Count of my_ptr_1 = 0
Current Reference Count of my_ptr_2 = 1
Freed my_ptr_2
Current Reference Count of my_ptr_1 = 0
Current Reference Count of my_ptr_2 = 0

As you can observe, both pointers point to the same object. After any one of them is freed, again, the reference counts are updated to reflect the changes.

如您所见,两个指针都指向同一对象。 在释放其中任何一个之后,再次更新参考计数以反映更改。



weak_ptr (weak_ptr)

The weak_ptr is a smart pointer in C++ that is similar to shared_ptr, but it does not maintain a reference count. This is useful in cases where objects of one class can point to another, and vice-versa.

weak_ptr是C ++中的一个智能指针,与shared_ptr相似,但是它不维护引用计数。 这在一个类的对象可以指向另一类,反之亦然的情况下很有用。

Consider the following scenario, where you have two classes A and B, having a shared_ptr member to the other class (i.e an object of A can point to an object of B, and vice-versa). Now, when you create two objects of A and B, make them point to each other, using the shared_ptr member, what happens now?

考虑以下情形,其中有两个类AB ,另一个类具有shared_ptr成员(即A的对象可以指向B的对象,反之亦然)。 现在,当您创建AB两个对象时,使用shared_ptr成员将它们指向彼此,现在会发生什么?


#include <iostream>
#include <memory>// Forward declare class B
class B;class A{public:int a;std::shared_ptr<B> ptr;A(int value = 200) { a = value; }~A() {std::cout << "Destructor for A\n"; }
};class B{public:int a;std::shared_ptr<A> ptr;B(int value = 200) { a = value; }~B() {std::cout << "Destructor for B\n"; }
};int main() {std::shared_ptr<A> ptr_a = std::make_shared<A>(750);std::shared_ptr<B> ptr_b = std::make_shared<B>(750);// Make ptr_a point to ptr_bptr_a->ptr = ptr_b;// And make ptr_b point to ptr_aptr_b->ptr = ptr_a;return 0;
}

Output

输出量

Yes, we don’t get any output, even though we expect our destructor to clean-up for us! What is happening?!!

是的,即使我们希望析构函数可以为我们清理,我们也不会得到任何输出! 怎么了?!!

This is an example of a Circular Reference, where pointers point to each other. Since these pointers are shared_ptrs, they also have their reference counts as 2.

这是循环引用的示例,其中的指针相互指向。 由于这些指针是shared_ptr ,因此它们的引用计数也为2。

Circular Reference Shared Ptrs
循环参考共享Ptr

When the destructor of the ptr_a‘s object tries to clean-up, it finds out that ptr_b is pointing to ptr_a, so it cannot simply remove it. Similarly, the destructor for ptr_bs object also cannot clean-up, resulting in no output!

ptr_a对象的析构函数尝试清除时,它会发现ptr_b指向ptr_a ,因此无法简单地将其删除。 同样, ptr_b对象的析构函数也无法清除,导致没有输出!

To eliminate this problem, we can use the weak_ptr smart pointer inside our class. Since the weak pointer does not contribute to the reference count, we can make one of the two pointers as a weak pointer, for example, ptr_b to ptr_a.

为了消除这个问题,我们可以在类内部使用weak_ptr智能指针。 由于弱指针不会增加引用计数,因此我们可以将两个指针之一作为弱指针,例如ptr_bptr_a

Since a weak pointer does not own the data, we can now clean up the shared pointer a, after which we can clear b also!

由于弱指针不拥有数据,因此我们现在可以清除共享指针a ,之后我们也可以清除b

So, we can make the ptr member of class B as a weak_ptr. (You could also do it for A, but we’ll do it for B)

因此,我们可以将B类的ptr成员设置为weak_ptr 。 (您也可以为A做,但是我们将为B做)

Resolve Dependency
解决依赖性

Let’s now make the changes to our code snippet to make this work.

现在,让我们对代码段进行更改以使其工作。


#include <iostream>
#include <memory>// Forward declare class B
class B;class A{public:int a;std::shared_ptr<B> ptr;A(int value = 200) { a = value; }~A() {std::cout << "Destructor for A\n"; }
};class B{public:int a;std::weak_ptr<A> ptr;B(int value = 200) { a = value; }~B() {std::cout << "Destructor for B\n"; }
};int main() {std::shared_ptr<A> ptr_a = std::make_shared<A>(750);std::shared_ptr<B> ptr_b = std::make_shared<B>(750);// Make ptr_a point to ptr_bptr_a->ptr = ptr_b;// And make ptr_b point to ptr_a// Since ptr_b->ptr is a weak pointer, we don't have the problem now!ptr_b->ptr = ptr_a;return 0;
}

Output

输出量


Destructor for A
Destructor for B

Now, we’ve changed the type of the smart pointer of class B to weak_ptr. So now, there is no more circular dependency, and we’ve finally solved this problem!

现在,我们将类B的智能指针的类型更改为weak_ptr 。 因此,现在不再有循环依赖项,我们终于解决了这个问题!



通用编程准则 (General Programming Guidelines)

To summarize this article, I’ll provide you with a small list of guidelines to follow when working with SMART pointers in C++.

总结本文,我将为您提供一小部分准则,以便在C ++中使用SMART指针时要遵循。

  • Always try to use smart pointers whenever you can. In most cases, use unique_ptr, if you’re not dealing with sharing a memory location with multiple pointers/threads.尽可能尝试使用智能指针 。 在大多数情况下,如果您不打算与多个指针/线程共享内存位置,请使用unique_ptr
  • Otherwise, use the reference-counted shared_ptr, when dealing with multiple owners.否则,在与多个所有者打交道时,请使用引用计数的shared_ptr
  • If you want to examine an object, but not require that the object itself exists, use a weak_ptr. This pointer does not contribute to the reference count and is suitable for such tasks.如果要检查对象,但不要求对象本身存在,请使用weak_ptr 。 该指针不会增加引用计数,并且适合此类任务。
  • Whenever you directly use raw pointers, try to make sure it is contained only in a very small amount of code, or where you must absolutely use it, to reduce the overhead of using smart pointers.每当您直接使用原始指针时 ,请尝试确保仅将其包含在非常少量的代码中或必须绝对使用的地方,以减少使用智能指针的开销。


结论 (Conclusion)

In this article, we learned how we could use smart pointers in C++, along with some examples for each type.

在本文中,我们学习了如何在C ++中使用智能指针,以及每种类型的一些示例。

For more articles, go through our C++ tutorials section! Do leave any feedback in the comment section below!

有关更多文章,请浏览我们的C ++教程部分 ! 请在下面的评论部分中留下任何反馈!

参考资料 (References)

  • Microsoft Documentation on Smart Pointers (Is a very good resource, even if you’re working on Linux. Highly recommended read)Microsoft关于智能指针的文档 (即使您使用的是Linux,这也是非常好的资源。强烈建议阅读)


翻译自: https://www.journaldev.com/37481/smart-pointers-in-c-plus-plus

c+智能指针源码分析

c+智能指针源码分析_C ++中的智能指针相关推荐

  1. 【C++】Android (Light)RefBase-sp-wp引用计数-智能指针源码分析

    文章目录 1.RefBase简介 2.RefBase源码分析 3.RefBase使用注意事项 4.总结 1.RefBase简介 什么是RefBase?RefBase是Android中的一个C++类,用 ...

  2. 以太坊C++客户端Aleth源码分析,转账交易和智能合约的入口代码

    本文主要记录以太坊C++客户端Aleth的源码分析和相关实验过程和结果.本文将讲解两部分的内容,一是转账交易和智能合约的入口代码在哪里?二是通过实验验证转账交易和智能合约交易这两种不同交易所对应的不同 ...

  3. 集合的get方法中参数从多少开始_源码分析CopyOnWriteArrayList 中的隐藏知识,你Get了吗?...

    欢迎点击 "未读代码" ,关注公众号,文章每周更新 杭州-阿里园区墙 前言 本觉 CopyOnWriteArrayList 过于简单,寻思看名字就能知道内部的实现逻辑,所以没有写这 ...

  4. android 代码 drawable,Android Drawable完全解析(一):Drawable源码分析(中)

    呃...我不是故意要凑篇幅写个什么上下篇,实在是因为Drawable源码有点长,一篇写不下啦O(∩_∩)O~ 鉴于源码一般较长,以后所有源码分析的部分,英文注释非必要情况都不再保留! 2:Drawab ...

  5. 深入源码分析SpringBoot中使用@ConditionalOnBean无效的问题(@ConditionalOnBean did not find any beans of type)

    一.前言 最近在使用SpringBoot的@ConditionalOnBean的时候遇到一个很很奇特的问题.即在@Bean中使用@ConditionalOnBean注解,在可以确保需要依赖的Bean一 ...

  6. 源码分析 vue-cli 中安装依赖

    最近想要自己实现一个 cli 看了 vue-cli 源码中安装依赖的部分 源码文件 @vue/cli/lib/util/ProjectPackageManager.js 有一个 install 函数执 ...

  7. [EOS源码分析]6.EOS特殊智能合约eosio

    这里说的eosio智能合约不是泛指eos的智能合约,它是一个特殊的具体的合约.它本事可大了,我们一起来看看它有哪些功能 负责智能合约部署 大家有注意到如下红色字体的log吗 $ cleos set c ...

  8. 【VUE】源码分析 - vue中的 HTMLParser,模板解析的第一步

      tip:本系列博客的代码部分(示例等除外),均出自vue源码内容,版本为2.6.14.但是为了增加易读性,会对不相关内容做选择性省略.如果大家想了解完整的源码,建议自行从官方下载. GitHub ...

  9. WeIdentity智能合约源码分析

    WeIdentity智能合约 介绍 本文结合WeIdentity智能合约文档对其源码进行阅读分析.当前,WeIdentity合约层面的工作目标主要包括两部分: WeIdentity DID智能合约,负 ...

最新文章

  1. 赠票 | 重磅揭晓Flink Forward Asia 2019完整议程!
  2. python中.whl文件下载,pandas
  3. 你在听音乐的时候能否感受到背后有“寒意“?
  4. jvm性能调优实战 - 34十万QPS的社交APP 如何优化GC性能提升3倍?
  5. cmd.exe_参数_启动参数
  6. ACM-数论 —— 一.整除的性质
  7. delphi 常用属性/方法《转》
  8. cocos2d的常用动作及效果总结之五:Animation
  9. kafka 消费端 api_在消费者的眼中:您真的需要为您的API提供客户端库吗?
  10. java.sql.SQLException: ORA-00604: 递归 SQL 级别 1 出现错误
  11. Cause: java.sql.SQLException: Could not retrieve transation read-only status server
  12. 华为不可参与 IEEE 审稿但可继续提供赞助;谷歌限制 Chrome 接口惹非议;Mozilla 号召用户换火狐 | 开发者周刊...
  13. 反向代理——Nginx
  14. 信息熵及其Python的实现
  15. Snaker的回退流程源码分析
  16. c语言点亮共阴极数码管,课程设计-基于单片机的共阴极数码管显示电路.doc
  17. var foo = 1; (function foo() { foo = 100; console.log(foo); }()) console.log(foo);
  18. 当谈论研发效能时,我们到底在谈什么?|大咖圆桌精华回顾
  19. 事件10001,10016,10037
  20. DCU Streamer Prefetcher

热门文章

  1. 解决点击锚点置顶内容被导航遮住
  2. Java基础知识强化87:BigInteger类之BigInteger加减乘除法的使用
  3. Shell-杀死指定进程
  4. 2nbsp;时间管理和内存管理
  5. [转载] Python中生成器和迭代器的区别
  6. [转载] Python字符串函数
  7. BZOJ 4034 树上操作
  8. BSD Socket~TCP~Example Code
  9. TSS ESS RSS
  10. java 之UDP编程