【C++ Primer】查缺补漏(一)动态内存

【C++ Primer】查缺补漏(一)动态内存


前言

本文是笔者回顾复习c++ primer时候对遗忘点的巩固笔记,非详细学习笔记

一、动态内存与智能指针

C++中的动态内存管理通过一对运算符完成:new在动态内存中为对象分配空间并返回指向该对象的指针,可以选择对对象进行初始化;delete接受一个动态对象的指针,销毁该对象并释放与之关联的内存。

新标准库提供了两种智能指针(smart pointer)类型来管理动态对象。智能指针的行为类似常规指针,但它自动释放所指向的对象。这两种智能指针的区别在于管理底层指针的方式:shared_ptr允许多个指针指向同一个对象;unique_ptr独占所指向的对象。标准库还定义了一个名为weak_ptr的伴随类,它是一种弱引用,指向shared_ptr所管理的对象。这三种类型都定义在头文件memory中。

题外话:new和malloc的区别

shared_ptr类

	shared_ptr<string> p1;      // shared_ptr that can point at a string
	shared_ptr<list<int>> p2;   // shared_ptr that can point at a list of ints

shared_ptr独有的操作:

操作含义
p.use_count()返回p的引用计数
p.unique()p.use_count() == 1,则返回true ,一旦一个shared_ptr的引用计数变为0,它就会自动释放其所管理的对象。

一些示例:

 	// shared_ptr that can point at a string
	shared_ptr<string> p1; 
	// shared_ptr that can point at a list of 
	shared_ptr<list<int>> p2; 
	// shared_ptr that points to an int with value 42
	shared_ptr<int> p3 = make_shared<int>(42);
	// p4 points to a string with value 9999999999
	shared_ptr<string> p4 = make_shared<string>(10, '9');
	// p5 points to an int that is value initialized
	shared_ptr<int> p5 = make_shared<int>();
	// object to which p points has one user
	auto p = make_shared<int>(42);
	// p and q point to the same object
    // object to which p and q point has two users
	auto q(p); 

程序使用动态内存通常出于以下三种原因:

  • 不确定需要使用多少对象。

  • 不确定所需对象的准确类型。

  • 需要在多个对象间共享数据。

例子:定义StrBlob类

#include<vector>
#include<string>
#include<memory>
class StrBlob
{
public:
	typedef std::vector<std::string>::size_type size_type;
	StrBlob();
	StrBlob(std::initializer_list<std::string> il);
	size_type size() const { return data->size(); }
	bool empty() const { return data->empty(); }
	// 添加和修改元素
	void push_back(const std::string& t) { data->push_back(t); }
	void pop_back();
	//元素访问
	std::string& front();
	std::string& back();
private:
	std::shared_ptr<std::vector<std::string>> data;
	void check(size_type i, const std::string& msg) const;
};

保证Blob的对象b1和b2中任意一个销毁时vector中的元素继续存在

构造函数

StrBlob::StrBlob(): data(make_shared<vector<string>>()){}
StrBlob::StrBlob()(initializer_list<std::string> il): 
					data(make_shared<vector<string>>()){}

shared_ptrnew结合使用

shared_ptr<int> p1 = new int(1024);    // error: must use direct initialization
shared_ptr<int> p2(new int(1024));     // ok: uses direct initialization

不要混合使用内置指针和智能指针。当将shared_ptr绑定到内置指针后,资源管理就应该交由shared_ptr负责。不应该再使用内置指针访问shared_ptr指向的内存。

// ptr is created and initialized when process is called
void process(shared_ptr<int> ptr)
{
    // use ptr
}   // ptr goes out of scope and is destroyed

int *x(new int(1024));   // dangerous: x is a plain pointer, not a smart pointer
process(x);     // error: cannot convert int* to shared_ptr<int>
process(shared_ptr<int>(x));    // legal, but the memory will be deleted!
int j = *x;     // undefined: x is a dangling pointer!

shared_ptr<int> p(new int(42));   // reference count is 1
process(p);     // copying p increments its count; in process the reference count is 2
int i = *p;     // ok: reference count is 1

也不要使用get初始化另一个智能指针或为智能指针赋值。

shared_ptr<int> p(new int(42));    // reference count is 1
int *q = p.get();   // ok: but don't use q in any way that might delete its pointer
{   // new block
    // undefined: two independent shared_ptrs point to the same memory
    shared_ptr<int>(q);
} // block ends, q is destroyed, and the memory to which q points is freed
int foo = *p;   // undefined; the memory to which p points was freed

unique_ptr

同一时刻只能有一个unique_ptr指向给定的对象。当unique_ptr被销毁时,它指向的对象也会被销毁。

unique_ptr<int> p1(new int(42));
// C++14
unique_ptr<int> p2 = make_unique<int>(42);

// transfers ownership from p1 (which points to the string Stegosaurus) to p2
unique_ptr<string> p2(p1.release());    // release makes p1 null
unique_ptr<string> p3(new string("Trex"));
// transfers ownership from p3 to p2
p2.reset(p3.release()); // reset deletes the memory to which p2 had pointed

p2.release();   // WRONG: p2 won't free the memory and we've lost the pointer
auto p = p2.release();   // ok, but we must remember to delete(p)

// p points to an object of type objT and uses an object of type delT to free that object
// it will call an object named fcn of type delT
unique_ptr<objT, delT> p (new objT, fcn);

void f(destination &d /* other needed parameters */)
{
    connection c = connect(&d);  // open the connection
    // when p is destroyed, the connection will be closed
    unique_ptr<connection, decltype(end_connection)*> p(&c, end_connection);
    // use the connection
    // when f exits, even if by an exception, the connection will be properly closed
}

二、动态数组

new和数组

使用new分配对象数组时需要在类型名之后跟一对方括号,在其中指明要分配的对象数量(必须是整型,但不必是常量)。new返回指向第一个对象的指针(元素类型)。

// call get_size to determine how many ints to allocate
int *pia = new int[get_size()];   // pia points to the first of these ints
int *pia = new int[10];     // block of ten uninitialized ints
int *pia2 = new int[10]();    // block of ten ints value initialized to 0
string *psa = new string[10];    // block of ten empty strings
string *psa2 = new string[10]();    // block of ten empty strings
// block of ten ints each initialized from the corresponding initializer
int *pia3 = new int[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
// block of ten strings; the first four are initialized from the given initializers
// remaining elements are value initialized
string *psa3 = new string[10] { "a", "an", "the", string(3,'x') };

delete p;       // p must point to a dynamically allocated object or be null
delete [] pa;   // pa must point to a dynamically allocated array or be null

智能指针和动态数组

使用unique_ptr可以直接管理动态数组,定义时需要在对象类型后添加一对空方括号[]

// up points to an array of ten uninitialized ints
unique_ptr<int[]> up(new int[10]);
up.release();   // automatically uses delete[] to destroy its pointer

unique_ptr不同,shared_ptr不直接支持动态数组管理。如果想用shared_ptr管理动态数组,必须提供自定义的删除器。

// to use a shared_ptr we must supply a deleter
shared_ptr<int> sp(new int[10], [](int *p) { delete[] p; });
sp.reset();    // uses the lambda we supplied that uses delete[] to free the array

shared_ptr未定义下标运算符,智能指针类型也不支持指针算术运算。因此如果想访问shared_ptr管理的数组元素,必须先用get获取内置指针,再用内置指针进行访问。

// shared_ptrs don't have subscript operator and don't support pointer arithmetic
for (size_t i = 0; i != 10; ++i)
    *(sp.get() + i) = i;    // use get to get a built-in pointer

allocator

allocator类是一个模板,定义时必须指定其可以分配的对象类型。

allocator<string> alloc;    // object that can allocate strings
auto const p = alloc.allocate(n);   // allocate n unconstructed strings

allocator分配的内存是未构造的,程序需要在此内存中构造对象。新标准库的construct函数接受一个指针和零或多个额外参数,在给定位置构造一个元素。额外参数用来初始化构造的对象,必须与对象类型相匹配。

auto q = p;     // q will point to one past the last constructed element
alloc.construct(q++);    // *q is the empty string
alloc.construct(q++, 10, 'c');  // *q is cccccccccc
alloc.construct(q++, "hi");     // *q is hi!

直接使用allocator返回的未构造内存是错误行为,其结果是未定义的。

对象使用完后,必须对每个构造的元素调用destroy进行销毁。destroy函数接受一个指针,对指向的对象执行析构函数。

while (q != p)
    alloc.destroy(--q);  // free the strings we actually allocated

deallocate函数用于释放allocator分配的内存空间。传递给deallocate的指针不能为空,它必须指向由allocator分配的内存。而且传递给deallocate的大小参数必须与调用allocator分配内存时提供的大小参数相一致。


题外话

几个月后再回过头来复习这一章内容,代码基本上忘得差不多了,查缺补漏直接写成了学习笔记。。。真的是要不断巩固啊~

补充阅读

C/C++面试:什么是智能指针?智能指针有什么作用?分为哪几种?各自有什么样的特点?
C++——malloc/free和new/delete的区别

  • 23
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值