头文件
最安全的分配和使用动态内存的方法是调用一个名为make_shared的标准库函数。
auto p = make_shared<int>(42);
auto q(p);
使用动态内存出于以下三种原因:
class StrBlob{
public:
typedef std::vector<std::string>::size_type size_type;
//构造函数
StrBlob();
StrBlob(std::initializer_list<std::string>il);
//
size_type size() const {return data->size();}
bool empty() const {return data->empty();}
//增删
void push_back(const std::string &t){data->push_back(t);}
void pop_back();
std::string& front();
std::string& back();
private:
std::shared_ptr<std::vector<std::string>>data;
void check(size_type i,const std::string &msg) const
{
if(i>data->size())
throw out_of_range(msg);
}
};
StrBlob::StrBlob():data(make_shared<std::vector<std::string>>()){}
StrBlob::StrBlob(std::initializer_list<std::string>il):data(make_shared<std::vector<std::string>>(il)){}
std::string& StrBlob::front()
{
check(0,"front on empty StrBlob");
return data->front();
}
std::string& StrBlob::back()
{
check(0,"fornt on empty StrBlob");
return data->back();
}
int *pi=new int;
int *pi=new int(1024);
auto p1=new auto(obj);
const int *pci = new const int(1024);
//动态分配的const对象必须初始化
int *p1=new int;//如果分配失败,new会抛出一个std::bad_alloc
int *p2=new (nothrow) int;//如果分配失败,new返回一个空指针
带参数的new成为定位new
使用动态内存的管理非常容易出错
- 忘记delete内存(内存泄漏)
- 使用已经释放掉的对象。通过在释放内存后将指针置为空,可以检测出这种错误
- 同一块内存释放两次
delete指针之后指针仍然保存了动态内存的地址,成为空悬指针。
delete指针之后记得置为nullptr
接收指针参数的构造函数为explicit,所以不能进行隐式转换,必须直接初始化形式来初始化一个智能指针
shared_ptr<int>p2(new int(42));//p2指向一个值为42的int
shared_ptr<int>p2=new int(42);//错误:必须使用直接初始化形式
普通指针指向的对象交由智能指针后,智能指针一旦将对象释放,那么普通指针便变为了空悬指针
如果使用智能指针,即使程序块过早结束,智能指针类也能确保在内存不再需要的时候将其释放
没有自己的析构函数,使用shared_ptr管理
默认情况下,shared_ptr假定它们指向的是动态内存。因此当一个shared_ptr被销毁的时候,他默认地对它管理的指针进行delete操作。
有时候我们希望调用自己定义的函数来代替delete。
void end_connection(connection *p){disconnect(*p);}
void f(destination &d)
{
connection c=connect(&d);
shared_ptr<connection>p(&c,end_connection);
//一旦发生异常交由智能指针自己调用end_connection进行释放
}
智能指针的一些陷阱
- 不适用相同的内置指针初始化或者reset多个智能指针
- 不delete get()返回的指针
- 不适用get()初始化或者reset另一个指针指针,和1类似
- 如果你使用了get()返回的指针,当智能指针计数为0销毁后,你使用的指针就变为空悬指针了
- 如果你使用智能指针管理的资源不是new分配的内存,那么记得自己定义删除器
初始化unique_ptr必须采用直接初始化形式
不支持普通的拷贝或赋值操作
unique_ptr<double>p1;
unique_ptr<int>p2(new int(42));
unique_ptr<string>p2(p1);
unique_ptr<string>p3;
p3=p2;
我们可以拷贝或赋值一个将要被销毁的unique_ptr
弱智能指针 讲一个weak_ptr绑到一个shared_ptr不会改变其引用计数
int *p=new int[get_size()];
typedef int arrT[42];
int *p = new arrT;
int *pia=new int[10];//10个未初始化的int
int *pia2=new int[10]();//10个0
string *psa=new string[10];//10个空的string
string *pas2=new string[10]();//10个空的string
可以用花括号进行初始化列表,缺补默认值 多报异常
char arr[0];//不合法
char *cp = new char[0];//合法
delete p;
delete [] pa;
unique_ptr<int[]>up(new int[10]);
up.release();//自动调用delete[]
管理动态数组的unique_ptr不能用.或者->,直接用下标访问
shared_ptr管理动态数组的话必须提供一个删除器,调用delete[]
shared_ptr<int>sp(new int[10],[](int *p){delete [] p;})
sp.reset();
需要完成的任务
实现的工具
在类之间共享数据
- ostream对象的用法
- !(std::cin>>s)遇到文件尾部返回false
- ifstream读入文件用法
#include "TextQuery.hpp"
#include "QueryResult.hpp"
#include
#include
using namespace std;
/*
* @function make_plural 根据输入的个数返回相应的string
*
* @param ctr 需要判断的个数
* @param word “word”
* @param word “s”
*
* */
string make_plural(size_t ctr,const string &word,const string &ending)
{
return (ctr > 1)? word+ending : word;
}
/*
* @function print 打印输出的结果
*
* @param os 输出流对象
* @param qr 包含结果的QueryResult类对象
* */
ostream &print(ostream &os ,const QueryResult &qr)
{
os<<qr.sought<<" occurs "<<qr.lines->size()<<" "<<make_plural(qr.lines->size(),"time","s")<<std::endl;
for(auto num : *qr.lines)
os << "\t(line "<< num + 1 <<")"<< *(qr.file->begin() + num)<<endl;
return os;
}
/*
* @function runQueries 处理文件与用户的输入并输出结果
*
* @param infile 需要处理的文件
* */
void runQueries(ifstream &infile)
{
//infile是需要处理的文件
TextQuery tq(infile);//保存文件并且建立查询set
//与用户交互:提示用户输入要查询的单词,完成拆查询并打印结果
while(true)
{
std::cout<<"enter word to look for , or q to quit:";
std::string s;
//遇到文件尾部或者用户输入q时循环中止
if(!(std::cin>>s) || s == "q")break;
print(std::cout,tq.query(s))<<endl;
}
}
int main()
{
ifstream test;
test.open("./test.txt");
runQueries(test);
return 0;
}
#ifndef TEXTQUERY_HPP
#define TEXTQUERY_HPP
#include
#include
#include
#include
#include
#include
#include
#include "QueryResult.hpp"
/*
* @class TextQuery
* @brief 处理输入的文本文件,得到结果放入QueryResult类对象中
*
* @param fiel 保存读入的文件
* @param wn 保存单词和对应的行号set
*
* @function query 接受string,返回结果
* */
class QueryResult;//返回类型,需要先定义
class TextQuery
{
public:
using line_no=std::vector<std::string>::size_type;//便于阅读,取别名
TextQuery(std::ifstream&);//构造函数
QueryResult query(const std::string&)const;//接受string,返回结果
private:
std::shared_ptr<std::vector<std::string>>file;//保存读如的文件
std::map<std::string,std::shared_ptr<std::set<line_no>>>wn;//保存单词和对应的行号set
};
#endif
TextQuery.cpo
- istringstream的使用
- map下标操作返回对应的元素
- 设置一个静态的set指针 ,用来给每次没有找到sought用作返回值
- 当map用来查找元素的时候使用find而不是下标,避免将不存在的关键字插入map中
#include "TextQuery.hpp"
#include
TextQuery::TextQuery(std::ifstream &is):file(new std::vector<std::string>)
{
std::string text;//暂存一行
while(getline(is,text))//循环获取一行
{
file->push_back(text);//将一行加入file中
int n=file->size()-1;//获取当前行的行号
std::istringstream line(text);//创建istringstream对象,将text分解为单词
std::string word;//暂存单词
while(line>>word)//读取单词
{
auto &lines = wn[word];//map返回一个shared_ptr>
if(!lines)//如果为空,说明单词第一次出现,创建一个新的set
lines.reset(new std::set<line_no>);
lines->insert(n);//将当前行号插入set
}
}
}
QueryResult TextQuery::query(const std::string &sought)const
{
//如果没有找到sought,我们将返回一个指向此set的指针
static std::shared_ptr<std::set<line_no>>nodata(new std::set<line_no>);
//使用find而不是下标运算符来查找查找单词,避免将单词添加到wm中
auto loc=wn.find(sought);
if(loc == wn.end())
return QueryResult(sought,nodata,file);
else
return QueryResult(sought,loc->second,file);
}
#ifndef QUERYRESULT_HPP
#define QUERYRESULT_HPP
#include
#include
#include
#include
/*
* @class QueryResult 接受TextQuery类中query函数返回的结果
*
* @param sought 需要查询的单词
* @param lines 单词出现的行号集合set
* @param file 输入的文件
* */
class QueryResult
{
friend std::ostream& print(std::ostream &os, const QueryResult&qr);
using line_no = std::vector<std::string>::size_type;
public:
QueryResult(std::string s,std::shared_ptr<std::set<line_no>> p,std::shared_ptr<std::vector<std::string>>f):sought(s),lines(p),file(f){}
private:
std::string sought;//查询单词
std::shared_ptr<std::set<line_no>>lines;//出现的行号
std::shared_ptr<std::vector<std::string>>file;//输入文件
};
#endif