首页 > 编程语言 > 详细

[c++] Why should I use Smart Pointers

时间:2020-04-25 20:19:11      阅读:44      评论:0      收藏:0      [点我收藏+]

深入理解智能指针


 

共享指针 - 热身问题

一、学习资源

C++中的智能指针

Ref: https://zhuanlan.zhihu.com/p/71649913

技术分享图片

 

二、销毁

[ 这部分仅关于:shared_ptr ]

vector销毁,vector中的指针们所指向的各自“空间“也需要销毁。

vector的某个指针改变,相联系的指针内容全部改变。

技术分享图片
// util/sharedptr1.cpp
#include <iostream>
#include <string>
#include <vector>
#include <memory>
using namespace std;
int main()
{
// two shared pointers representing two persons by their name
    shared_ptr<string> pNico(new string("nico"));
    shared_ptr<string> pJutta(new string("jutta"));
// capitalize person names
    (*pNico)[0] = ’N’;
    pJutta->replace(0,1,"J");
// put them multiple times in a container
    vector<shared_ptr<string>> whoMadeCoffee;
    whoMadeCoffee.push_back(pJutta);
    whoMadeCoffee.push_back(pJutta);
    whoMadeCoffee.push_back(pNico);
    whoMadeCoffee.push_back(pJutta);
    whoMadeCoffee.push_back(pNico);
// print all elements
    for (auto ptr : whoMadeCoffee) {
        cout << *ptr << " ";
    }
    cout << endl;
// overwrite a name again
    *pNico = "Nicolai";
// print all elements again
    for (auto ptr : whoMadeCoffee) {
        cout << *ptr << " ";
    }
    cout << endl;
// print some internal data
    cout << "use_count: " << whoMadeCoffee[0].use_count() << endl;
}
View Code

 

 

定义销毁行为

如果某个指针要放弃某一块内存时,使用 lambda 作为构造函数的第二个参数,定义销毁操作。

当对象的最后一个指针被调用时,lambda被调用。

shared_ptr<string> pNico(new string("nico"),
                         [](string* p) {
                                         cout << "delete " << *p << endl;
                                         delete p;
                                       }
);
...
pNico = nullptr; // pNico does not refer to the string any longer
whoMadeCoffee.resize(2); // all copies of the string in pNico are destroyed

 

 

处理数组

默认的销毁行为不会执行delete[],所以要自己定义销毁行为,例如:

std::shared_ptr<int> p(new int[10],
                       [](int* p) {
                         delete[] p;  // <---- lambda
                       }
);

 

另一种方式也可以:

std::shared_ptr<int> p(new int[10],
                       std::default_delete<int[]>());

 

 

清理临时文件

处理清理内存以外,还有其他的资源需要处理,以下是一个清理临时文件的示例:

共享指针,指向 ”输出流“ 这个 ”对象“。

#include <string>
#include <fstream> // for ofstream
#include <memory>  // for shared_ptr
#include <cstdio>  // for remove()

class FileDeleter { private: std::string filename;
public: FileDeleter (const std::string& fn): filename(fn) {
}
void operator () (std::ofstream* fp) { fp->close(); // close.file std::remove(filename.c_str()); // delete file } };
int main() { // create and open temporary file: std::shared_ptr<std::ofstream> fp( new std::ofstream("tmpfile.txt"), FileDeleter("tmpfile.txt") ); ... }

 

 

清理共享内存

shm_unlink主要用于linux Posix模式共享内存中的删除共享内存。

// util/sharedptr3.cpp
#include <memory>      // for shared_ptr
#include <sys/mman.h>  // for shared memory
#include <fcntl.h>
#include <unistd.h>
#include <cstring>     // for strerror()
#include <cerrno>      // for errno
#include <string>
#include <iostream>
class SharedMemoryDetacher { public: void operator () (int* p) { std::cout << "unlink /tmp1234" << std::endl; if (shm_unlink("/tmp1234") != 0) { std::cerr << "OOPS: shm_unlink() failed" << std::endl; } } };

std::shared_ptr
<int> getSharedIntMemory(int num) { void* mem;
int shmfd = shm_open("/tmp1234", O_CREAT|O_RDWR, S_IRWXU|S_IRWXG); if (shmfd < 0) { throw std::string(strerror(errno)); } if (ftruncate(shmfd, num*sizeof(int)) == -1) { throw std::string(strerror(errno)); }
mem
= mmap(nullptr, num*sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED, shmfd, 0); if (mem == MAP_FAILED) { throw std::string(strerror(errno)); } return std::shared_ptr<int>(static_cast<int*>(mem), SharedMemoryDetacher()); }
int main() { // get and attach shared memory for 100 ints: std::shared_ptr<int> smp(getSharedIntMemory(100));
// init the shared memory for (int i = 0; i < 100; ++i) { smp.get()[i] = i*42; }
// deal with shared memory somewhere else: ... std::cout << "<return>" << std::endl; std::cin.get();
// release shared memory here: smp.reset(); ... }

 

 

析构函数清理

当然还有一种更加清晰的实现方法:构造函数实现初始化,析构函数实现清理。这样就可以简单地使用shared_ptr管理对象。

 

三、初始化

(1) 为了避免隐式转换,智能指针不能使用赋值的方式初始化,使用括号初始化或者列表初始化是没有问题的。

(2) 另一种初始化的方法是使用make_shared<>,它是一种 更好且更安全 的方法:因为使用new时会创建一个对象,计算它的引用计数时会创建一个对象,而make_shared只会创建一个对象,并且不会出现控制模块失效的情况。

shared_ptr<string> p1 = make_shared<string>(10, 9);  
shared_ptr<string> p2 = make_shared<string>("hello");  
shared_ptr<string> p3 = make_shared<string>(); 

(3) 先定义一个智能指针再进行赋值。但是不能使用赋值运算符(=),必须使用reset函数。

shared_ptr<string> pNico4;
pNico4 = new string("nico"); // ERROR: no assignment for ordinary pointers
pNico4.reset(new string("nico")); // OK

 

 

 

类 weak_ptr

一、shared_ptr 的弊端

使用shared_ptr的主要原因是不想关注资源的释放。但是某些情况下shared_ptr会出现错误或者失效。

1、循环引用问题:如果两个对象使用shared_ptr指向彼此,当释放的时候,将不会自动释放资源,因为引用计数的计算有问题。

2、当共享对象的时候,指针的生命期要长于对象,因此对象不会被shared_ptr删除。指针就无法注意到对象是否释放。

 

 

 

 

如果一个资源将被多层传递和访问,那么参数类型该用 weak_ptr 还是 shared_ptr?

 

如何评价 C++11 的右值引用(Rvalue reference)特性?

unique_ptr相关

 

 /* implement */

 

[c++] Why should I use Smart Pointers

原文:https://www.cnblogs.com/jesse123/p/12774971.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!