c++primer之动态内存

一、静态内存用来保存局部static对象、类static数据成员、定义在任何函数外的变量; 栈内存用来保存定义在函数内的非static对象

二、除了静态内存和栈内存,每个程序还拥有一个内存池,这部分内存被称作自由空间或堆;程序用堆来存储动态分配的对象,即那些在程序运行时分配的对象。 动态对象的生存周期由程序控制,当动态对象不再使用,必须显式的销毁它们

三、在c++中,动态内存的管理是通过一对运算符来完成的:

new:在动态内存中为对象分配空间并返回一个指向该对象的指针

delete:接受一个动态对象的指针,销毁该对象,并释放与之关联的内存

四、新标准库中提供的2种智能指针的区别在于管理底层指针的方式:

shared_ptr:允许多个指针同时指向同一个对象

unique_ptr: 独占所指的对象

五、新标准库定义了一个名为weak_ptr的伴随类,它是一种弱引用,指向shared_ptr所管理的对象

六、默认初始化的智能指针种保存着一个空指针

七、解引用一个智能指针返回它指向的对象,如果在一个条件判断中使用智能指针,效果就是检测它是否为空

八、最安全的分配和使用动态内存的方法是调用一个名为make_shared的标准库函数,此函数在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr

九、每个shared_ptr都有一个关联的计数器,通常称其为引用计数,无论何时我们拷贝一个shared_ptr,计数器都会递增。当用一个shared_ptr初始化另一个shared_ptr,或将它作为参数传递给一个函数,以及作为函数的返回值时,它所关联的计数器就会递增,当我们给shared_ptr赋予一个新值或是shared_ptr被销毁时,计数器就会递减,一旦一个shared_ptr的计数器变为0,它就会自动释放所管理的对象

十、当指向一个对象的最后一个shared_ptr被销毁时,shared_ptr类会自动销毁此对象,它是通过另一个特殊的成员函数析构函数完成销毁工作的,析构函数会递减它所指向的对象的引用计数,如果引用计数变为0,shared_ptr的析构函数就会销毁此对象并释放所占用的内存

十一、shared_ptr在无用之后仍然保留的一种可能情况是,你将shared_ptr存放在一个容器中,随后重排了容器,从而不再需要某些元素,在这种情况下,应该确保用erase删除那些不再需要的shared_ptr元素

十二、程序使用动态内存出于以下三种原因之一:

1、程序不知道自己需要使用多少对象

2、程序不知道所需对象的准确类型

3、程序需要在多个对象间共享数据

十三、一般而言, 如果2个对象共享底层的数据,当某个对象被销毁时,不能单方面的销毁底层数据

十四、拷贝一个shared_ptr会递增其应用计数;将一个shared_ptr赋予另一个shared_ptr会递增赋值号右侧shared_ptr的引用计数,而递减左侧shared_ptr的引用计数

十五、默认情况下,动态分配的对象是默认初始化的,意味着内置类型或组合类型的对象的值将是未定义的,而类类型对象将使用默认构造函数进行初始化

十六、默认情况下,如果new不能分配所要求的内存空间,它会抛出一个类型为bad_alloc的异常;可以改变使用new的方式来阻止它抛出异常,如int *p = new (nothrow) int; 这种形式的new为定位new,定位new表达式允许我们向new传递额外的参数,这种形式的new不能分配所需内存,会返回一个空指针

十七、传递给delete的指针必须指向动态分配的内存或者是一个空指针,释放非new分配的内存或者将相同的指针值释放多次,其行为是未定义的

十八、对于一个由内置指针管理的动态对象,直到被显式释放之前它都是存在的

十九、在delete之后,指针就变成了空悬指针,它指向一块曾经保存数据对象但现在已经无效额内存的指针

二十、动态内存的一个基本问题是可能有多个指针指向相同的内存,在delete内存之后重置指针的方法只对这个指针有效,对其他任何仍指向内存的指针是没有作用的

二一、接受指针参数的智能指针构造函数时explicit的,不能将一个内置指针隐式转换为一个智能指针,必须使用直接初始化形式来初始化一个智能指针,

二二、一个返回shared_ptr的函数不能在其返回语句中隐式转为为一个普通指针

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

二四、智能指针类型定义了一个名为get的函数,它返回一个内置指针,指向智能指针管理的对象,使用get返回的指针的代码不能delete此指针

二五、删除器函数必须能够完成对shared_ptr中保存的指针进行释放的操作

二六、某个时刻只能有一个unique_ptr指向一个给定对象,当unique_ptr被销毁时,它所指向的对象也被销毁

二七、由于一个unique_ptr拥有它指向的对象,因此unique_ptr不支持普通的拷贝或赋值操作,但可以通过调用release或reset将指针的所有权从一个unique_ptr转移给另一个unique_ptr,release成员返回unique_ptr当前保存的指针并将其置为空

二八、reset令unique_ptr重新指向给定的指针,如果unique_ptr不为空,它原来指向的对象被释放

二九、weak_ptr是一种不控制所指向对象生存期的智能指针,它指向由一个shared_ptr管理的对象,将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数,一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放,即使有weak_ptr指向对象,对象也会被释放

三十、由于对象可能不存在,不能使用weak_ptr直接访问对象,必须调用lock,此函数检查weak_ptr指向的对象是否仍存在,如果存在,lock返回一个指向共享对象的shared_ptr

三一、分配动态数组的类必须定义自己版本的拷贝、赋值和析构操作

三二、不能对动态数组调用begin或end,也不能使用for语句来处理动态数组中的元素

三三、动态数组并不是数组类型

三四、如果初始化数目小于元素数目,剩余元素将进行值初始化,如果初始化器数目大于元素数目,则new表达式失败,不会分配任何内存

三五、动态分配一个空数组是合法的,如int* p = new int[n],当n等于0时,调用new[n]是可以的,但此时指针不能解引用,它不指向任何元素

三六、释放动态数组delete[] p,数组中的元素按逆序销毁

三七、使用unique_ptr管理动态数组时,必须在对象类型后面跟一对空方括号[]

三八、当一个unique_ptr指向一个数组时,不能使用点和箭头运算符,可以使用下标运算符来访问数组中的元素

三九、shared_ptr不直接支持管理动态数组,如果要使用shared_ptr管理动态数组,必须提供自定义的删除器,shared_ptr未定义下标运算符, 必须使用get获取一个内置指针,然后用它来访问数组元素

class Foo
{

};

template<typename T>
shared_ptr<Foo> factory(T arg)
{
	return make_shared<Foo>(arg);
}

template<typename T>
void use_factory(T arg)
{
	shared_ptr<Foo> p = factory(arg); // p 离开了作用域,它指向的内存会被自动释放
}

template<typename T>
shared_ptr<Foo> use_factory_temp(T arg)
{
	shared_ptr<Foo> p = factory(arg); // 当返回p时,引用计数进行了递增操作
	return p;// p 离开了作用域,它指向的内存不会被自动释放
}

template<typename T>
void use_factory_normal(T arg)
{
	Foo* p = factory(arg);
	delete p; //由内置指针管理的动态内存需要显式释放
}

class StrBlob
{
	friend class StrBlobPtr;

	StrBlobPtr begin() { return StrBlobPtr(*this); }
	StrBlobPtr end() { auto ret = StrBlobPtr(*this, data->size()); return ret; }
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& s) { data->push_back(s); }
	void pop_back();
	string& front();
	string& back();


private:
	shared_ptr<vector<string>> data;
	void check(size_type i, const string& msg) const;
};

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
{
	cout << "i: " << i << endl;
	if (i >= data->size()) {
		throw out_of_range(msg);
	}
}
string& StrBlob::front()
{
	check(0, "front on empty StrBlob");
	return data->front();
}

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

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

class StrBlobPtr
{
public:
	StrBlobPtr() : curr(0) {}
	StrBlobPtr(StrBlob& a, size_t sz = 0) : wptr(a.data), curr(sz) {}

	string& deref() const;
	StrBlobPtr& incr();

private:
	shared_ptr<vector<string>> check(size_t, const string&) const;
 	weak_ptr<vector<string>> wptr;
	size_t curr;
};

shared_ptr<vector<string>> StrBlobPtr::check(size_t i, const string& msg) const
{
	// 不能使用weak_ptr直接访问对象,而必须调用lock
	// lock建测weak_ptr指向的对象释放仍存在
	auto ret = wptr.lock(); 
	if (!ret) {
		throw runtime_error("unbound StrBlobPtr");
	}
	if (i >= ret->size()) {
		throw out_of_range(msg);
	}
	return ret;
}

string& StrBlobPtr::deref() const
{
	auto p = check(curr, "dereference past end");
	return (*p)[curr];
}

StrBlobPtr& StrBlobPtr::incr()
{
	check(curr, "increment past end of StrBlobStr");
	++curr;
	return *this;
}



void testStrBlob()
{
	StrBlob b1;

	{
		StrBlob b2 = { "a", "an", "the" };
		b1 = b2;
		b2.pop_back();
		b2.push_back("about");

		cout << "块内" << endl;
		cout << "b1:" << b1.size() << endl; // 4
		cout << "b2:" << b2.size() << endl; // 4
	}
	cout << "块外" << endl;
	cout << "b1:" << b1.size() << endl; // 4
}

shared_ptr<int> clone(int p) 
{
	// 一个返回shared_ptr的函数不能在其返回语句中隐式转换一个普通指针
	//return new int(p); // 错误,隐式转换为shared_ptr<int>
	return shared_ptr<int>(new int(p));
}
int main()
{
	shared_ptr<string> p1;
	shared_ptr<list<int>> p2;

	if (p1 && p1->empty()) {
		*p1 = "hi";
	}

	shared_ptr<int> p3 = make_shared<int>(42);
	shared_ptr<string> p4 = make_shared <string> (10, '9');
	shared_ptr<int> p5 = make_shared<int>();
	auto p6(p3);


	auto p7 = make_shared<int>(41);
	// 给p7赋值,令它指向另一个地址
	// 递增p6指向的对象的引用计数
	// 递减p7原来指向的对象的引用计数
	// p7原来指向的对象已经没有引用者,会自动释放
	p7 = p6;

	testStrBlob();

	int i, * pi1 = &i, * pi2 = nullptr;
	double* pd = new double(33), * pd2 = pd;

	// delete i; // i不是一个指针
	delete pi1; // 未定义,pi1指向一个局部变量
	delete pd;
	delete pd2; // 未定义,pd2指向的内存已经被释放了
	delete pi2; 
	
	int* dp2(new int(43));
	auto dq2 = dp2; 
	delete dp2; // dp2和dq2均变为无效
	dp2 = nullptr; // 指出dp2 不再绑定到任何对象,但对dq2没有作用

	// 接受指针参数的智能指针函数是explicit的,不能将一个内置指针隐式转换为一个智能指针
	//shared_ptr<int> p11 = new int(1024); // 错误,必须使用直接初始化的形式
	shared_ptr<int> p11(new int(1024));

	shared_ptr<int> p12(new int(43));
	int* q12 = p12.get();
	{
		shared_ptr<int>(q12);
	} // 程序块结束,q被销毁,它指向的内存被释放
	// int foo = *p12; // 未定义,p12指向的内存已经被释放了


	unique_ptr<string> p13(new string("hello"));
	//unique_ptr<string> p14(p13); // unique_str不支持拷贝
	//unique_ptr<string> p15;
	//p15 = p13; // unique_ptr不支持赋值
	// 可以通过release或reset将指针的所有权从一个unique_str转移给另一个unique
	unique_ptr<string> p14(p13.release());


	int* pia = new int[10];
	typedef int arrT[42];
	int* pia2 = new arrT;

	int* pia3 = new int[10]; // 10个未初始化的int
	int* pia4 = new int[10](); // 10个值初始化为0的int
	string* pia5 = new string[10]; // 10个空string
	string* pia6 = new string[10](); // 10个空string

	//char arr[0]; // 错误,不能定义长度为0的数组
	char* cp = new char[0]; // 动态分配一个空数组是可以的,但不能解引用,它不指向任何元素
	delete[] cp; // 释放动态数组。 数组中的元素按逆序销毁

	unique_ptr<int[]> up(new int[10]); // 在对象类型后面跟一对空方括号
	for (size_t i = 0; i != 10; ++i) {
		cout << up[i] << endl;// 当一个unique_ptr指向一个数组时,可以使用下标运算符访问数组中的元素
	}

	shared_ptr<int> spp(new int[10], [](int* p) { delete[] p; });
	for (size_t i = 0; i != 10; ++i) {
		// shared_ptr未定义下标运算符,需要用get获取一个内置指针,然后用它访问数组元素
		cout << *(spp.get() + i) << endl; 
	}
	

	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值