第12章 动态内存

C++ primer 5th 第12章 动态内存

=====================================================================

第12章 动态内存 400页  shared_ptr

=====================================================================

//QQ108201645编写
#include <iostream>
#include <string>
#include <list>
#include <memory>//智能指针所在头文件
using namespace std;

int main()
{
	//类似vector,智能指针也是模板
	shared_ptr<string> p1;//shared_ptr,可以指向string,如果加上=make_shared<string>(),则p1不为空
	shared_ptr<list<int>> p2;//shared_ptr,可以指向int的list
	//如果p1不为空,检查它是否指向一个空string
	if (p1&&p1->empty())
		*p1 = "hi";//如果p1指向空string,解引用p1,赋一个新值
	cout << *p1 << endl;
	system("pause");
	return 0;
}

 

=====================================================================

表12.1:shared_ptr和unique_ptr都支持的操作

shared_ptr<T> sp

空智能指针,可以指向类型为T的对象

unique_ptr<T> up

p

将p用作一个条件判断,若p指向一个对象,则为true

*p

解引用p,获得它指向的对象

p->mem

等价于(*p).mem

p.get()

返回p中保存的指针。要小心使用,若智能指针释放了其对象,返回的指针所指向的对象也就消失了

swap(p, q)

交换p和q中的指针

p.swap(q)

=====================================================================

表12.2:shared_ptr独有的操作

make_shared<T>(args)

返回一个shared_ptr,指向一个动态分配的类型为T的对象。使用args初始化此对象

make_shared<T>p(q)

p是shared_ptr的拷贝;此操作会递增q中的计数器。q中的指针必须能转换为T* (参见4.11.2节,第143页)

p = q

p和q都是shared_ptr,所保存的指针必须能相互转换。此操作会递减p的引用计数,递增q的引用计数;若p的引用计数变为0,则将其管理的原内存释放

p.unique()

若p.use_count()为1,返回true;否则返回false

p.use_count

返回与p共享对象的智能指针数量;可能很慢,主要用于调试

 

=====================================================================

第12章 动态内存 401页  make_shared函数

=====================================================================

//QQ108201645编写
#include <iostream>
#include <string>
#include <list>
#include <vector>
#include <memory>

using namespace std;

int main()
{
	//指向一个值为42的int的shared_ptr
	shared_ptr<int> p3 = make_shared<int>(42);
	cout << *p3 << endl;

	//p4指向一个值为"9999999999"的string
	shared_ptr<string> p4 = make_shared<string>(10,'9');
	cout << *p4 << endl;

	//p5指向一个值初始化的(参见3.3.1节,第88页)int,即,值为0
	shared_ptr<int> p5 = make_shared<int>();
	cout << *p5 << endl;

	//p6指向一个动态分配的空vector<string>
	shared_ptr<vector<string>> p6 = make_shared<vector<string>>();
	cout << p6 << endl;

	auto p = make_shared<int>(42);//p指向的对象只有p一个引用者
	auto q(p);//p和q指向相同的对象,此对象有两个引用者
	

	auto r = make_shared<int>(42);//r指向的int 只有一个引用者
	r = q;//给r赋值,令它指向另一个地址
	//递增q指向的对象的引用计数
	//递减r原来指向的对象的引用计数
	//r原来指向的对象已没有引用者,会自动释放
	system("pause");
	return 0;
}

 

=====================================================================

第12章 动态内存 401页  make_shared函数

=====================================================================

//QQ108201645编写
#include <vector>
#include <string>
#include <memory>
#include <iostream>
using namespace std;

#ifndef FOO_H
#define FOO_H


typedef int T;
struct Foo 
{  //结构体 默认公有成员
	Foo(T t) : val(t) { }
	T val;
};

std::ostream& print(std::ostream &os, const Foo &f)
{
	os << f.val;
	return os;
}

#endif

//factory返回一个shared_ptr,指向一个动态分配的对象
shared_ptr<Foo> factory(T arg)
{
	// 恰当的处理arg
	// shared_ptr 负责释放内存
	return make_shared<Foo>(arg);

}

void use_factory(T arg)
{
	shared_ptr<Foo> p = factory(arg);
	//return p;
}

int main()
{
	T arg;
	while (cin >> arg)
		use_factory(arg);
}

 

=====================================================================

第12章 动态内存 404页  定义StrBlob类(包含练习题)

=====================================================================

//QQ108201645编写
#include <initializer_list>
#include <iostream>
#include <memory>
#include <vector>
#include <string>
using namespace std;
//定义StrBlob类
class StrBlob
{
public:
	typedef vector<string>::size_type size_type;
	StrBlob();
	StrBlob(initializer_list<string> i1);
	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();
	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> i1)
	:data(make_shared<vector<string>>(i1)){}

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();
}
string& StrBlob::back()
{
	check(0, "back on empty StrBolb");
	return data->back();
}

void StrBlob::pop_back()
{
	check(0, "pop_back on empty StrBolb");
	return data->pop_back();
}
int main()
{
	/*练习12.1:在此代码的结尾,b1 和 b2 各包含多少个元素?*/
	StrBlob b1;
	{
		StrBlob b2 = { "a", "an", "the" };
		b1 = b2;
		b2.push_back("about");
	}
	/*由于StrBlob的data成员是一个指向string的vector的shared_ptr,
	因此StrBlob的赋值不会拷贝vector的内容,而是多个StrBlob对象共享同一个
	(创建于动态内存空间上)vector的对象
	创建b2时提供了3个string的列表,因此会创建一个包含3个string的vector对象,
	并创建一个shared_ptr指向此对象(引用计数为1)。
	当把b2赋予给b1时,创建一个sshared_ptr也指向刚才创建的vector对象,引用计数
	变为2。
	因此,向b2添加一个string时,会向两个StrBlob共享vector中添加此string。
	最终,在代码结尾,b1和b2均包含4个string*/
}

=====================================================================

//QQ108201645编写
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <initializer_list>
using namespace std;
/*练习12.2:编写你自己的StrBlob 类,包含const 版本的 front 和 back。*/
class StrBlob
{
public:
	using size_type = vector<string>::size_type;
	StrBlob():data(make_shared<vector<string>>()){}
	StrBlob(initializer_list<string>i1);
	size_type size()const
	{
		return data->size();
	}
	bool empty()const
	{
		return data->empty();
	}
	string& front()
	{
		check(0, "front on empty StrBlob");
		return data->front();
	}
	string& back()
	{
		check(0, "back on empty StrBlob");
		return data->back();
	}
	void push_back(const string& t)
	{
		data->push_back(t);
	}
	void pop_back()
	{
		check(0, "pop_back on empty StrBlob");
		return data->pop_back();
	}
	const string& front()const
	{
		check(0, "front on empty StrBlob");
		return data->front();
	}
	const string& back()const
	{
		check(0, "front on empty StrBlob");
		return data->back();
	}
private:
	void check(size_type i, const string& msg)const
	{
		if (i >= data->size())
			throw out_of_range(msg);
	}
	shared_ptr<vector<string>> data;
};
StrBlob::StrBlob(initializer_list<string> i1)
	:data(make_shared<vector<string>>(i1)) {}

int main()
{
	
	const StrBlob csb{ "hello","world","pezy" };

	StrBlob sb{ "hello","world","pezy" };
	cout << csb.front() << " " << csb.back() << endl;
	StrBlob s1 = sb;
	sb.back() = "xxxx";//返回最后一个元素,并把xxxx赋予给它
	cout << sb.front() << " " << sb.back() << endl;
	sb.pop_back();
	cout << sb.front() << " " << sb.back() << endl;
	cout << s1.front() << " " << s1.back() << endl;

	/*
	练习12.3:StrBlob 需要const 版本的push_back 和 pop_back吗?如果需要,
	添加进去。否则,解释为什么不需要。
	不需要,因为push_back和poo_back是写操作,不能使用const。
	*/
	/*
	练习12.4:在我们的 check 函数中,没有检查 i 是否大于0。为什么可以忽略这个检查?
	我们将check定义为私有成员函数,即只会被StrBlob的成员函数调用,而不会被用户程序调用,
	所以很容易的保证传递给它的i值符合要求,不必进行检查
	*/
	system("pause");
	return 0;
}

 

=====================================================================

/*练习12.5:我们未编写接受一个 initializer_list explicit(参见7.5.4节,第
	264页)参数的构造函数。讨论这个设计策略的优点和缺点。*/
	/*
	未编写一个初始化列表参数的显式构造,意味着可以进行StrBlob的隐式类型转换,
	即在需要StrBlob的地方可以使用列表进行替代,而且可以进行拷贝形式的初始化
	(如赋值)。简单方便
	但隐式转换不总是好的,列表中可能并非都是合法的值,对于接受StrBlob的函数。
	传递给它一个列表,会创建一个临时StrBlob对象,用列表对其初始化,然后将其传递
	给函数,当函数完成后,此对象将被丢弃,再也无法访问了,对于这个情况,可以显式定义
	构造函数,禁止隐式类型转换。
	*/

=====================================================================

第12章 动态内存 407页  直接管理内存

=====================================================================

//QQ108201645编写
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
	int *pi = new int;//pi指向一个动态分配的、未初始化的无名对象
	string *ps = new string;//初始化为空的string
	int *pi1 = new int;//pi1指向一个未初始化的int
	int *pi2 = new int(1024);//pi指向对象的值为1024
	string *ps1 = new string(10, '9');//*ps1的内容是"9999999999"字符串

	//vector有10个元素,值依次从0到9
	vector<int>* pv = new vector<int>{ 0,1,2,3,4,5,6,7,8,9 };

	string *ps2 = new string;//默认初始化为空的string
	string* ps3 = new string();//值初始化为空的string
	int *pi3 = new int;//默认初始化;*pi3的值未定义
	int *pi4 = new int();//默认初始化为0;*pi3=0
	return 0;
}

=====================================================================

第12章 动态内存 407页  使用new动态分配和初始化对象

=====================================================================

//QQ108201645编写
#include <iostream>
#include <string>
#include <vector>
using namespace std;
struct Object
{
	int num;
	Object(int n):num(n){}
};
int main()
{
	Object obj(0);
	auto p1 = new auto(obj);//p指向一个与obj类型相同的对象,该对象用obj进行初始化
	int a, b, c;
	//auto p2 = new auto{a, b, c};//错误:括号中只能有单个初始化器
	/*
	p1的类型是一个指针,指向从obj自动推断出的类型,若obj=int那么p1就是int*;
	若obj是一个string,那么p1就是string* ;依此类推。新分配的对象的值用obj的值进行初始化。*/
	//分配并初始化一个const int
	const int* pci = new const int(1024);
	//分配并默认初始化一个const的空string
	const string* pcs = new const string;
	//一个动态分配的const对象必须进行初始化

	//如果分配失败,new返回一个空指针
	int *p = new int;//如果分配失败,new抛出std::bad_alloc
	int *p2 = new(nothrow) int;//如果分配失败,new返回一个空指针

	return 0;
}

=====================================================================

第12章 动态内存 409页  释放动态内存

=====================================================================

//QQ108201645编写
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
	int *p = new int;
	//delete表示接受要释放的对象
	delete p;//p必须指向一个动态分配的对象或是一个空指针

	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;//正确:释放一个空指针总是没有错误的

	const int *pci = new const int(1024);
	delete pci;//正确,释放一个const对象
	return 0;
}

=====================================================================

//QQ108201645编写
#include <iostream>
using namespace std;
using T = int;
struct Foo
{
	Foo(T t)
		:val(t){}
	T val;
};
//factory返回一个指针,指向一个动态分配的对象
Foo* factory(T arg)
{//视情况处理ary
	return new Foo(arg);//调用者负表释放此内存
}
#ifdef DECLARATION
void use_factory(T arg)
{
	Foo *p = factory(arg);
	//使用p但不delete它

}//p离开了它的作用域,但它所指向的内存没有释放!
#endif
#ifdef DECLARATION
void use_factory(T arg)
{
	Foo *p = factory(arg);
	//使用p
	delete p;//现在记得释放内存,我们不需要它了

}
#endif
//如果其他代码要使用use_factory所分配的对象,我们就应该修改些函数
Foo* use_factory(T arg)
{
	Foo* p = factory(arg);
	return p;
}
int main()
{
	Foo* p=use_factory(5);
	delete p;
	return 0;
}
#ifdef DECLARATION
动态内存的管理容易出错:使用new和delete管理动态内存存在三个常见问题:
1. 忘记delete内存, 忘记释放动态内存会导致人们常说的“内存泄露”问题,
这种内存永远不可能归还给系统,查找内存泄露错误是非常困难的,因为
通常应用程序运行很长时间,直到内存耗尽时,才会发现这种错误。
2. 使用已经释放掉的动态对象, 通过在释放内存后将指针置为空,有时可以检测出这
种错误。
3. 同一块内存释放多次,当两个指针可能指向同一块动态分配对象时,可能发生这
种错误,如果对其中一个指针进行了delete操作,对象的内存就被归还给了自由空间了。
相对于查找和修正这些错误来说,制造出这些错误要简单的多。

坚持使用智能指针,就可以避免所有这些问题。对于一块内存,只有在没有任何智能指针
指向它的情况下,智能指针都会自动释放它。
#endif

=====================================================================

//QQ108201645编写
#include <iostream>
using namespace std;
int main()
{
	int* p(new int(42));//p指向动态内存
	auto q = p;//p和q指向相同的内存
	delete p;//p和q均变为无效
	p = nullptr;//指出其不p不再绑定到任何对象
	return 0;
}

=====================================================================

第12章 动态内存 411页  练习题

=====================================================================

//QQ108201645编写
#include <iostream>
#include <vector>
using namespace std;

vector<int>* new_vector(void)
{
	return new (nothrow) vector<int>;
}
void read_ints(vector<int>* pv)
{
	int v[] = { 1,2,3,4,5 };
	pv->insert(pv->begin(),begin(v), end(v));
}
void print_ints(vector<int>* pv)
{
	for (auto &v : *pv)
		cout << v << " ";
	cout << endl;
}
int main()
{
	/*练习12.6:编写函数,返回一个动态分配的 int 的vector。将此vector传递给另
	一个函数,这个函数读取标准输入,将读入的值保存在 vector 元素中。再将vector
	传递给另一个函数,打印读入的值。记得在恰当的时刻delete vector。*/
	vector<int>* pv = new_vector();
	if (!pv)
	{
		cerr << "内存不足" << endl;
		return -1;
	}
	read_ints(pv);
	print_ints(pv);//输出
	delete pv;
	pv = nullptr;
	return 0;
}

=====================================================================

//QQ108201645编写
#include <iostream>
#include <vector>
using namespace std;

shared_ptr<vector<int>> new_vector(void)
{
	return make_shared<vector<int>>();
}
void read_shared(shared_ptr<vector<int>>& spv)
{
	int v[] = { 5,6,7,8,9 };
	spv->insert(spv->begin(), begin(v), end(v));
}
void print_ints(shared_ptr<vector<int>>& spv)
{
	for (auto &s : *spv)
		cout << s << " ";
	cout << endl;
}
int main()
{
	/*练习12.7:重做上一题12.6,这次使用 shared_ptr 而不是内置指针。*/
	shared_ptr<vector<int>> pv = new_vector();
	if (!pv)
	{
		cerr << "内存不足" << endl;
		return -1;
	}
	read_shared(pv);
	print_ints(pv);//输出

	return 0;
}

=====================================================================

 

/*
练习12.8:下面的函数是否有错误?如果有,解释错误原因。
bool b()
{
	int* p = new int;
	// ...
	return p;
}
分配成功返回一个合法指针,返回非0值,分配失败,p得到nullptr,两者都可以转换成bool值
但是new在调用分配失败时是抛出一个异常bad_alloc,而不是nullptr,因此程序不能达到预想
的目的。
可将new int改为new(nothrow) int来令new在分配失败时不抛出异常,
而是返回nullptr。但仍然不是好方法,应该捕获异常或是判断返回的指针来返回bool类型,而不是
依赖类型转换

*/

/*
练习12.9:解释下面代码执行的结果。
int *q = new int(42), *r = new int(100);
r = q;
auto q2 = make_shared<int>(42), r2 = make_shared<int>(100);
r2 = q2;
q赋值给r,r所指的内存没有释放导致内存泄露。q2赋值给r2,r2的引用计数减1后为0,自动销毁。
*/

=====================================================================

第12章 动态内存 412页  shared_ptr和new结合使用

=====================================================================

//QQ108201645编写
#include <iostream>
#include <vector>
using namespace std;

shared_ptr<int> clone(int p)
{
	//return new int(p);//错误:隐式转换为shared_ptr<int>
	return shared_ptr<int>(new int(p));//正确:显式地用int*创建shared_ptr<int>
}
int main()
{
	//shared_ptr<int> p1 = new int(1024);//错误:必须使用直接初始化形式
	shared_ptr<int> p2(new int(1024));//正确:使用了直接初始化形式
	shared_ptr<int> p3 = make_shared<int>(5);
	cout << *p3 << endl;
	return 0;
}

=====================================================================

表12.3:定义和改变shared_ptr的其他方法

shared_ptr<T> p(q)

p管理内置指针q所指向的对象;q必须指向new分配的内存,且能够转换为T*类型

shared_ptr<T> p(u)

p从unique_ptr u那里接管了对象的所有权;将u置为空

shared_ptr<T> p(q, d)

p接管了内置指针q所指向的对象的所有权。q必须能转换为T*类型(参见4.11.2节,第143页)。p将使用可调用对象d(参见10.3.2节,第346页)来代替delete

shared_ptr<T> p(p2, d)

如表12.2所示,p是shared_ptr p2的拷贝,唯一的区别是p将用可调用对象d来代替delete

p.reset()

若p是唯一指向其对象的shared_ptr,reset会释放对象。若传递了可选的参数内置指针q,会令p指向q,否则会将p置为空。若还传递了参数d,将会调用d而不是delete来释放q

p.reset(q)

p.reset(q, d)

 

=====================================================================

第12章 动态内存 413页  不要混合使用普通指针与智能指针

=====================================================================

//QQ108201645编写
#include <iostream>
using namespace std;

void process(shared_ptr<int> ptr)
{
	cout << ptr.use_count();//输出引用计数
	//使用ptr
}//ptr离开作用域,被销毁
int main()
{
	shared_ptr<int> p(new int(42));//引用计数为1
	process(p);//拷贝p会递增它的引用计数;在process中引用计数为2
	int i = *p;//正确:引用计数为1
	int *x(new int(1024));//危险:x是一个普通指针,不是一个智能指针
	//process(x);//错误:不能将int* 转换为一个shared_ptr<int>
	process(shared_ptr<int>(x));//合法的的,但内存会被释放
	int j = *x;//未定义的:x是一个空悬指针!
	return 0;
}

=====================================================================

第12章 动态内存 413页  也不要使用get初始化另一个智能指针或为智能指针初始化

=====================================================================

//QQ108201645编写
#include <iostream>
#include <string>
using namespace std;

int main()
{
	shared_ptr<int> p(new int(42));//引用计数为1
	int *q = p.get();//正确:但使用q时要注意,不要让它管理的指针被释放
	{
		//新程序块
		//未定义:两个独立的shared_ptr指向相同的内存
		shared_ptr<int>(q);
	}//程序块结束,q被销毁,它指向的内存被释放
	int foo = *p;//未定义:p指向的内存已经被释放
	//p = new int(1024);//错误:不能将一个指针赋予shared_ptr
	p.reset(new int(1024));//正确:p指向一个新对象
	string newVal = " world";
	shared_ptr<string> p1(new string("hello"));
	if (p1.unique())
		p1.reset(new string(*p1));
	cout << *p1 << endl;
	*p1 += newVal;
	cout << *p1 << endl;
	return 0;
}

=====================================================================

第12章 动态内存 414页  练习题

=====================================================================

//QQ108201645编写
#include <iostream>
using namespace std;

void process(shared_ptr<int> ptr)
{

}
int main()
{
	/*练习12.10:下面的代码调用了第413页中定义的process 函数,解释此调用是否正
	确。如果不正确,应如何修改?
	shared_ptr<int> p(new int(42));
	process(shared_ptr<int>(p));
	*/
	/*正确的。利用p创建一个临时的shared_ptr赋予process的参数
	ptr,p和ptr都指向相同的int对象,引用计数被正确的置为2.process执行
	完毕后,ptr被销毁,引用计数减1,这是正确的--只有p指向它
	*/
	/*
	练习12.11:如果我们像下面这样调用 process,会发生什么?
	process(shared_ptr<int>(p.get()));
	将会导致智能指针所指的内存被释放2次。
*/
	shared_ptr<int> p(new int(42));
	process(shared_ptr<int>(p));
	//process(shared_ptr<int>(p.get()));
	/*
	p.get()获得一个普通指针指向p所共享的int对象。利用此指针创建一个shared_ptr,而一浊利用
	p创建一个shared_ptr,将不会形成正确正确的动态对象共享,编译器会认为p和ptr是使用两个地址
	(虽然它们相等)创建两个不相干的shard_ptr,而非共享同一个动态对象。这样,两者的引用计数均为1
	。当pprocess执行完后,ptr引用计数减为0,所管理的内存地址被释放,而此内存就是p所管理的。p成为
	一个管理空悬指针的shared_ptr。*/
	/*
	练习12.12:p 和 q 的定义如下,对于接下来的对 process 的每个调用,如果合法,
	解释它做了什么,如果不合法,解释错误原因:
auto p = new int();
auto sp = make_shared<int>();
(a) process(sp);
(b) process(new int());
(c) process(p);
(d) process(shared_ptr<int>(p));
(a)正确,sp是共享指针,对process的调用会拷贝sp,传递给process的参数ptr,两者都指向相同的int对象,
引用计数变为2。
(b)合法,new创建一个int对象。指向它的指针被用来创建一个shared_ptr,传递给processt的参数ptr,引用计数
为1,当process执行完毕,ptr被销毁,引用计数变为0,临时int对象因而被销毁。不存在内在泄漏和空悬指针的总是
(c)不合法,不能将int*转换为shared_ptr<int>
(d)合法,但是 是错误的程序,p是一个指向int对象的普通指针,被用来创建一个临时shared_ptr。传递给process
的参数ptr,引用计数为1.当process执行完毕,ptr被销毁,引用计数变为0,int被销毁,p变为空悬指针

*/
	/*
	练习12.13:如果执行下面的代码,会发生什么?
auto sp = make_shared<int>();
auto p = sp.get();
delete p;
第二行用get获取sp指向的int对象的地址,第三行用delete释放这个地址。意味着sp的引用计数仍为1,但其
指向的int对象已经被释放了。sp成为类似空悬指针的shared_ptr。
*/
}

=====================================================================

第12章 动态内存 415页  智能指针与异常

=====================================================================

//QQ108201645编写
#include <iostream>
using namespace std;

void f()
{
	shared_ptr<int> sp(new int(42));//分配一个新对象
	cout << *sp.get() << endl;
	//这段代码抛出一个异常,且在f中未被捕获
}//在函数结束时shared_ptr自动释放内存

void f1()
{
	int *ip = new int(42);//动态分配一个新对旬 
	//这段代码抛出一个异常,且在f1中未被捕获
	delete ip;//在退出前释放内存
}
int main()
{
	shared_ptr<int> p;
	shared_ptr<int> p1;
	{
		shared_ptr<int> p = make_shared<int>(42);
		cout << p << endl;
		if (!p1.unique())
			p1.reset(new int(5));
		cout << *p1 << endl;
	}
	int *a;
	f();
	a = new int;

}

=====================================================================

第12章 动态内存 416页  智能指针与哑类

=====================================================================

//QQ108201645编写
#ifdef DECLARATION
struct destination;
struct connection
{

};
connection connect(destination*);
void disconnect(connection);
void f(destination &d/*其他参数*/)
{
	//获得一个连接;记住使用完后要关闭它
	connection c = connect(&d);
	//使用连接
	//如果我们在f退出前忘记调用disconnect,就无法关闭c了
}
void f1(destination &d/*其他参数*/)
{
	connection c = connect(&d);
	shared_ptr<connection> p(&c, end_connection);
	//使用连接
	//当f退出时(即使是由于异常而退出),connection会被关闭
}

不使用相同的内置指针值初始化(或reset)多个智能指针。

不delete get() 返回的指针。

不使用get() 初始化或reset另一个智能指针。

如果你使用get() 返回的指针,记住当最后一个对应的智能指针销毁后,你的
指针就变为无效了。   空悬指针

如果你使用智能指针管理的资源不是new分配的内存,记住传递给它一个删除
器。
#endif

=====================================================================

第12章 动态内存 417页  练习题

=====================================================================

//QQ108201645编写
/*练习12.14:编写你自己版本的用 shared_ptr 管理 connection 的函数。*/
#include <memory>
#include <iostream>
using namespace std;

struct  destination {};
struct connection {};

connection connect(destination* pd)
{
	cout << "打开连接" << endl;
	return connection();//返回一个默认构造函数
}
void disconnect(connection c)
{
	cout << "关闭连接" << endl;
}
void end_connection(connection* p)
{
	disconnect(*p);
}
//未使用shared_ptr和版本
void f(destination &d)
{
	cout << "直接管理connect" << endl;
	connection c = connect(&d);
	//忘记调用disconnect关闭连接
	cout << endl;
}

//使用shared_ptr的版本
void f1(destination &d)
{
	cout << "用sshared_ptr管理connect" << endl;
	connection c = connect(&d);
	shared_ptr<connection>p(&c, end_connection);
	//忘记调用disconnect关闭连接
	cout << endl;
}

int main()
{
	destination d;
	f(d);
	f1(d);
	return 0;
}

=====================================================================

//QQ108201645编写
#include <memory>
#include <string>
#include <iostream>
using namespace std;

struct connection
{
	std::string ip_;
	int port_;
	connection(string ip,int port)
		:ip_(ip),port_(port){}
};

struct destination 
{
	string ip_;
	int port_;
	destination(string ip, int port)
		:ip_(ip), port_(port) {}
};

connection connect(destination* pDest)
{
	shared_ptr<connection> pConn(new connection(pDest->ip_, pDest->port_));
	cout << "产生关联(" << pConn.use_count() << ")" << endl;
	return *pConn;
}

void disconnect(connection pConn)
{
	cout << "关闭关联(" << pConn.ip_ << ":" << pConn.port_ << ")" << endl;
}
void end_connection(connection* pConn)
{
	disconnect(*pConn);
}

void f(destination &d)
{
	connection conn = connect(&d);
	shared_ptr<connection> p(&conn, end_connection);
	cout << "马上连接(" << p.use_count() << ")" << endl;
}

int main()
{
	destination dest("198.161.1.1", 80);
	f(dest);
}

=====================================================================

//QQ108201645编写
#include<iostream>
#include <memory>
#include <string>
using namespace std;

struct connection
{
	string ip_;
	int port_;
	connection(const string& ip,int port)
		:ip_(ip),port_(port){}
};

struct destination 
{
	string ip_;
	int port_;
	destination(const string& ip,int port)
		:ip_(ip),port_(port){}
};

connection connect(destination* pDest)
{
	shared_ptr<connection>pConn(new connection(pDest->ip_, pDest->port_));
	cout << "产生关联(" << pConn.use_count() << ")" << endl;
	return *pConn;
}

void disconnect(connection pConn)
{
	cout << "关闭关联(" << pConn.ip_ << ":" << pConn.port_ << ")" << endl;
}

void f(destination &d)
{
	connection c = connect(&d);
	shared_ptr<connection> p(&c, [](connection *p) {disconnect(*p); });

}
int main()
{
	/*练习12.15:重写第一题的程序,用 lambda (参见10.3.2节,第346页)代替end_connection 函数。*/
	destination dest("202,101,153,22", 80);
	f(dest);
}

=====================================================================

第12章 动态内存 417页  unique_ptr

=====================================================================

//QQ108201645编写
#include<iostream>
#include <memory>
#include <string>
using namespace std;

int main()
{
	unique_ptr<double> p1;//可以指向一个double的uunique_ptr
	unique_ptr<int>p2(new int(42));//p2指向一个值为42的int
	unique_ptr<string>p3(new string("Stegosaurus"));
	//unique_ptr<string> p4(p3);//错误,unique_ptr不支持拷贝
	unique_ptr<string> p5;
	//p5 = p3;//错误,unique_ptr不支持赋值

}

=====================================================================

表12.4:unique_ptr操作(另见表12.1,第401页)

unique_ptr<T> u1

空unique_ptr,可以指向类型为T的对象。u1会使用delete来释放它的指针;u2会使用一个类型为D的可调用对象来释放它的指针

unique_ptr<T, D> u2

unique_ptr<T, D> u(d)

空unique_ptr,指向类型为T的对象,用类型为D的对象D代替delete

u=nullptr

释放u指向的对象,将u置为空

u.release()

u放弃对指针的控制权,返回指针,并将u置为空

u.reset()

释放u指向的对象

u.reset(q)

如果提供了内置指针q,令u指向这个对象;否则将u置空

u.reset(nullptr)

 

=====================================================================

第12章 动态内存 418页  unique_ptr

=====================================================================

//QQ108201645编写
#include<iostream>
#include <memory>
#include <string>
using namespace std;

int main()
{
	unique_ptr<string>p1(new string("hello"));
	unique_ptr<string>p2(p1.release());//release将p1置为空
	cout << *p2 << endl;
	unique_ptr<string>p3(new string("Trex"));
	//将所所有权从p3转移给p2

	p2.reset(p3.release());//reset释放了p2原来指向的内存
	cout<<*p2<<endl;
	//p2.release();//错误,p2不会释放内存.而且我们丢失了指针
	auto p=p2.release();//正确,但我们必须记得delete(p)
	delete (p);
}

=====================================================================

第12章 动态内存 417页  传递unique_ptr参数和返回unique_ptr

=====================================================================

//QQ108201645编写
#include<iostream>
#include<memory.h>
using namespace std;

unique_ptr<int> clone(int p)
{
	//正确:从int*创建一个unique_ptr<int>
	return unique_ptr<int>(new int(p));
}
unique_ptr<int> clone1(int p)
{
	unique_ptr<int>ret(new int(p));
	return ret;
}
int main()
{
	unique_ptr<int> p = clone(5);
	unique_ptr<int>p1 = clone1(6);
	cout << *p << " " << *p1 << endl;
}

=====================================================================

第12章 动态内存 417页  向unique_ptr传递删除器

=====================================================================

//QQ108201645编写
#include<iostream>
#include<memory.h>
using namespace std;

struct destination
{
};
struct connection 
{
};
connection connect(destination* p)
{
	cout << "产生关联" << endl;
	return connection();
}
void disconnect(connection c)
{
	cout << "关闭关联" << endl;
}
void end_connection(connection *p)
{
	disconnect(*p);
}
void f(destination& d)
{
	connection c = connect(&d);//打开连接
	unique_ptr<connection, decltype(end_connection)*>
		p(&c, end_connection);

}
int main()
{
	destination d;
	f(d);
	unique_ptr<connection, decltype(end_connection)*> p(new connection, end_connection);
	return 0;
}

 

=====================================================================

第12章 动态内存 419页  练习题

=====================================================================

//QQ108201645编写
/*练习12.16:如果你试图拷贝或赋值 unique_ptr,编译器并不总是能给出易于理解的
错误信息。编写包含这种错误的程序,观察编译器如何诊断这种错误。*/
#include<iostream>
#include <memory>
using namespace std;

int main()
{
	shared_ptr<int> p1 = make_shared<int>(5);
	shared_ptr<int> p2(p1);
	shared_ptr<int> p3 = p2;

	unique_ptr<int> p4(new int(15));
	//unique_ptr<int> p5(p4);//错误不支持拷贝
	unique_ptr<int> p5;
	//p5 = p4;//不支持赋值
	p5.reset(p4.release());
	cout << *p5 << endl;
	//unique_ptr<int> p6(p5);
	//unique_ptr<int> p6 = p5;
	/*
	严重性	代码	说明	项目	文件	行	禁止显示状态
错误(活动)	E1776	无法引用 函数 "std::unique_ptr<_Ty, _Dx>::unique_ptr(const std::unique_ptr<_Ty, _Dx> &) [其中 _Ty=int, _Dx=std::default_delete<int>]" (已声明 所在行数:2337,所属文件:"C:\Program Files\Microsoft Visual Studio\2017\Enterprise\VC\Tools\MSVC\14.16.27023\include\memory") -- 它是已删除的函数	01cpp	C:\Users\Administrator\source\repos\01cpp\01cpp\main.cpp	20

严重性	代码	说明	项目	文件	行	禁止显示状态
错误	C2280	“std::unique_ptr<int,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)”: 尝试引用已删除的函数	01cpp	c:\users\administrator\source\repos\01cpp\01cpp\main.cpp	20

	*/
	
}

=====================================================================

//QQ108201645编写
#include<iostream>
#include <memory>
using namespace std;

int main()
{
	/*
	练习12.17:下面的 unique_ptr 声明中,哪些是合法的,哪些可能导致后续的程序错
	误?解释每个错误的问题在哪里。
int ix = 1024, *pi = &ix, *pi2 = new int(2048);
typedef unique_ptr<int> IntP;
(a) IntP p0(ix);
(b) IntP p1(pi);
(c) IntP p2(pi2);
(d) IntP p3(&ix);
(e) IntP p4(new int(2048));
(f) IntP p5(p2.get());

	*/
	int ix = 1024, *pi = &ix, *pi2 = new int(2048);
	typedef unique_ptr<int> IntP;
	//IntP p0(ix);//不合法,unique_ptr需要用一个指针初始化,无法将int转换为指针
	IntP p1(pi);//合法,可以用一个int*来初始化IntP,但逻辑是错误的,它用一个普通int变量的地址初始化p1,p1销毁时会释放此内存,行为是未定义的
	IntP p2(pi2);//合法,用一个指向动态分配的对象的指针来初始化是正确的
	IntP p3(&ix);//合法,但存在与(b)相同的问题
	IntP p4(new int(2048));//合法,用一个指向动态分配一个新对象来初始化是正确的
	/*IntP p5(p2.get());//合法,但用p2管理的对象的地址来初始化p5,造成两个unique_ptr指向相同的内存地址
	。当其中一个unique_ptr被销毁(或调用reset释放对象)时,该内存被释放,另一个就变成空悬指针*/

	/*练习12.18:shared_ptr 为什么没有 release 成员?
	unique_ptr“独占”对象的所有权,不能拷贝与赋值。release操作
	是用将转移对象所有权给另一个unique_ptr。而多个shared_ptr是可以共享对象的所有权的,可以简单
	拷贝和赋值,不需要release的操作来转移所有权*/
}

=====================================================================

第12章 动态内存 420页  weak_ptr

=====================================================================

//QQ108201645编写

表12.5:weak_ptr

weak_ptr<T > w

空weak_ptr,指向类型为T的对象

weak_ptr<T > w(sp)

与shared_ptr sp指向相同对象的weak_ptr。T必须能转换为sp指向的类型

w = p

p可以是一个shared_ptr或一个weak_ptr。赋值后w与p共享对象

w.reset()

将w置为空

w.use_count()

与w共享对象的shared_ptr的数量

w.expired()

若w.use_count为0,返回true,否则返回false

w.lock()

若expired为true,返回一个空shared_ptr;否则返回一个指向w的对象的shared_ptr

 

=====================================================================

//QQ108201645编写
#include<iostream>
#include <memory>
using namespace std;

int main()
{
	auto p = make_shared<int>(5);
	weak_ptr<int> wp(p);//wp弱指针共享p;p的引用计数未改变
	cout << wp.use_count() << endl;//显示引用次数
	cout << wp.expired() << endl;//引用次数不为0返回0,0才返回true
	if (shared_ptr<int> np = wp.lock())
	{
		cout << "如果np不为空则条件成立" << endl;
	}
	weak_ptr<int> p1 = wp.lock();
	cout << p.use_count() << endl;
	cout << wp.use_count() << endl;
}

=====================================================================

第12章 动态内存 420页  weak_ptr

=====================================================================

//QQ108201645编写
#include <iostream>
#include <memory>
#include <vector>
using namespace std;

int main()
{
	vector<int> v1{ 1,2,3 }, v2{ 4,5,6 };
	shared_ptr<vector<int>> sv1 = make_shared<vector<int>>(v1);
	shared_ptr<vector<int>> sv2(new vector<int>(v2));
	cout << "(*sv1)[0] = " << (*sv1)[0] << endl;
	cout << "(*sv1)[1] = " << (*sv1)[1] << endl;
	cout << "(*sv1)[2] = " << (*sv1)[2] << endl;
	for (auto beg = sv2->begin(), end = sv2->end(); beg != end; ++beg)
		cout << *beg << " ";
	system("pause");
	return 0;
}

=====================================================================

//QQ108201645编写
#include<iostream>
#include <memory>
#include <vector>
#include <string>
using namespace std;
class StrBlobPtr;//对于StrBlob中的友元声明,这个前置声明是必须的
class StrBlob
{
public:
	friend class StrBlobPtr;//声明StrBlobPtr是StrBlob的友元类
	typedef vector<string>::size_type size_type;
	StrBlob();
	StrBlob(initializer_list<string> i1);
	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();
	string& back();

	//返回首元素和尾后元素 StrBlobPtr
	StrBlobPtr begin();//不能直接定义,要定义在StrBlobPtr后面
	StrBlobPtr end();

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> i1)
	: data(make_shared<vector<string>>(i1)) {}

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();
}
string& StrBlob::back()
{
	check(0, "back on empty StrBolb");
	return data->back();
}

void StrBlob::pop_back()
{
	check(0, "pop_back on empty StrBolb");
	return 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();//前缀递增

	bool operator!=(const StrBlobPtr& p)
	{
		return p.curr != curr;
	}
private:
	//若检查成功,check返回一个指向vector的shared_ptr
	shared_ptr<vector<string>> check(size_t, const string&)const;
	//保存一个weak_ptr,意味着底层vector可能会被销毁
	weak_ptr<vector<string>> wptr;
	size_t curr;//在数组中的当前位置
};

shared_ptr<vector<string>> StrBlobPtr::check(size_t i, const string &msg)const
{
	auto ret = wptr.lock();//vector还存在吗
	if (!ret)
		throw runtime_error("unbound StrBlobPtr");
	if (i >= ret->size())
		throw out_of_range(msg);
	return ret;//否则返回指向vector的shared_ptr
}
string& StrBlobPtr::deref()const
{
	auto p = check(curr, "dereference past end");
	return (*p)[curr];//(*p)是对象所指向的vector
}
StrBlobPtr& StrBlobPtr::incr()
{
	//如果curr已经指向容器的尾后位置,就不能递增它
	check(curr, "increnment past end of StrBlobPtr");
	++curr;//推进当前位置
	return *this;
}

StrBlobPtr StrBlob::begin()
{
	return StrBlobPtr(*this);
}
StrBlobPtr StrBlob::end()
{
	auto ret = StrBlobPtr(*this, data->size());
	return ret;
}
int main()
{
	StrBlob blob;
	for (std::string str; std::getline(cin, str); )
		blob.push_back(str);
	for (StrBlobPtr pbeg(blob.begin()), pend(blob.end()); pbeg != pend; pbeg.incr())
		std::cout << pbeg.deref() << std::endl;
}

=====================================================================

第12章 动态内存 422页  练习题

=====================================================================

//QQ108201645编写
/*练习12.19:定义你自己版本的 StrBlobPtr,更新 StrBlob 类,加入恰当的 friend 声明以及 begin 和 end 成员。*/
#include<iostream>
#include <memory>
#include <string>
#include <vector>
#include <initializer_list>
using namespace std;
//提前声明,StrBlob的友元类
class StrBlobPtr;
class StrBlob
{
	friend class StrBlobPtr;
public:
	typedef vector<string>::size_type size_type;
	StrBlob() :data(make_shared<vector<string>>()) {}
	StrBlob(initializer_list<string> i1)
		:data(make_shared<vector<string>>(i1)) {}

	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()
	{
		check(0, "pop_back on empty StrBlob");
		data->pop_back();
	}
	//元素访问
	const string& front()const//const版本首元素
	{
		check(0, "front on empty StrBlob");
		return data->front();
	}
	string& front()
	{
		check(0, "front on empty StrBlob");
		return data->front();
	}
	const string& back()const//const版本尾元素
	{
		check(0, "back on empty StrBlob");
		return data->back();
	}
	string& back()
	{
		check(0, "back on empty StrBlob");
		return data->back();
	}

	//提供给StrBlobPtr的接口
	StrBlobPtr begin();//定义StrBlobPtr后才能定义这两个函数
	StrBlobPtr end();
private:
	shared_ptr<vector<string>> data;
	void check(size_type i, const string& msg)const;
};
void StrBlob::check(size_type i, const string& msg)const
{
	if (i >= data->size())
		throw out_of_range(msg);//抛出一个访问受定义范围之外的元素所带来的错误
}

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

	string& deref()const;
	StrBlobPtr& incr();//前缀递增
	StrBlobPtr& decr();//前缀递减

private:
	//若检查成功,check返回一个指向vector的shared_ptr
	shared_ptr<vector<string>> check(size_t, const string&)const;
	//保存一个weak_ptr,意味着底层vector可能会被销毁
	weak_ptr<vector<string>> wptr;
	size_t curr;//在数组中的当前位置
};
inline shared_ptr<vector<string>> StrBlobPtr::check(size_t i, const string& msg)const
{
	auto ret = wptr.lock();//vector还存在吗?
	if (!ret)
		throw runtime_error("unbound StrBlobPtr");
	if (i >= ret->size())
		throw out_of_range(msg);
	return ret;//否则,返回指向vector的shared_ptr
}
string& StrBlobPtr::deref()const
{
	auto p = check(curr, "dereference past end");
	return (*p)[curr];//(*p)是对象所指向的vector
}
StrBlobPtr& StrBlobPtr::incr()
{
	check(curr, "increment past end of StrBlobPtr");
	++curr;
	return *this;
}
StrBlobPtr& StrBlobPtr::decr()
{
	--curr;
	check(curr, "decrement past begin of StrBlobPtr");
	return *this;
}
StrBlobPtr StrBlob::begin()
{
	return StrBlobPtr(*this);
}
StrBlobPtr StrBlob::end()
{
	auto ret = StrBlobPtr(*this, data->size());
	return ret;
}
bool eq(const StrBlobPtr& lhs, const StrBlobPtr& rhs)
{
	auto l = lhs.wptr.lock(), r = rhs.wptr.lock();
	//若底层的vector是同一个
	if (l == r)
	{
		return (!r || lhs.curr == rhs.curr);
	}
	else
		return false;//若指向不同vector,则不可能相同
}
bool neq(const StrBlobPtr& lhs, const StrBlobPtr& rhs)
{
	return !eq(lhs, rhs);
}
int main()
{
	StrBlob b1;
	{
		StrBlob b2 = {"a","an","the"};
		b1 = b2;
		b2.push_back("about");
		cout << b2.size() << endl;
	}
	cout << b1.size() << endl;
	cout << b1.front() << " " << b1.back() << endl;
	const StrBlob b3 = b1;
	cout << b3.front() << " " << b3.back() << endl;
	for (auto it = b1.begin(); neq(it, b1.end()); it.incr())
		cout << it.deref() << endl;
	return 0;
	
}

=====================================================================

//QQ108201645编写
/*练习12.20:编写程序,逐行读入一个输入文件,将内容存入一个 StrBlob 中,用一
个 StrBlobPtr 打印出 StrBlob 中的每个元素。*/
#include<iostream>
#include <memory>
#include <string>
#include <vector>
#include <initializer_list>
#include<fstream>
#include <algorithm>
using namespace std;

class StrBlobPtr;//前向声明
class StrBlob
{
	friend class StrBlobPtr;//声明StrBlobPtr是StrBlob的友元类
	typedef vector<string>::size_type size_type;
public:
	StrBlob() :data(make_shared<vector<string>>()) {};
	StrBlob(initializer_list<string> i1)
		:data(make_shared<vector<string>>(i1)){}

	void push_back(const string& t)
	{
		data->push_back(t);
	}
	void pop_back()
	{
		check(0, "pop_back on empty StrBlob");
		data->pop_back();
	}
	//返回首元素和尾后元素 StrBlobPtr
	const string& front()const
	{
		check(0, "front on empty StrBlob");
		return data->front();
	}

	string& front()
	{
		check(0, "front on empty StrBlob");
		return data->front();
	}
	const string& back()const
	{
		check(0, "back on empty StrBlob");
		return data->back();
	}
	string& back()
	{
		check(0, "back on empty StrBlob");
		return data->back();
	}
	//返回首元素和尾后元素 StrBlobPtr
	StrBlobPtr begin();//不能直接定义,要定义在StrBlobPtr后面
	StrBlobPtr end();
private:
	shared_ptr<vector<string>> data;
	void check(size_type i, const string &msg)const
	{
		if (i >= data->size())
			throw out_of_range(msg);
	}
};

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

	string &deref()const;
	StrBlobPtr& incr();//前缀引用,递增
	StrBlobPtr& operator++()//用这个替换前缀引用
	{
		check(curr, "increnment past end of StrBlobPtr");
		++curr;
		return *this;
	}
	bool operator!=(const StrBlobPtr& p)
	{
		return curr != p.curr;
	}
	
private:
	shared_ptr<vector<string>> check(size_t i, const string& msg)const
	{
		shared_ptr<vector<string>> ret = wptr.lock();//如果引用次数等于0返回true==(1),不等于0返回0.
		//cout << typeid(ret).name() << endl;
		if (!ret)
			throw runtime_error("unbound StrBlobPtr");
		if (i >= ret->size())
		{
			throw out_of_range(msg);
		}
		return ret;
	}
	weak_ptr<vector<string>> wptr;
	size_t curr;
};
/*练习12.21:也可以这样编写 StrBlobPtr 的 deref 成员:*/

string& StrBlobPtr::deref()const
{
/*
// 	auto p = check(curr, "dereference past end");
// 	return (*p)[curr];
原版更易读。
*/
	return (*check(curr, "dereference past end"))[curr];
}
StrBlobPtr& StrBlobPtr::incr()
{
	check(curr, "increment past end of StrBlobPtr");
	++curr;
	return *this;
}

StrBlobPtr StrBlob::begin()
{
	return StrBlobPtr(*this);
}
StrBlobPtr StrBlob::end()
{
	auto ret = StrBlobPtr(*this, data->size());
	return ret;
}


int main()
{

	string fileName = "1.txt";
	if (remove(fileName.c_str()) == 0)
		cout << "删除文件成功" << endl;
	fstream out(fileName,ostream::out);
	if (!out)
		cerr << "没有文件" << endl;
	cout << "创建并写入文件" << endl;
	out << "hello world" << endl;
	out << "good boy" << endl;
	out << "Hi" << endl;
	out.close();

	out.open(fileName,ios_base::in);
	if (!out)
		cerr << "没有文件" << endl;

	string word;
	StrBlob blob;
	while (getline(out,word))
	{
		blob.push_back(word);
	}
	out.close();
	for (StrBlobPtr pbeg(blob.begin()),pend(blob.end());
		pbeg != pend; ++pbeg)
	{
		cout << pbeg.deref() << endl;
	}
	system("pause");
	return 0;
}

=====================================================================

//QQ108201645编写
/*练习12.22:为了能让 StrBlobPtr 使用 const StrBlob,你觉得应该如何修改?
	定义一个名为ConstStrBlobPtr 的类,使其能够指向 const StrBlob。*/
#include<iostream>
#include <memory>
#include <string>
#include <vector>
#include <initializer_list>
#include<fstream>
#include <algorithm>
using namespace std;

class constStrBlobPtr;//前向声明
class StrBlob
{
	friend class constStrBlobPtr;//声明StrBlobPtr是StrBlob的友元类
	typedef vector<string>::size_type size_type;
public:
	StrBlob() :data(make_shared<vector<string>>()) {};
	StrBlob(initializer_list<string> i1)
		:data(make_shared<vector<string>>(i1)){}

	void push_back(const string& t)const
	{
		data->push_back(t);
	}
	void pop_back()const
	{
		check(0, "pop_back on empty StrBlob");
		data->pop_back();
	}
	//返回首元素和尾后元素 StrBlobPtr
	const string& front()const
	{
		check(0, "front on empty StrBlob");
		return data->front();
	}

	string& front()
	{
		check(0, "front on empty StrBlob");
		return data->front();
	}
	const string& back()const
	{
		check(0, "back on empty StrBlob");
		return data->back();
	}
	string& back()
	{
		check(0, "back on empty StrBlob");
		return data->back();
	}
	//返回首元素和尾后元素 StrBlobPtr

	//const类型这里要改变
	constStrBlobPtr begin()const;//不能直接定义,要定义在StrBlobPtr后面
	constStrBlobPtr end()const;
private:
	shared_ptr<vector<string>> data;
	void check(size_type i, const string &msg)const
	{
		if (i >= data->size())
			throw out_of_range(msg);
	}
};

class constStrBlobPtr
{
public:
	constStrBlobPtr():curr(0){}
	//const类型这里要改变
	constStrBlobPtr(const StrBlob& a,size_t sz=0)
		:wptr(a.data),curr(sz){}

	string &deref()const;
	constStrBlobPtr& incr();//前缀引用,递增
	constStrBlobPtr& operator++()//用这个替换前缀引用
	{
		check(curr, "increment past end of StrBlobPtr");
		++curr;
		return *this;
	}
	bool operator!=(const constStrBlobPtr& p)
	{
		return curr != p.curr;
	}
	
private:
	shared_ptr<vector<string>> check(size_t i, const string& msg)const
	{
		shared_ptr<vector<string>> ret = wptr.lock();//如果引用次数等于0返回true==(1),不等于0返回0.
		//cout << typeid(ret).name() << endl;
		if (!ret)
			throw runtime_error("unbound StrBlobPtr");
		if (i >= ret->size())
		{
			throw out_of_range(msg);
		}
		return ret;
	}
	weak_ptr<vector<string>> wptr;
	size_t curr;
};

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

//const类型这里要改变
constStrBlobPtr StrBlob::begin()const
{
	return constStrBlobPtr(*this);
}
constStrBlobPtr StrBlob::end()const
{
	auto ret = constStrBlobPtr(*this, data->size());
	return ret;
}


int main()
{

	string fileName = "1.txt";
	if (remove(fileName.c_str()) == 0)
		cout << "删除文件成功" << endl;
	fstream out(fileName,ostream::out);
	if (!out)
		cerr << "没有文件" << endl;
	cout << "创建并写入文件" << endl;
	out << "hello world" << endl;
	out << "good boy" << endl;
	out << "Hi" << endl;
	out.close();

	out.open(fileName,ios_base::in);
	if (!out)
		cerr << "没有文件" << endl;

	string word;
	//const类型这里要改变
	const StrBlob blob;
	while (getline(out,word))
	{
		blob.push_back(word);
	}
	out.close();
	for (constStrBlobPtr pbeg(blob.begin()),pend(blob.end());
		pbeg != pend; ++pbeg)
	{
		cout << pbeg.deref() << endl;
	}
	system("pause");
	return 0;
}

=====================================================================

第12章 动态内存 423页  动态数组

=====================================================================

//QQ108201645编写
#include<iostream>
using namespace std;

int get_size()
{
	return 5;
}
int main()
{
	int *pia = new int[get_size()];//pia指向第一个int
	typedef int arrT[42];//arrT表示42个int的数组类型
	int *p = new arrT;//分配一个42个int的数组;p指向第一个int
	/*int *p = new int[42];编译器执行这个表达式时还是会用new[ ]*/
	
}

=====================================================================

//QQ108201645编写
#include<iostream>
#include<string>
using namespace std;
#define N 5
#define size_zero 0
size_t get_size()
{
	return N;
}
int main()
{
	int *pia = new int[10];//10个未初始化的int
	int *pia2 = new int[10]();//10个值初始化值为0的int
	string *psa = new string[10];//10个空string
	string *psa2 = new string[10]();//10个空string
	//新标准支持元素初始化器的花括号列表
	int *pia3 = new int[10]{ 0,1,2,3,4,5,6,7,8,9 };
	//10个string ,前4个用给定的初始化器来初始化,剩余的进行值初始化
	string *psa3 = new string[10]{ "a","an","the",string(3,'x') };

	size_t n = get_size();//get_size返回需要的元素的数目
	int* p = new int[n];//分配数组保存元素
	for (int *q = p, i = 0; q != p + n; ++q)
		*q = i++;

	//char arr[size_zero];//错误,不能定义长度为0的数组
	char *cp = new char[size_zero];//正确.但cp不能解引用
	cout<<*cp<<endl;
	/*释放动态数组*/
	delete p;//p必须指向一个动态分配的对象或为空

	int *pa = new int[n] {1, 2, 3, 4, 5};//注意观察调试
	delete[] pa;//pa必须指向一个动态分配的数组或为空

	typedef int arrT[42]; //arrT表示42个int的数组类型别名
	p = new arrT;//分配一个42个int 的数组;p指向第一个元素
	int *beg=p,*end = p + 42;
	while (beg!=end)
	{
		*beg++ = 1;
	}
	delete [] p;//方括号是必需的,因为分配的是一个数组
	system("pause");
	return 0;
}

=====================================================================

//QQ108201645编写
#include<iostream>
#include<memory>
using namespace std;

int main()
{
	unique_ptr<int[]>up(new int[10]);
	
	for (size_t i = 0; i != 10; ++i)
		up[i] = i;
	
	up.release();//自动调用delete[]销毁其指针
	//<int[]>>指up指向一个int数组而不是一个int,当up销毁它管理的指针时,会自动使用delete[]。
	system("pause");
	return 0;
}

=====================================================================

第12章 动态内存 426页  指向数组的unique_ptr

=====================================================================

//QQ108201645编写

表12.6:指向数组的unique_ptr

指向数组的unique_ptr不支持成员访问运算符(点和箭头运算符)。

其它unique_ptr操作不变。

unique_ptr<T[ ]> u

u可以指向一个动态分配的数组,数组元素类型为T

unique_ptr<T[ ]> u (p)

u指向内置指针p所指向的动态分配的数组。p必须能转换为类型T*(参见4.11.2节,第143页)

u[i]

返回u拥有的数组中i处的对象

u必须指向一个数组

 

=====================================================================

//QQ108201645编写
#include<iostream>
#include<memory>
using namespace std;

int main()
{
	/*shared_ptr不直接支持管理动态数组,如果希望使用shared_ptr
	管理一个动态数组,必须提供自己定义的删除器*/
	shared_ptr<int> sp(new int[10], [](int *p) {delete[] p; });
	

	//shared_ptr未定义下标运算符,并且不支持指针的算术运算
	for (size_t i = 0; i != 10; ++i)
		*(sp.get() + i) = i;//使用get获取一个内置批针

	sp.reset();//使用提供的lambda表达式释放数组,它使用delete[]

	system("pause");
	return 0;
}

=====================================================================

第12章 动态内存 426页  练习题

=====================================================================

//QQ108201645编写
#include<iostream>
#include<string>
#include <conio.h>
#pragma warning(disable:4996)
using namespace std;

int main()
{
	/*练习12.23:编写一个程序,连接两个字符串字面常量,将结果保存在一个动态分配的
	char数组中。重写这个程序,连接两个标准库string对象。*/
	const char *c1 = "hello ";
	const char *c2 = "world";
	size_t len = strlen(c1) + strlen(c2) + 1;
	char *str = new char[len]{ 0 };
	strcat(str, c1);
	strcat(str, c2);
	cout << str << endl;
	memset(str, 0, len);//清空内容

	const string s1 = "hello", s2 = " world";
	strcpy(str, (s1 + s2).c_str());//将s1+s2的结果转换成char*并拷贝给str
	cout << str << endl;
	delete[] str;//释放内存

	/*练习12.24:编写一个程序,从标准输入读取一个字符串,存入一个动态分配的字符数组中。描述你的程
	序如何处理变长输入。测试你的程序,输入一个超出你分配的数组长度的字符串。*/
#define N 12
	str = new char[N]{ 0 };

	cout << "input: ";
	char c;
	int l = 0;
	while (c=getchar())
	{
		if (l >= N-1)
		{
			cout << "达到" << l << "个数组容量上限:" << endl;
			break;
		}
		str[l++] = c;//存入动态数组
	}
	cout << str << endl;
	delete[] str;//释放内存

	/*练习12.25:给定下面的new表达式,你应该如何释放pa?
	int *pa = new int[10];
	delete [] pa;
	*/
	system("pause");
	return 0;
}

=====================================================================

第12章 动态内存 427页  allocator类

=====================================================================

//QQ108201645编写
#include<iostream>
#include<string>
#include <memory>
using namespace std;

int main()
{
	const int n = 5;
	string *const p = new string[n];//构造n个空string
	string s;
	string *q = p;//q指向第一个string
	while (q != p + n&&cin>>s)
	{
		*q++ = s;//赋予*q一个新值
	}
	const size_t size = q - p;//记住我们读取了多少个string
	//使用数组
	delete[] p;//p指向了一个数组,记得用delete[]来释放
	cout << size << endl;

	/*标准allocator类在memory中*/
	allocator<string> alloc;//可以分配string的allocator对象
	auto const p = alloc.allocate(n);//分配n个未初始化的string
	//allocate调用为n个string分配了内存
	system("pause");
	return 0;
}

=====================================================================

//QQ108201645编写

表12.7:标准库allocator类及其算法

allocator<T> a

定义了一个名为a的allocator对象,它可以为类型为T的对象分配内存

a.allocate(n)

分配一段原始的、未构造的内存,保存n个类型为T的对象

a.deallocate(p, n)

释放从T*指针p中地址开始的内在,这块内在保存了n个类型为T的对象;p必须是一个先前由allocator返回的指针,且n必须是p创建时所需要的大小。在调用deallocate之前,用户必须对每个在这块内存中创建的对象调用destory

a.construct(p, args)

p必须是一个类型为T*的指针,指向一块原始内存;args被传递给类型为T的构造函数,用来在p指向的内存中构造一个对象

a.destory(p)

p为T*类型的指针,此算法对p指向的对象执行析构函数(参见12.1.1节,第402页)

 

=====================================================================

第12章 动态内存 428页  allocator分配未构造的内存

=====================================================================

//QQ108201645编写
#include<iostream>
#include<string>
#include <memory>
using namespace std;

int main()
{
	/*标准allocator类在memory中*/
	const size_t n = 5;
	allocator<string> alloc;//可以分配string的allocator对象
	auto const p = alloc.allocate(n);//分配n个未初始化的string
	//allocate调用为n个string分配了内存

	string *q = p;//q指向最后构造的元素之后的位置
	alloc.construct(q++);//*q为空字符串
	alloc.construct(q++, 10, 'c');//*q为10个'c'
	alloc.construct(q++, "hi");//*q为hi

	cout << *p << endl;//正确:使用string的输出运算符
	//cout << *q << endl;//错误;q指向未构造的内存
	while (q!=p)
	{
		//alloc.destroy(--q);//用下面来表示
		--q;
		cout<<*q<<endl;
		alloc.destroy(q);//destroy销毁构造的元素
	}
	/*销毁元素后,就可以重新使用这部分内存保存其他的string,也可以将其归还给系统。
	释放内存通过调用deallocate来完成*/
	alloc.deallocate(p, n);
	system("pause");
	return 0;
}

=====================================================================

第12章 动态内存 429页  allocator算法

=====================================================================

//QQ108201645编写

表12.8: allocator算法

这些函数在给定目的的位置创建元素,而不是由系统分配内存给它们

uninitialized_copy(b,e,b2)

从迭代器b和e指出的输入范围中拷贝元素到迭代器b2指定的未构造的原始内存中。b2指向的内存必须足够大,能容纳输入序列中元素的拷贝

uninitialized_copy_n(b,n,b2)

从迭代器b指向的元素开始,拷贝b个元素到b2开始的内存中

uninitialized_fill(b,e,t)

从迭代器b指定的原始范围中创建对象,对象的值均为t的拷贝

uninitialized_fill_n(b,n,t)

从迭代器b指向的内存地址开始创建n个对象。b必须指向足够大的未构造的原始内存,能够容纳给定数量的对象

=====================================================================

第12章 动态内存 429页  allocator算法

=====================================================================

//QQ108201645编写
#include<iostream>
#include<string>
#include <memory>
#include <vector>
using namespace std;

int main()
{
	vector<string> vi{ "hello","world","hi" };
	const size_t len = vi.size() * 2;

	//分配比vi中元素所占用空间大一倍的动态内存
	allocator<string> alloc;
	auto p = alloc.allocate(len);//p是第一个位置
	//通过拷贝vi中的元素来构造从p开始的元素
	auto q = uninitialized_copy(vi.begin(), vi.end(), p);//q是复制后目标末尾之后的元素
	//将剩余的元素初始化为"42"
	uninitialized_fill_n(q, vi.size(), "42");

	for (size_t i = 0; i != len; ++i)
		cout << *(p+i)<< endl;
	system("pause");
	return 0;
}

=====================================================================

第12章 动态内存 429页  allocator练习题

=====================================================================

//QQ108201645编写
#include<iostream>
#include<string>
#include<memory>
using namespace std;

int main()
{
	/*练习题12.26:使用allocator重写427页中的程序。*/
	allocator<string> alloc;
	const size_t n = 5;
	string *const p = alloc.allocate(n);
	string s;
	string *q = p;
	int i = 0;
	while (i != n && cin >> s)
	{
		alloc.construct(q + i, s);
		i++;
	}
	i = 0;
	while (i != n)//输出内容
	{
		cout << *(q + i) << endl;
		i++;
	}
	q += n;
	while (q != p)//释放内容
	{
		--q;
		alloc.destroy(q);
	}

	alloc.deallocate(p, n);//释放p开始的指针位置,大小为n
	system("pause");
	return 0;
}

 

=====================================================================

#include<iostream>
#include<string>
#include<memory>
#include <vector>
#include<algorithm>

using namespace std;


vector<string> svec = { "abc","def","hello","world","123" };
const int N = svec.size()*2;
int main()
{
	allocator<string> alloc;
	string* p = alloc.allocate(N);
	cout << typeid(p).name() << endl;
	string* c = uninitialized_copy(svec.begin(), svec.end(), p);
	cout << typeid(c).name() << endl;
	c = uninitialized_fill_n(c, svec.size(), "42");
	
	c = p;
	int i = 0;
	cout << "输出内容:" << endl;
	while (i!=N)
	{
		cout << *c++ << endl;
		i++;
	}

	while (c != p)
	{
		--c;
		alloc.destroy(c);
	}
	alloc.deallocate(p, N);
	system("pause");
	return 0;
}

 

=====================================================================

#include<iostream>
#include<string>
#include<memory>
#pragma warning(disable:4996)
using namespace std;
int main()
{
	string s("hello world 123 456 789 111 ");//带空格的6个元素
	int n = 6;
	allocator<char*> alloc;
	/*char** const*/auto p = alloc.allocate(n);
	//cout << typeid(p).name() << endl;
	char** c = p;
	string::size_type pos = 0, cur = 0;
	while ((cur = s.find_first_of(' ', cur)) != string::npos)
	{

		char *d = new char[cur + 1 - pos];
		strcpy(d, s.substr(pos, cur - pos).c_str());//cur-pos的个数
		alloc.construct(c, d);
		c++;
		pos = ++cur;
	}
	int i = 0;
	while (i != n)
	{
		char **d = p + i;
		cout << *d << " ";
		i++;
	}
	while (c != p)
	{
		c--;
		cout << *c << " ";
		delete[] *c;
		alloc.destroy(c);
	}
	alloc.deallocate(p, n);
	system("pause");
	return 0;
}

 

=====================================================================

#include<iostream>
#include<string>
#include<memory>
#pragma warning(disable:4996)
using namespace std;
class chars
{
public:
	chars(const char *str)
	{
		int len = strlen(str) + 1;
		str_ = new char[len];
		memset(str_, 0, len);
		strncpy(str_, str, len);
	}
	~chars()
	{
		cout << "\n析构:" << str_ << endl;
		delete[] str_;
	}
	char* Show()
	{
		return str_;
	}

private:
	char* str_;
};
int main()
{
	string s("hello world 123 456 789 111 ");//带空格的6个元素
	int n = 6;
	allocator<chars> alloc;
	/*char** const*/auto p = alloc.allocate(n);
	//cout << typeid(p).name() << endl;
	auto c = p;
	string::size_type pos = 0, cur = 0;
	while ((cur = s.find_first_of(' ', cur)) != string::npos)
	{
		alloc.construct(c, s.substr(pos, cur - pos).c_str());
		c++;
		pos = ++cur;
	}
	int i = 0;
	while (i != n)
	{
		auto d = p + i;
		cout << d->Show() << " ";
		i++;
	}
	while (c != p)
	{
		c--;
		alloc.destroy(c);//模板只会调用带析构的对象,所以用chars类来保存
	}
	alloc.deallocate(p, n);
	system("pause");
	return 0;
}

=====================================================================

第12章 动态内存 430页  使用标准库:文本查询程序

=====================================================================

//QQ108201645编写
#include <iostream>
#include <sstream>
#include <string>
#include <memory>
#include <vector>
#include <map>
#include <set>
#include <fstream>
#include <algorithm>
using namespace std;

string make_plural(size_t ctr, const string &word,
	const string &ending)
{
	return (ctr > 1) ? word + ending : word;
}
class QueryResult;
class TextQuery
{
	typedef vector<string>::size_type line_no;
public:
	TextQuery(fstream&);
	QueryResult query(const string&)const;
private:
	shared_ptr<vector<string>> file;
	map<string, shared_ptr<set<line_no>> > wm;
};
TextQuery::TextQuery(fstream& is) :file(new vector<string>)//new一个vector存入进去
{
	string text;
	while (getline(is, text))//文件中的每一行
	{
		file->push_back(text);//保存文本
		int n = file->size() - 1;//当前行号
		istringstream line(text);//文本解成单词
		string word;
		while (line >> word)//行中的每个单词
		{//如果不在wm中,以它为下标在wm中添加一项
			shared_ptr<set<line_no>> &lines = wm[word];//lines是一个shared_ptr(返回的是引用,可以改变wm的元素)
			if (!lines)//在第一次遇到这个单词时,指针为空
				lines.reset(new set<line_no>);//分配一个新的set
			lines->insert(n);//将行号插入set中
		}
	}
}


class QueryResult
{
	friend ostream &print(ostream &os, const QueryResult &qr);
public:
	typedef vector<string>::size_type line_no;
	QueryResult(string s,
		shared_ptr<set<line_no>> p,
		shared_ptr<vector<string>> f)
		:sought(s), lines(p), file(f) {}

private:
	string sought;//查询单词
	shared_ptr<set<line_no>> lines;//出现的行号
	shared_ptr < vector<string>> file;//输入文件
};
QueryResult TextQuery::query(const string& sought)const
{//如果示找到sought,将返回一个指向set的指针
	static shared_ptr<set<line_no>> nodata(new set<line_no>);
	//使用find而不是下标运算符来查找单词,避免将单词添加到wm中!
	//auto loc = wm.find(sought);
	//cout << typeid(loc).name() << endl;
	map<string, shared_ptr<set<line_no>>>::const_iterator loc = wm.find(sought);
	
	if (loc == wm.end())
		return QueryResult(sought, nodata, file);//未找到
	else
		return QueryResult(sought, loc->second, file);
}
ostream &print(ostream &os, const QueryResult &qr)
{//如果找到了单词,打印出现次数和所有出现位置
	os << qr.sought << " occurs " << qr.lines->size() << " "
		<< make_plural(qr.lines->size(), " time", "s") << endl;
	//打印单词出现的每一行
	for (auto num : *qr.lines)
		os << "\t(line " << num + 1 << ")"//对set中的每个单词
		<< *(qr.file->begin() + num) << endl;//避免行号从0开始
	return os;
}
void runQueries(fstream &infile)
{
	// infile 是一个 fstream ,指向我们要处理的文件
	TextQuery tq(infile);  // 保存文件并建立查询map
	// 与用户交户:提示用户输入要查询的单词,完成查询并打印结果
	while (true)
	{
		cout << "enter word to look for, or q to quit: ";
		string s;
		// 或遇到文件尾或用户输入'q'时循环终止
		if (!(cin >> s) || s == "q") break;
		// 指向查询并打印结果
		print(cout, tq.query(s)) << endl;
	}
}
int main()
{
	string s = "1.txt";
	if (remove(s.c_str()) == 0)
		cout << "重建文件" << endl;

	fstream infile(s, ios::out);
	infile << "A set element contains only a key\n\n\n"
		"operator creates a new element\n\n\n"
		"Regardless of whether the element\n\n"
		"When we fetch an element from a map,we\n\n"
		"If the element is not found, find returns";//写入内容
	infile.close();
	infile.open(s, ios::in);
	if (!infile)
	{
		cerr << "No input file!" << endl;
		return EXIT_FAILURE;
	}
	runQueries(infile);
	infile.close();
	return 0;

}

=====================================================================

//QQ108201645编写
/*练习题12.27与12.30:TextQuery和QueryResult类的使用,我们已经介绍过语和标准
库特性。不要提前看后续章节内容,只用已经学到的知识对这两个类编写你自己的版本*/
/*练习题12.31:如果用vector代替set保存行号,会有什么差异,哪种更好,为什么
虽然set比vector会维护关键字的顺序,但我们是逐行读取文本输入,因此每个单词
出现的行号是自然按升序加入到容器中,不用特意用关联容器来保证行号的升序。
set是基于红黑树实现的,插入操作时间复杂性为O(logn)(n为
容器中的元素数目),而vector的push_back可达到常量时间。
另外,一个单词在同一行中可能出现多次。set自然可保证关键字不重复,但对vector这也
构不成障碍,每次添加行号前与最后一个行号比较一下就可以,同一行就不插入,总体性能还是vector更优*/
#include<iostream>
#include <map>
#include <set>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
using namespace std;

class QueryResult;
class TextQuery
{
	using line_no = vector<string>::size_type;
public:
	TextQuery(fstream& is) :file(new vector<string>)//new一个vector存入进去
	{
		string text;
		while (getline(is,text))//文件中的每一行
		{
			file->push_back(text);//保存文本
			int n = file->size() - 1;//当前行号
			istringstream line(text);//文本解成单词
			string word;
			while (line>>word)//行中的每个单词
			{//如果不在wm中,以它为下标在wm中添加一项
				shared_ptr<set<line_no>> &lines = wm[word];//lines是一个shared_ptr
				if (!lines)//在第一次遇到这个单词时,指针为空
					lines.reset(new set<line_no>);//分配一个新的set
				lines->insert(n);//将行号插入set中
			}
		}
	}
	QueryResult query(const string&)const;
private:
	shared_ptr<vector<string>> file;
	map<string, shared_ptr<set<line_no>> >wm;
};



class QueryResult
{
	friend ostream &print(ostream& os, const QueryResult &qr);
public:
	typedef vector<string> ::size_type line_no;
	QueryResult(string s,
		shared_ptr<set<line_no>> p,
		shared_ptr<vector<string>> f)
		:sought(s),lines(p),file(f){}

	/*练习12.33:在第15章中我们将扩展查询系统,在QueryResult类中将会需要一些
	额外的成员。添加名为begin和end的成员,返回一个迭代器,指向一个给定查询返回
	的行的set中的位置。再添加一个名为get_file的成员,返回一个shared_ptr,
	指向QueryResult对象中的文件*/
	typedef set<line_no>::const_iterator line_it;
	line_it begin()const
	{
		return lines->cbegin();
	}
	line_it end()const
	{
		return lines->cend();
	}
	shared_ptr<vector<string>> get_file()
	{
		return file;
	}
private:
	string sought;//查询单词
	shared_ptr<set<line_no>> lines;//出现的行号
	shared_ptr < vector<string>> file;//输入文件
};
QueryResult TextQuery::query(const string& sought)const
{//如果示找到sought,将返回一个指向set的指针
	static shared_ptr<set<line_no>> nodata(new set<line_no>);
	//使用find而不是下标运算符来查找单词,避免将单词添加到wm中!
	//auto loc = wm.find(sought);
	//cout << typeid(loc).name() << endl;
	map<string, shared_ptr<set<line_no>>>::const_iterator loc = wm.find(sought);
	if (loc == wm.end())
		return QueryResult(sought, nodata, file);
	else
	{
		return QueryResult(sought, loc->second, file);
	}
}
ostream& print(ostream& os, const QueryResult &qr)
{//如果找到了单词,打印出现次数和所有出现位置
	os << qr.sought << " occurs " << qr.lines->size() << " "
		<< "times" << endl;
	for (auto num : *qr.lines)//打印单词出现的每一行
		os << "\t(line" << num + 1 << ")"//对set中的每个单词
		<< *(qr.file->begin()+num)<< endl;//避免行号从0开始
	return os;
}
void runQueries(fstream &infile)
{// infile 是一个 fstream ,指向我们要处理的文件
	TextQuery tq(infile);// 保存文件并建立查询map

	/*while (true)// 与用户交户:提示用户输入要查询的单词,完成查询并打印结果
	{
		cout << "enter word to look for, or q to quit ";
		string s;
		if (!(cin >> s) || s == "q")break;

		print(cout, tq.query(s)) << endl;
	}*/
	/*练习题12.29:我们曾经用do while循环来编写管理用户交互的循环(参见5.4.4节,第
	169页)。用do while重写本节程序,解释你倾向于哪个版本,为什么。*/
	do// 与用户交户:提示用户输入要查询的单词,完成查询并打印结果
	{
		cout << "enter word to look for, or q to quit ";
		string s;
		if (!(cin >> s) || s == "q")break;

		print(cout, tq.query(s)) << endl;
	} while (true);
	/*步骤:输入->检查循环条件->执行查询
	检查循环条件是在中间步骤,因此两者没什么区别,也不会比另一个更简洁,
	只能根据需要来使用*/
}
int main()
{
	string s = "1.txt";
	if (remove(s.c_str()) == 0)
		cout << "重建文件" << endl;

	fstream infile(s, ios::out);
	infile << "A set element contains only a key\n\n\n"
		"operator creates a new element\n\n\n"
		"Regardless of whether the element\n\n"
		"When we fetch an element from a map,we\n\n"
		"If the element is not found, find returns";//写入内容
	infile.close();
	infile.open(s, ios::in);
	if (!infile)
	{
		cerr << "No input file!" << endl;
		return EXIT_FAILURE;
	}
	runQueries(infile);
	infile.close();
	return 0;
}

=====================================================================

/*练习12.28:编写程序实现文本查询,不要定义类来管理数据。你的程序应该接受一个
文件,并与用户交互来查询单词。使用vector、map和set容器来保存来自文件的数
据并生成查询结果。*/
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <fstream>
#include <map>
#include <set>
using namespace std;

using line_no = vector<string>::size_type;
vector<string> file;
map<string, set<line_no>> wm;
string cleanup_str(const string &word)
{
	string ret;
	for (auto it = word.begin(); it != word.end(); ++it)
	{
		if (!ispunct(*it))//如果参数不是标点符号
			ret += tolower(*it);//就把内容转成小写
	}
	return ret;
}

void input_text(fstream &is)
{
	string text;
	while (getline(is, text))//对文件中的每一行
	{
		file.push_back(text);//保存这行
		int n = file.size();//当前行号
		istringstream line(text);//将文本解为单词
		string word;
		while (line >> word)
		{
			wm[cleanup_str(word)].insert(n);
		}
	}
}

ostream &query_and_print(const string &sought, ostream& os)
{
	//使用find而不是下标运算符来查找单词,避免将单词添加到wm中
	map<string, set<line_no>>::const_iterator loc = wm.find(sought);
	if (loc == wm.end())
	{
		os << sought << "出现了0次" << endl;
	}
	else
	{
		set<line_no> lines = loc->second;//行号set
		os << sought << "出现了" << lines.size() << "次" << endl;
		for (auto num : lines)
			os << "\t(第" << num << "行) "
			<< *(file.begin() + num - 1) << endl;
	}
	return os;
}
void runQueries(fstream &infile)
{
	//infile是一个fstream,指向我们要查询的文件
	input_text(infile);//读入文本并建立查询map
		//与用户交互;提示用户输入要查询的单词,完成查询并打印结果
	while (true)
	{
		cout << "enter word to look for, or q to quit: ";
		string s;
		if (!(cin >> s) || s == "q")break;
		query_and_print(s, cout) << endl;
	}
}
int main()
{
	string s = "1.txt";
	if (remove(s.c_str()) == 0)
		cout << "重建文件" << endl;

	fstream infile(s, ios::out);
	infile << "A set element contains only a key\n\n\n"
		"operator creates a new element\n\n\n"
		"Regardless of whether the element\n\n"
		"When we fetch an element from a map,we\n\n"
		"If the element is not found, find returns";//写入内容
	infile.close();
	infile.open(s, ios::in);
	if (!infile)
	{
		cerr << "No input file!" << endl;
		return EXIT_FAILURE;
	}
	runQueries(infile);
	infile.close();
	return 0;

}

=====================================================================

//QQ108201645编写
/*练习题12.32重写TextQuery和QueryResult类,用StrBlob代替vector<string>
保存输入文件*/
#include<iostream>
#include <set>
#include <map>
#include <vector>
#include <string>
#include <sstream>
#include <fstream>
#include <memory>
#include <algorithm>
#include <initializer_list>
using namespace std;

class StrBlobPtr;//对于StrBlob中的友元声明,这个前置声明是必须的
class StrBlob
{
public:
	friend class StrBlobPtr;//声明StrBlobPtr是StrBlob的友元类
	typedef vector<string>::size_type size_type;
	StrBlob();
	StrBlob(initializer_list<string> i1);
	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();
	string& back();

	//返回首元素和尾后元素 StrBlobPtr
	StrBlobPtr begin();//不能直接定义,要定义在StrBlobPtr后面
	StrBlobPtr end();

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> i1)
	: data(make_shared<vector<string>>(i1)) {}

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();
}
string& StrBlob::back()
{
	check(0, "back on empty StrBolb");
	return data->back();
}

void StrBlob::pop_back()
{
	check(0, "pop_back on empty StrBolb");
	return 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();//前缀递增

	bool operator!=(const StrBlobPtr& p)
	{
		return p.curr != curr;
	}
private:
	//若检查成功,check返回一个指向vector的shared_ptr
	shared_ptr<vector<string>> check(size_t, const string&)const;
	//保存一个weak_ptr,意味着底层vector可能会被销毁
	weak_ptr<vector<string>> wptr;
	size_t curr;//在数组中的当前位置
};

shared_ptr<vector<string>> StrBlobPtr::check(size_t i, const string &msg)const
{
	auto ret = wptr.lock();//vector还存在吗
	if (!ret)
		throw runtime_error("unbound StrBlobPtr");
	if (i >= ret->size())
		throw out_of_range(msg);
	return ret;//否则返回指向vector的shared_ptr
}
string& StrBlobPtr::deref()const
{
	auto p = check(curr, "dereference past end");
	return (*p)[curr];//(*p)是对象所指向的vector
}
StrBlobPtr& StrBlobPtr::incr()
{
	//如果curr已经指向容器的尾后位置,就不能递增它
	check(curr, "increnment past end of StrBlobPtr");
	++curr;//推进当前位置
	return *this;
}

StrBlobPtr StrBlob::begin()
{
	return StrBlobPtr(*this);
}
StrBlobPtr StrBlob::end()
{
	auto ret = StrBlobPtr(*this, data->size());
	return ret;
}


class QueryResult;
class TextQuery
{
	using line_no = vector<string>::size_type;
public:
	TextQuery(fstream &is) :file(new StrBlob)//分配一个vector传给file
	{
		string text;
		while (getline(is,text))//读取一行
		{
			file->push_back(text);//保存当前一行内容
			int n = file->size() - 1;//通过file大小得到当前行号
			istringstream line(text);//把text的内容传给line解读成单词
			string s;
			while (line >> s)//逐个读取
			{
				shared_ptr<set<line_no>> &lines = wm[s];//把单词存入map的key并返回value的引用(默认统计第二个元素的大小)
				if (!lines)//如果为空时就分配一个set
					lines.reset(new set<StrBlob::size_type>);
				lines->insert(n);//插入行号,相同会被忽略
			}
		}
	}
	QueryResult query(const string&)const;
private:
	shared_ptr<StrBlob> file;
	map<string, shared_ptr<set<line_no>>> wm;
	static string cleanup_str(const string& word)
	{
		string ret;
		for (auto it = word.begin(); it != word.end(); ++it)
			if (!ispunct(*it))
				ret += tolower(*it);
		return ret;
	}
};
class QueryResult
{
	friend ostream& print(ostream& os, const QueryResult&);
	typedef vector<string>::size_type line_no;
	typedef set<line_no>::const_iterator line_it;
public:
	QueryResult(string s,
		shared_ptr<set<StrBlob::size_type>> p,
		shared_ptr<StrBlob> f)
		:sought(s),lines(p),file(f){}

	set<line_no>::size_type size()const
	{
		return lines->size();
	}
	line_it begin()const
	{
		return lines->cbegin();
	}
	line_it end()const
	{
		return lines->cend();
	}
private:
	string sought;
	shared_ptr<set<StrBlob::size_type>> lines;
	shared_ptr<StrBlob> file;
};
std::ostream& print(std::ostream &os, const QueryResult& qr)
{
	os << qr.sought << " occurs " << qr.lines->size()
		<< (qr.lines->size() > 1 ? " times" : " time") << std::endl;
	for (auto i : *qr.lines) {
		StrBlobPtr p(*qr.file, i);
		os << "\t(line " << i + 1 << ") " << p.deref() << std::endl;
	}
	return os;
}
QueryResult TextQuery::query(const string& s)const
{
	static shared_ptr<set<line_no>> nodata(new set<line_no>);
	map<string, shared_ptr<set<line_no>>>::const_iterator loc = wm.find(cleanup_str(s));
	if (loc == wm.end())
		return QueryResult(s, nodata, file);
	else
		return QueryResult(s, loc->second, file);
}
void runQueries(fstream &s)
{
	TextQuery tq(s);
	while (true)
	{
		cout << "输入查询内容:" << endl;
		string s;
		if (!(cin>>s)||s=="q")break;
		print(cout, tq.query(s));
	}
}
int main()
{
	string s1 = "1.txt";
	if (remove(s1.c_str()) == 0)
		cout << "重建文件" << endl;
	fstream infile(s1, ios::out);
	infile << "A set element contains only a key\n\n\n"
		"operator creates a new element\n\n\n"
		"Regardless of whether the element\n\n"
		"When we fetch an element from a map,we\n\n"
		"If the element is not found, find returns";//写入内容
	infile.close();
	infile.open(s1);
	if (!infile)
	{
		cerr << "No input file" << endl;
		return EXIT_FAILURE;
	}
	runQueries(infile);
	infile.close();
	system("pause");
	return 0;
}

=====================================================================

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值