C++ Primer 12章 动态内存 学习笔记

本文探讨了C++中动态内存管理的多种方式,包括智能指针(shared_ptr、unique_ptr、weak_ptr)的使用及其与异常处理的结合,介绍了如何通过智能指针确保资源在异常情况下的正确释放。同时,深入讲解了StrBlob类的设计,展示了如何利用shared_ptr进行内存管理,以及allocator类在内存分配中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

12.1、动态内存与智能指针

静态内存:用于保存局部static对象、类static数据成员以及定义在任何函数之外的变量。(static对象在使用之前分配,程序结束销毁。)

栈内存:用来保存定义在函数内的非static对象。(栈对象,仅在程序块运行时存在)

分配在静态或栈内存中的对象由编译器自动创建和销毁。

智能指针:shared_ptr(允许多个指针指向同一个对象),unique_ptr(独占所指向的对象)

weak_ptr伴随类:弱引用,指向shared_ptr所管理的对象。

12.1.1、shared_ptr类

每个shared_pt都有一个关联的计数器,称为引用计数。当我们拷贝(初始化、参数传递、函数返回值)一个shared_ptr时,计数器递增。当被赋予新值或被销毁,计数器递减。

析构函数:一般用于释放对象所分配的资源。

程序使用动态内存的三个原因:程序不知道自己需要使用多少个对象、程序不知道所需对象的准确类型、程序需要在多个对象间共享数据。

StrBlob类

#ifndef SRC_STRBLOB_H_
#define SRC_STRBLOB_H_
#include<vector>
#include<string>
#include<initializer_list>
#include<memory>
#include<stdexcept>

using namespace std;

class StrBlob{
public:
	typedef vector<string>::size_type size_type;
	StrBlob();
	StrBlob(initializer_list<string> il);
	size_type size() const { return data->size(); }
	bool empty() const { return data->empty(); }
	//添加和删除元素
	void push_back(const string &t) { data->push_back(t); }
	void pop_back();
	//元素访问
	string& front();
	const string& front() const;
	string& back();
	const string& back() const;
private:
	shared_ptr<std::vector<std::string>> data;
	//如果data[i]不合法,抛出一个异常
	void check(size_type i, const std::string &msg) const;
};
//两个构造函数都使用初始化列表来初始化其data成员
StrBlob::StrBlob() : data(make_shared<vector<string>>()) { }
StrBlob::StrBlob(initializer_list<string> il) : data(make_shared<vector<string>>(il)) { }

void StrBlob::check(size_type i, const string &msg) const
{
	if(i >= data->size())
		throw out_of_range(msg);
}

string& StrBlob::front()
{
	//如果vector为空,check会抛出一个异常
	check(0, "front on empty StrBlob");
	return data->front();
}
//const版本的front
const string& StrBlob::front() const
{
	check(0, "front on empty StrBlob");
	return data->front();
}

string& StrBlob::back()
{
	check(0,"back on empty StrBlob");
	return data->back();
}
//const版本back
const string& StrBlob::back() const
{
	check(0,"back on empty StrBlob");
	return data->back();
}

void StrBlob::pop_back()
{
	check(0,"pop_back on empty StrBlob");
	data->pop_back();
}

#endif /* SRC_STRBLOB_H_ */

测试程序

#include<iostream>
#include "StrBlob.h"

using namespace std;

int main(int argc, char **argv)
{
	StrBlob b1;
	{
		StrBlob b2 = { "a", "an", "the"};
		b1 = b2;
		b2.push_back("about");
		cout << b2.size() << endl;		//4
	}
	cout << b1.size() << endl;			//4
	cout << b1.front() << " " << b1.back() << endl;		//a about

	const StrBlob b3 = b1;
	cout << b3.front() << " " << b3.back() << endl;		//a about

	return 0;
}

12.1.2、直接管理内存

使用new和delete直接管理内存

#include<iostream>
#include<vector>

using namespace std;
//shared_ptr<vector<int>> new_vector(void)      //智能指针管理内存,在主函数末尾不需要主动释放内存
vector<int> *new_vector()           //返回动态分配int的vector
{
    //return make_shared<vector<int>> ();
    return new (nothrow) vector<int>;   //如果分配失败,new返回一个空指针
}

void read_ints(vector<int> *pv)         //读取标准输入
{
    int v;

    while(cin >> v)
        pv->push_back(v);
}

void print_ints(vector<int> *pv)        //打印
{
    for(const auto &v : *pv)
        cout << v << " ";
    cout << endl;
}

int main(int argc, char *argv[])
{
    vector<int> *pv = new_vector();
    if(!pv){
        cout << "内存不足!" << endl;
        return -1;
    }

    read_ints(pv);
    print_ints(pv);

    delete pv;                          //释放内存
    pv = nullptr;                       //将指针重置

    return 0;
}

12.1.3、shared_ptr和new结合使用

接受指针参数的智能指针构造函数是explicit的,因此,不能将一个内置指针隐式转换为一个智能指针,必须直接初始化形式。

默认情况下,一个用来初始化智能指针的普通指针必须指向动态内存,因为智能指针默认使用delete释放它所关联的对象。

不要混合使用普通指针和智能指针,使用一个内置指针来访问一个智能指针所负责的对象是很危险的。

智能指针类型定义了一个名为get的函数,它返回一个内置指针,指向智能指针管理的对象。

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

12.1.4、智能指针和异常

使用智能指针可以确保在异常发生会,资源被正确的释放。

如果使用内置指针管理内存,且在new之后在对应的delete之前发生了异常,则内存不会被释放。

#include<iostream>
#include<memory>

using namespace std;

struct destination{};
struct connection{};

connection connect(destination *pd)
{
    cout << "打开链接" << endl;
    return connection();
}

void disconnect(connection c)
{
    cout << "关闭链接" << endl;
}

//未使用shared_ptr的版本
void f(destination &d)
{
    cout << "直接管理connect" << endl;
    connection c = connect(&d);
    //忘记调用disconnect关闭链接

    cout << endl;
}

void end_connection(connection *p) {disconnect(*p);}

//使用shared_ptr的版本
void f1(destination &d)
{
    cout << "用shared_ptr管理connect" << endl;
    connection c = connect(&d);

    shared_ptr<connection> p(&c, end_connection);
    //shared_ptr<connection> p(&c, [](connection *p) { disconnection(*p); });
    //忘记调用disconnect关闭链接

    cout << endl;
}

int main(int argc, char *argv[])
{
    destination d;
    f(d);
    f1(d);
    return 0;
}

12.1.5、unique_ptr

一个unique_ptr“拥有”它所指向的对象。必须采用直接初始化的形式。不支持普通的拷贝和赋值操作。

可以拷贝或赋值一个将要销毁的unique_ptr。

12.1.6、weak_ptr

12.2、动态数组

12.2.1、new和数组

//调用get_size确定分配多少个int
int *pia = new int[get_size()];     //pia指向第一个int 

分配一个数组会得到一个元素类型的指针

delete [] pia;      //释放动态数组
//up指向一个包含10个未初始化的int数组
unique_ptr<int[]> up(new int[10]);
up.release();           //自动用delete[]销毁其指针

shared_ptr不直接支持管理动态数组,必须提供自己定义的删除器。不支持下标运算,不支持指针的算术运算,使用get获取一个内置指针。

12.2.2、allocator类

将内存分配和对象构造分离开来。它分配的内存是原始的、未构造的。

拷贝和填充未初始化内存的算法

 

#include<iostream>
#include<string>
#include<memory>

using namespace std;

int main(int argc, char *argv[])
{
    allocator<string> alloc;
    //分配100个未初始化的string
    auto const p = alloc.allocate(4);
    string s;
    string *q = p;      //q指向第一个string
    while(cin >> s && q != p + 4)
        alloc.construct(q++, s);        //用s初始化string
    const size_t size = q - p;          //记住读取了多少个string

    //使用数组
    for(size_t i = 0; i < size; ++i)
        cout << p[i] << " " << endl;

    while(q != p)                       //使用完毕后释放已构造的string
        alloc.destroy(--q);
    alloc.deallocate(p, 100);           //释放内存

    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值