1.9 while循环50到100的整数相加

#include<iostream>
using namespace std;
int main() {
	int i = 50;
	int sum = 0;
	while (i <= 100) {
		sum += i;
		i++;
	}
	cout << sum << endl;
	return 0;
}

1.10 --实现递减顺序打印10到0的整数

#include<iostream>
using namespace std;
int main() {
	int i = 10;
	while (i >= 0) {
		cout << i << endl;
		i--;
	}
	return 0;
}

1.11 输入两个整数,输出范围内所有整数

#include<iostream>
using namespace std;

int main() {
	int a, b;
	cout << "请输入两个整数:" << endl;
	cin >> a >> b;
	int left =  a <= b ? a : b;
	int right = a > b ? a : b;
	while (right >= left) {
		cout << right << " ";
		right--;
	}
	return 0;

}

1.16 cin读取一组数,输出和

数量不定的输入数据。

#include<iostream>
using namespace std;
int main() {
	int a;
	int sum = 0;
	while (cin >> a) {
		sum += a;
	}
	cout << sum;
	return 0;
}

2.18 更改指针的值以及指针所指对象的值

#include<iostream>
using namespace std;
int main() {
	int *p;
	int a = 10;
	p = &a; //指针可以重新指向,引用不行
	cout << p << " " << *p << endl; //0000003D391EFBD4 10
	*p = 20;
	cout << p << " " << *p << endl; //0000003D391EFBD4 20
	return 0;
}

2.35 auto类型

#include <iostream>
#include<typeinfo>
using namespace std;

int main() {
	const int i = 42;
	auto j = i;
	const auto &k = i;
	auto *p = &i;
	const auto j2 = i, &k2 = i;
	cout << typeid(i).name() << endl;
	cout << typeid(j).name() << endl;
	cout << typeid(k).name() << endl;
	cout << typeid(p).name() << endl;
	cout << typeid(j2).name() << endl;
	cout << typeid(k2).name() << endl;
	return 0;

}

3.6替换为X & 3.10 去掉逗号

#include<iostream>
#include<vector>
using namespace std;

void test_3_6(string& s) {
	for (char& item : s) {
		item = 'X';
	}
}

void test_3_10(string& s) {
	string res;
	for (char &item : s) {
		if (item != '.') {
			res += item;
		}
	}
	s = res;
}

int main() {
	string s;
	cin >> s;
	test_3_10(s);
	cout << s;
	vector<string>(3, "4");
	return 0;
}

3.17 读若干string转大写

注意Cin会自动忽略空格,TAB,回车,并以此为分割。

#include "test.h"
#include<iostream>
#include<vector>
using namespace std;
int main() {
	vector<string> res;
	string s;
	while (cin >> s) {
		res.push_back(s);
	}
	for (string& item : res) {
		for (char& item2 : item) {
			//item2 = toupper(item2);
			item2 -= 32;// 'a'97 -> 'A'65
		}
	}
	for (string item : res) {
		cout << item << endl;
	}
	return 0;
}

3.20 vector双向头尾向中间输出

#include "test.h"
#include<iostream>
#include<vector>
using namespace std;
int main() {
	vector<int> res;
	int a;
	while (cin >> a) {
		res.push_back(a);
		cout << a << endl;
	}
	for (int i = 0, j = res.size() - 1; i <= j; i++, j--) {
		if (i == j) cout << res[i];
		else
			cout << res[i] + res[j] << endl;
	}
	return 0;
}

3.23 vector元素翻倍(使用迭代器)

#include "test.h"
#include<iostream>
#include<vector>
using namespace std;
int main() {
	vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
	for (auto item = v.begin(); item != v.end(); item++) {
		*item *= 2;
		cout << *item << endl;
	}
	return 0;
}

3.26 首尾相加迭代器版

注意end是指向尾后,for循环如果包括中间情况,就要对size偶数单独处理,相反就对奇数单独处理(练习书是这么做的),或者像3.20相遇也可以。

#include "test.h"
#include<iostream>
#include<vector>
using namespace std;
int main() {
	vector<int> res;
	int a;
	while (cin >> a) {
		res.push_back(a);
	}
	for (int i = 0; i <= res.size() / 2; i++) {
		
		if (res.size() % 2 == 0 && i == res.size() / 2) break;
		else cout << *(res.cbegin() + i) + *(res.cend() - i - 1) << endl;
	}

	return 0;
}

3.25 划分分数段迭代器版

#include "test.h"
#include<iostream>
#include<vector>
using namespace std;
int main() {
	vector<int> score(11, 0);
	int grade;
	while (cin >> grade) {
		if (grade <= 100 && grade >= 0) {
			++*(score.begin() + (grade / 10));
		}
	}
	int digt = 0;
	for (auto item = score.begin(); item != score.end(); item++, digt++) {
		cout << digt << "~" << digt + 10 << endl;
		cout << *item << endl;
	}
}

3.40两个C 字符串拼接

有点坑,strlen不能定义数组维数,因为在运行时才能遍历字符获取长度,要不然new 动态分配要不然用sizeof。

#include "test.h"
#include<iostream>
#include<cstring>
//#include<string>
using namespace std;
int main() {
	char str1[] = "Welcome to";
	char str2[] = "C++ family";
	
	//char *result = new char[strlen(str1) + strlen(str2) - 1];//动态分配内存,运行时在堆上
	//char result[strlen(str1) + strlen(str2) - 1];//静态分配内存,编译时在栈上,不允许,strlen不能在编译时获得数组长度
	char result[sizeof(str1) + sizeof(str2) - 1];// - 1是为了留一个位置给'\0'
	cout << sizeof(result)<< endl;//21
	cout << strlen(str1)<< endl; //10
	cout << strlen(str2) << endl; //10 strelen不算'\0',只计算有用的字符串
	cout << sizeof(str1)<< endl; //11, sizeof计算'\0',是整个类型的容量
	cout << sizeof(str2) << endl; //11

	strcpy(result, str1);
	cout << sizeof(result)<< endl;//21 注意类型的容量不变
	cout<< strlen(result) << endl;//10 字符串内容变了
	strcat(result, str2);

	cout<< strlen(result) << endl;//20

	cout << "str1:" << str1 << endl; //str1:Welcome to
	cout << "str2:" << str2 << endl; //str2:C++ family
	cout << result; //Welcome toC++ family
	return 0;
}

5.9 统计输入流里的元音字母

#include "test.h"
#include<iostream>
using namespace std;
int main() {
	char a;
	int cnt = 0;
	while (cin >> a) {
		switch (a) {
			
		case 'a':
		case 'e':
		case 'i':
		case 'o':
		case 'u':
			cnt++;
			cout << a;
			break;
		}
	}
	cout << cnt << endl;
	return 0;
}

5.24 异常处理

#include "test.h"
#include<iostream>
using namespace std;
int main() {
	int a = 0;
	int b = 0;
	try {
		if (b == 0)
			throw runtime_error("除数不能为0");
		cout << a / b;
	} catch (runtime_error err) {
		cout << err.what();
	}

	return 0;
}

6.7 局部静态变量(对象)

#include "test.h"
#include<iostream>
using namespace std;
int doit() {
	static int cnt = 0;
	return cnt++;
}

int main() {
	cout << doit() << endl;
	cout << doit() << endl;
	cout << doit() << endl;
	return 0;
}

6.25 带参main函数

#include<iostream>
using namespace std;
int main(int argc, char** argv) {
	string str;
	for (int i = 0; i != argc; ++i)
		cout << argv[i] << endl;

	cout << argc;
	return 0;
}

7.4 Person类构建

使用头文件进行类声明,源文件进行类外成员函数定义的方式,有点麻烦(看不见成员变量),但是规范。

//Person.h
#ifndef TEST %头文件保护符 预处理变量
#define TEST
#include<string>
using namespace std;
class Person {
private:
	string name;
	string address;
public:
	string getName() const;
	string getAddress() const;
};
#endif
//7_4.cpp
#include "Person.h"
#include<iostream>
//using namespace std; 头文件里using过了
string Person::getAddress() const {
	return address;
}

string Person::getName() const {
	return name;
}

7.9 Person友元函数

声明放在类声明的头文件里,定义还是在cpp里,记得加个友元,因为要访问私有变量。

//Person.h
friend	istream& read(istream& is, Person& per);
//7_4.cpp
istream& read(istream& is, Person& per) {
	is >> per.name >> per.address;
	return is;
}

7.15 Person的构造函数

四种常见的构造函数(都是类内定义)

//Person.h
public:
	Person() = default; //等同于Person(){},是类内定义
	Person(const string &name, const string &address) : name(name), address(address) {}//成员初值列表,和下一种效果等价,但效率更高
	Person(const string& name, const string& address) { //可以这么写,但尽量避免,且有时候必须使用初始化比如引用和常量,那只能用成员初值列表
		this->name = name;
		this->address = address;
	}
	Person(istream& is) {//包了cin的操作
		read(is, *this);
	}

7.23 Screen类构造

类的定义放在头文件里(一般用不着类的声明,没啥意义),Screen要根据长和高计算默认填入的空字符数量,用了string的(n,char)直接初始化方式。

//Screen.h
#ifndef TEST
#define TEST
#include<string>
using namespace std;
class Screen {
public:
	Screen() = default;
	Screen(unsigned int width, unsigned int height) {
		width = width;
		height = height;
		contents = string(height * width, ' ');
	}
	Screen(unsigned int width, unsigned int height, char c) //尽量用成员初值列,注意这里调用了string的(n,char)初始化方法
		: height(height), width(width),contents(height * width, c) { }
private:
	unsigned int height = 0;
	unsigned int width = 0;
	unsigned int cursor = 0;
	string contents;
};
#endif

7.27 Screen类添加move,set,display函数,重点在返回对象引用连续调用

形如screen.move().set().display(),因为每个函数实际上只做操作,返回值是对象引用,是个左值,可以继续调用成员函数。

//Screen.h
#ifndef TEST
#define TEST
#include<string>
using namespace std;
class Screen {
public:
    Screen() = default;
    //unsigned int 等价于 unsigned
    Screen(unsigned int width, unsigned int height) {
        width = width;
        height = height;
        contents = string(height * width, ' ');
    }
    Screen(unsigned width, unsigned height, char c) //尽量用成员初值列,注意这里调用了string的(n,char)初始化方法
        : height(height), width(width),contents(height * width, c) { }

    Screen& move(unsigned r, unsigned c) {//返回Screen对象引用是为了连调a.b.c
        cursor = r * width + c;
        return *this;
    }
    Screen& set(char ch) {
        contents[cursor] = ch;
        return *this;
    }
    Screen& set(unsigned r, unsigned c, char ch) {
        contents[r * width + c] = ch;
        return *this;
    }
    Screen& display(ostream& cout) {//实际上cout不传也可以,输出流一般也就默认只用cout这一种
        cout << contents;
        return *this;
    }
private:
    unsigned int height = 0;
    unsigned int width = 0;
    unsigned int cursor = 0;
    string contents;
};
#endif

//7_23.cpp
#include "Screen.h"
#include<iostream>
#include<typeinfo>
int main() {

	Screen myScreen(5, 5, 'X'); //5*5都是X的string,实际上是按一维存的
	myScreen.move(4, 0).set('#').display(cout);//将光标移动到五行一列,设置成#
	cout << "\n";
	myScreen.display(cout);//输出
	cout << "\n";
	unsigned a;
	cout << typeid(a).name() << endl;
	return 0;
}

7.27 Screen类添加其他类的成员函数作为友元

注意组织程序的结构,先定义Window_mgr类,并声明clear函数(不能定义,真正的定义要放在拿到Screen的权限之后,否则重定义),然后定义Screen,进行clear的友元声明赋予权限,最后再对clear进行定义添加实际的功能。

class Window_mgr {
public :
    void clear();
};
class Screen {
    friend void  Window_mgr:: clear();
public:
 //Screen的若干构造函数,省略
private:
    unsigned int height = 0;
    unsigned int width = 0;
    unsigned int cursor = 0;
    string contents;
};

void Window_mgr::clear() {
    Screen myScreen(10, 20, 'X');
    cout << "清理之前myScreen内容是:" << endl;
    cout << myScreen.contents << endl;
    myScreen.contents = "";
    cout << "清理之后myScreen内容是:" << endl;
    cout <<myScreen.contents << endl;
}

7.41委托构造函数

//Person.h
#ifndef TEST %头文件保护符 预处理变量
#define TEST
#include<string>
#include<iostream>
using namespace std;
class Person {
friend	istream& read(istream& is, Person& per);
private:
	string name = "";
	string address = "";
	int id = 0;
public:
	string getName() const;
	string getAddress() const;

	//构造函数
	//Person() = default; //等同于Person(){},是类内定义
	Person(const string &name, const string &address, const int id) : name(name), address(address), id(id) {cout << "成员初值列构造" << endl; }//成员初值列表,和下一种等价
	//委托构造函数
	Person(const string& name, const string& address) : Person(name, address, 0) {
		cout << "委托构造函数,name,address双参数" << endl;
	}

	//Person(const string& name) : Person(name, 0, 0) { //委托构造函数不能作为重载依据,参数列表一致
	//	cout << "委托构造函数,name单参数" << endl;
	//}

	Person(const string& name) : Person(name, "") {
		cout << "委托构造函数,name单参数" << endl;
	}

	Person() : Person("", "", 0) {
		cout << "委托构造函数,无参数" << endl;
	}

	Person(istream& is) {//包了输入的操作
		read(is, *this);
	}
};

//源Cpp文件
#include "Person.h"
#include<iostream>
//using namespace std; 头文件里using过了
string Person::getAddress() const {
	return address;
}

string Person::getName() const {
	return name;
}

istream& read(istream& is, Person& per) {
	is >> per.name >> per.address;
	return is;
}

int main() {
	Person p1; //先输出(执行)成员初值列,再输出委托构造函数,无参数
	cout << endl;
	Person p2("a");//成员初值列构造->委托构造函数,name,address双参数->委托构造函数,name单参数(委托顺序是反过来的)
	cout << endl;
	Person p3("a", "b"); //成员初值列构造->委托构造函数,name,address双参数
	cout << endl;
	Person p4("a", "b", 1); //成员初值列构造
	
	return 0;
}

#endif

注意委托构造函数可以互相调用,本身函数体里也可以有东西,委托省略的是初始化成员值的内容,先执行受委托构造函数的函数体

alt

12.2 共享一个vector<string>的类StrBlob,使用shared_ptr

//StrBlob.h
#ifndef TEST
#define TEST
#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> list);
	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<vector<string>> data;
	void check(size_type i, const string& msg) const;
};

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

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

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

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

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

//Test.cpp
#include "StrBlob.h"
#include<iostream>

int main() {
	StrBlob b1;
	StrBlob b2 = {"a", "an", "the"};
	b1 = b2;
	b1.push_back("test");
	cout << b1.size() << " " << b2.size() << endl;
	return 0;
}

12.6动态分配Vector

使用普通指针/智能指针,主要区别就在不需要在Main里面手动释放指针并置空了。

#include<iostream>
#include<vector>
using namespace std;

//void read(vector<int>* pv) {
//	int a;
//	while (cin >> a) {
//		pv->push_back(a);
//	}
//}
//void print(vector<int>* pv) {
//	for (const auto v : *pv) {
//		cout << v << " ";
//	}
//	cout << endl;
//}

void read(shared_ptr<vector<int>> pv) {
	int a;
	while (cin >> a) {
		pv->push_back(a);
	}
}
void print(shared_ptr<vector<int>> pv) {
	for (const auto v : *pv) {
		cout << v << " ";
	}
	cout << endl;
}
int main() {
	//vector<int>* pv = new (nothrow) vector<int>;
	shared_ptr<vector<int>> pv = make_shared<vector<int>>();
	if (pv == nullptr) {
		return -1;
	}
	read(pv);
	print(pv);
	//delete pv;
	//pv = nullptr;

	return 0;
}

12.14 自定义shared_ptr释放操作

使用disconnect替换默认的delete,注意使用直接初始化的方式。

#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);
//}
void f(destination& d) {
	cout << "用shared_ptr管理connect" << endl;
	connection c = connect(d);
	shared_ptr<connection> p1(&c, [](connection *p1) {disconnect(*p1); });
	cout << endl;
}

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

12.19 12.19 weak_ptr伴随指针类

用StrBlob中shared_ptr 创建,增添vector,实现资源自动释放,利用StrBlobPtr中weak_ptr lock会检查shared_ptr个数是否为0,来保证查询时vector一定存在,不存在则报错。注意在实现weak_ptr递增递减的时候,递减要先cur--,来区分check(0)处和递增的边界越界条件。

//StrBlobTest.h
#ifndef TEST
#define TEST
#include<vector>
#include<string>
#include<initializer_list>
#include<memory>
#include<stdexcept>

using namespace std;
class StrBlobPtr;
class StrBlob {
	friend class StrBlobPtr;
public:
	typedef vector<string>::size_type size_type;
	StrBlob();
	StrBlob(initializer_list<string> list);
	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;

	//提供给StrBlobPtr的接口
	StrBlobPtr begin();
	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> list): data(make_shared<vector<string>>(list)) { }
void StrBlob::check(size_type i, const string& msg) const {
	if (i >= data->size()) {
		throw out_of_range(msg);
	}
}

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

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

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

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

//StrBlobPtr用于访问StrBlob所存元素
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:
	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 {
	auto ret = wptr.lock();
	if (!ret)
		throw runtime_error("unbound StrBlobPtr");
	if (i >= ret->size() || i < 0) {
		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 StrBlobPtr");
	++curr;
	return *this;
}
StrBlobPtr& StrBlobPtr::decr() {
	--curr;
	check(curr, "increment past end 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();
	if (l == r)
		return (!r || lhs.curr == rhs.curr);
	else
		return false;
}
bool neq(const StrBlobPtr& lhs, const StrBlobPtr& rhs) {
	return !eq(lhs, rhs);
}

#endif

//Test.cpp
#include<iostream>
#include<fstream>
#include"StrBlobTest.h"
using namespace std;
int main(int argc, char **argv) {
	ifstream in(argv[1]);
	if (!in) {
		cout << "无法打开输入文件" << endl;
		return -1;
	}
	StrBlob b;
	string s;
	while (getline(in, s))
		b.push_back(s);
	
	//string aa = "sadsadasd";
	//string bb = "sdat565";
	//string cc = "atw56521";
	
	//b.push_back(aa);
	//b.push_back(bb);
	//b.push_back(cc);

	for (auto it = b.begin(); neq(it, b.end()); it.incr()) {
		cout << it.deref() << endl;
	}
	return 0;
}


12.23使用动态数组存储char和string

主要还是char API的使用。

#include<iostream>
#include<cstring>
using namespace std;
int main() {
	char c1[] = "Hello"; //会先把Hello拷贝到栈区
	const char* c2 = "World";

	char *r = new char[strlen(c1) + strlen(c2) + 1];
	strcpy(r, c1);
	strcat(r, c2);
	cout << r << endl;

	string s1 = "hello";
	string s2 = "world";
	strcpy(r, (s1 + s2).c_str());
	cout << r << endl;

	delete[] r;
}

12.26用allocator分配空间构造string

注意使用allocator时内存分配和对象构造是分开的,对象销毁(析构)与内存释放也是分开的。

#include<iostream>
#include<memory>
using namespace std;
int main() {
	allocator<string> alloc;
	auto const p = alloc.allocate(100);//分配内存空间
	string s;
	string *q = p;
	while (cin >> s && q != p + 100) {
		alloc.construct(q++, s); //构造string
	}
	const size_t size = q - p;

	for (int i = 0; i < size; i++) {
		cout << p[i] << endl;
	}
	while (q != p) {
		alloc.destroy(--q);//每个元素都销毁
	}
	alloc.deallocate(p, 100);//释放内存空间
	return 0;
}

12.27文本查询

  • 书上例题版本通过shared_ptr实现TextQuery和QueryResult共享vector<string>和set数据,数据对象的生命周期同步。
  • 课后题进一步使用StrBlob类,通过weak_ptr确保数据对象资源存在

alt

例题

//QueryResult.h
#pragma once
#include<vector>
#include<string>
#include<iostream>
#include<map>
#include<set>
using namespace std;
class QueryResult {
	friend ostream& print(ostream&, const QueryResult &);
public:
	using line_no = vector<string>::size_type;
	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;
};

string make_plural(size_t ctr, const string& word , const string& ending)
{
	return ctr == 1 ? word : word + ending;
}

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 << ")" << *(qr.file->begin() + num) << endl;
	return os;
}
//TextQuery.h
#ifndef TEST
#define TEST
#include<vector>
#include<string>
#include<iostream>
#include<map>
#include<set>
#include<fstream> 
#include <sstream>    // string stream
using namespace std;
class QueryResult;
class TextQuery {
public:
	using line_no = vector<string>::size_type;
	TextQuery(ifstream&);
	QueryResult query(const string&) const;
private:
	shared_ptr<vector<string>>file;
	map<string,shared_ptr<set<line_no>>> wm;
};

TextQuery::TextQuery(ifstream& is) : file(new vector<string>) {
	string text;
	while (getline(is, text)) {
		file->push_back(text);
		int n = file->size() - 1;
		istringstream line(text);
		string word;
		while (line >> word) {
			auto &lines = wm[word];
			if(!lines)
				lines.reset(new set<line_no>);
			lines->insert(n);
		}
	}
}

QueryResult TextQuery::query(const string& sought) const {
	static shared_ptr<set<line_no>> nodata(new set<line_no>);
	auto loc = wm.find(sought);
	if (loc == wm.end())
		return QueryResult(sought, nodata, file);
	else
		return QueryResult(sought, loc->second, file);
}
#endif

//main.cpp
#include"TextQuery.h"
#include<cstdlib>
using namespace std;
void runQueries(ifstream& infile) {
	TextQuery tq(infile);
	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;
	}
}

int main(int argc, char **argv) {
	ifstream infile;
	if (argc < 2 || !(infile.open(argv[1]), infile)) {
		cerr << "No input file!" << endl;
		return EXIT_FAILURE;
	}
	runQueries(infile);
	return 0;
}

使用weak_ptr对vector_string进行资源保护

使用StrBlob保存vector<string>,StrBlobPtr访问StrBlob确保data存在,否则报错。 alt

  • 对于主程序完全不用变
  • StrBlobPtr进一步完善,添加接受const StrBlob的构造函数使得查询时确保不修改源数据。对应要在StrBlob中添加begin和end的const版本
  • 给StrBlobPtr添加dref解引用的可接受off偏移量的版本,用于print时指针加num指向对应的行
  • 由智能指针转为StrBlob类作为file的类型,所以->转为.
  • QueryResult中print对应改成上述提供的函数调用方式
//TextQuery.h
#ifndef TEST
#define TEST
#include<vector>
#include<string>
#include<iostream>
#include<map>
#include<set>
#include<fstream> 
#include<sstream>    // string stream
#include"QueryResult.h"
using namespace std;
class QueryResult;
class TextQuery {
public:
	using line_no = vector<string>::size_type;
	TextQuery(ifstream&);
	QueryResult query(const string&) const;
private:
	StrBlob file;
	map<string,shared_ptr<set<line_no>>> wm;
};

TextQuery::TextQuery(ifstream& is) : file(new vector<string>) {
	string text;
	while (getline(is, text)) {
		file.push_back(text);
		int n = file.size() - 1;
		istringstream line(text);
		string word;
		while (line >> word) {
			auto &lines = wm[word];
			if(!lines)
				lines.reset(new set<line_no>);
			lines->insert(n);
		}
	}
}

QueryResult TextQuery::query(const string& sought) const {
	static shared_ptr<set<line_no>> nodata(new set<line_no>);
	auto loc = wm.find(sought);
	if (loc == wm.end())
		return QueryResult(sought, nodata, file);
	else
		return QueryResult(sought, loc->second, file);
}
#endif
//QueryResult.h
#pragma once
#include<vector>
#include<string>
#include<iostream>
#include<map>
#include<set>
#include"StrBlob.h"
using namespace std;
class QueryResult {
	friend ostream& print(ostream&, const QueryResult &);
public:
	using line_no = vector<string>::size_type;
	QueryResult(string s, shared_ptr<set<line_no>> p, StrBlob f): sought(s), lines(p), file(f){}
private:
	string sought;
	shared_ptr<set<line_no>> lines;
	StrBlob file;
};

string make_plural(size_t ctr, const string& word , const string& ending)
{
	return ctr == 1 ? word : word + ending;
}

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 << ")" << qr.file.begin().deref(num) << endl;
	return os;
}
//StrBlob.h
#ifndef TEST
#define TEST
#include<vector>
#include<string>
#include<initializer_list>
#include<memory>
#include<stdexcept>

using namespace std;
class StrBlobPtr;
class StrBlob {
	friend class StrBlobPtr;
public:
	typedef vector<string>::size_type size_type;
	StrBlob();
	StrBlob(initializer_list<string> list);
	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<vector<string>> data;
	void check(size_type i, const string& msg) const;

	//提供给StrBlobPtr的接口
	StrBlobPtr begin();
	StrBlobPtr end();
	StrBlobPtr begin() const;
	StrBlobPtr end() const;
};

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

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

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

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

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

//StrBlobPtr用于访问StrBlob所存元素
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:
	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 {
	auto ret = wptr.lock();
	if (!ret)
		throw runtime_error("unbound StrBlobPtr");
	if (i >= ret->size() || i <= 0) {
		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 StrBlobPtr");
	++curr;
	return *this;
}
StrBlobPtr& StrBlobPtr::decr() {
	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;
}

bool eq(const StrBlobPtr& lhs, const StrBlobPtr& rhs) {
	auto l = lhs.wptr.lock(), r= rhs.wptr.lock();
	if (l == r)
		return (!r || lhs.curr == rhs.curr);
	else
		return false;
}
bool neq(const StrBlobPtr& lhs, const StrBlobPtr& rhs) {
	return !eq(lhs, rhs);
}

#endif

13.5拷贝构造函数深拷贝具有指针的对象

注意.的优先级比*高,()可加可不加。

#include<iostream>
using namespace std;

class HasPtr {
public:
	HasPtr(const string& s = string()):ps(new string(s)),i(0){ }
	HasPtr(const HasPtr& a) {
		ps = new string(*(a.ps));
		i = a.i;
	}
private:
	string* ps;
	int i;
};