07 Smart Pointers

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值