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
注意委托构造函数可以互相调用,本身函数体里也可以有东西,委托省略的是初始化成员值的内容,先执行受委托构造函数的函数体
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确保数据对象资源存在
例题
//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存在,否则报错。
- 对于主程序完全不用变
- 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;
};