欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

07 Smart Pointers

程序员文章站 2022-03-11 17:26:35
...

STL提供了两大类smart pointer
1. shared_ptr
实现共享式拥有。多个smart pointer可以指向相同的对象, 该对象和其相关资源会在"最后一个reference"被销毁是释放。STL提供了 weak_ptr、bad_weak_ptr和enable_shared_from_this 等辅助类
2. unique_ptr
实现独占式拥有。保证同一时间内只有一个smart pointer可以指向该对象, 对于避免内存泄漏特别有用。

shared_ptr

目标: 在其所指向的对象不再被需要之后, 自动释放对象的相关资源。
可以向任何其他指针一样使用shared_ptr, 赋值、拷贝、比较, 使用 *和->方位其指向的对象

#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;
}

可以使用 make_shared() 函数

shared_ptr<string> pNico(new string("nico"));
shared_ptr<string> pJutta(new string("jutta"));
shared_ptr<string> pNico = make_shared<string>("nico");
shared_ptr<string> pJutta = make_shared<string>("jutta");

1. 定义删除方法 Deleter

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

2. 处理数组 array

shared_ptr默认提供的是delete而不是delete[], 这就表示它不能直接用来处理数组

std::shared_ptr<int> p(new int[10]); // ERROR, but compiles

对于使用 new[] 创建的对象, 需要定义一个自己的Dleter
也可以使用为 unique_ptr 而提供的辅助函数 default_delete()

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

3. 处理其他销毁

实例一: 处理文件

#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"));
	...
}

实例二: 处理共享内存

#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();
	...
}

4. weak_ptr

unique_ptr

在发生异常时可以避免内存泄漏的smart指针, 可确保一个对象和其资源同一时间只被一个pointer拥有。一旦拥有者被销毁或变成empty, 或开始拥有另一个对象, 先前拥有的那个对象就会被销毁, 其任何相应资源也会被释放。
unique_ptr继承auto_ptr, auto_ptr已不再被认可
unique_ptr与普通指针相近, 使用*访问指向的对象, -> 访问对象的成员

1. 使用 unique_ptr

一般为了避免异常内存泄漏, 代码格式会繁琐

void f()
{
	ClassA* ptr = new ClassA; // create an object explicitly
	try {
		... // perform some operations
	}
	catch (...) { // for any exception
		delete ptr; // - clean up
		throw; // - rethrow the exception
	}
	delete ptr; // clean up on normal end
}

如果使用 unique_ptr

void f()
{
	// create and initialize an unique_ptr
	std::unique<ClassA> ptr(new ClassA);
	... // perform some operations
}

(1) 内容访问

// create and initialize (pointer to) string:
std::unique_ptr<std::string> up(new std::string("nico"));
(*up)[0] = ’N’; // replace first character
up->append("lai"); // append some characters
std::cout << *up << std::endl; // print whole string

(2) 该指针不提供指针算术, 例如 ++ 运算
(3) 不允许使用普通指针初始化

std::unique_ptr<int> up = new int; // ERROR
std::unique_ptr<int> up(new int); // OK

(4) 可以定义空指针

std::unique_ptr<std::string> up;

(5) 可以向bool一样判断

if (up) { // if up is not empty
	std::cout << *up << std::endl;
}
if (up != nullptr) // if up is not empty
if (up.get() != nullptr) // if up is not empty

(6) 把unique_ptr作为成员

class ClassB {
private:
	std::unique_ptr<ClassA> ptr1; // unique_ptr members
	std::unique_ptr<ClassA> ptr2;
public:
	// constructor that initializes the unique_ptrs
	// - no resource leak possible
	ClassB (int val1, int val2): ptr1(new ClassA(val1)), ptr2(new ClassA(val2)) {
	}
	// copy constructor
	// - no resource leak possible
	ClassB (const ClassB& x): ptr1(new ClassA(*x.ptr1)), ptr2(new ClassA(*x.ptr2)) {
	}
	// assignment operator
	const ClassB& operator= (const ClassB& x) {
		*ptr1 = *x.ptr1;
		*ptr2 = *x.ptr2;
		return *this;
	}
	// no destructor necessary
	// (default destructor lets ptr1 and ptr2 delete their objects)
	...
};

2. 数组处理

C++无法分辨指针指向的是对象还是数组, 然而, 删除对象用delete, 删除数组需要用 delete[]
下面是一个错误的定义

std::unique_ptr<std::string> up(new std::string[10]); // runtime ERROR

STL提供了一个特化版本用来处理数组

std::unique_ptr<std::string[]> up(new std::string[10]); // OK

这个版本不再使用*和->, 而是用[]

std::unique_ptr<std::string[]> up(new std::string[10]); // OK
std::cout << *up << std::endl; // ERROR: * not defined for arrays
std::cout << up[0] << std::endl; // OK

3. 其他资源的删除 Deleter

#include <iostream>
#include <string>
#include <memory> // for unique_ptr
#include <dirent.h> // for opendir(), ...
#include <cstring> // for strerror()
#include <cerrno> // for errno
using namespace std;
class DirCloser
{
public:
	void operator () (DIR* dp) {
	if (closedir(dp) != 0) {
			std::cerr << "OOPS: closedir() failed" << std::endl;
		}
	}
};

int main()
{
	// open current directory:
	unique_ptr<DIR,DirCloser> pDir(opendir("."));
	// process each directory entry:
	struct dirent *dp;
	while ((dp = readdir(pDir.get())) != nullptr) {
		string filename(dp->d_name);
		cout << "process " << filename << endl;
		...
	}
}

 

相关标签: C++ STL c++