C++ primer 5th 第12章 动态内存
=====================================================================
第12章 动态内存 400页 shared_ptr
=====================================================================
//QQ108201645编写
#include
#include
#include
#include //智能指针所在头文件
using namespace std;
int main()
{
//类似vector,智能指针也是模板
shared_ptr p1;//shared_ptr,可以指向string,如果加上=make_shared(),则p1不为空
shared_ptr> 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的对象 |
unique_ptr |
|
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 |
返回一个shared_ptr,指向一个动态分配的类型为T的对象。使用args初始化此对象 |
make_shared |
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
#include
#include
#include
#include
using namespace std;
int main()
{
//指向一个值为42的int的shared_ptr
shared_ptr p3 = make_shared(42);
cout << *p3 << endl;
//p4指向一个值为"9999999999"的string
shared_ptr p4 = make_shared(10,'9');
cout << *p4 << endl;
//p5指向一个值初始化的(参见3.3.1节,第88页)int,即,值为0
shared_ptr p5 = make_shared();
cout << *p5 << endl;
//p6指向一个动态分配的空vector
shared_ptr> p6 = make_shared>();
cout << p6 << endl;
auto p = make_shared(42);//p指向的对象只有p一个引用者
auto q(p);//p和q指向相同的对象,此对象有两个引用者
auto r = make_shared(42);//r指向的int 只有一个引用者
r = q;//给r赋值,令它指向另一个地址
//递增q指向的对象的引用计数
//递减r原来指向的对象的引用计数
//r原来指向的对象已没有引用者,会自动释放
system("pause");
return 0;
}
=====================================================================
第12章 动态内存 401页 make_shared函数
=====================================================================
//QQ108201645编写
#include
#include
#include
#include
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 factory(T arg)
{
// 恰当的处理arg
// shared_ptr 负责释放内存
return make_shared(arg);
}
void use_factory(T arg)
{
shared_ptr p = factory(arg);
//return p;
}
int main()
{
T arg;
while (cin >> arg)
use_factory(arg);
}
=====================================================================
第12章 动态内存 404页 定义StrBlob类(包含练习题)
=====================================================================
//QQ108201645编写
#include
#include
#include
#include
#include
using namespace std;
//定义StrBlob类
class StrBlob
{
public:
typedef vector::size_type size_type;
StrBlob();
StrBlob(initializer_list 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>data;
void check(size_type i, const string& msg)const;
};
StrBlob::StrBlob():data(make_shared>()){}
StrBlob::StrBlob(initializer_list i1)
:data(make_shared>(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
#include
#include
#include
#include
using namespace std;
/*练习12.2:编写你自己的StrBlob 类,包含const 版本的 front 和 back。*/
class StrBlob
{
public:
using size_type = vector::size_type;
StrBlob():data(make_shared>()){}
StrBlob(initializer_listi1);
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> data;
};
StrBlob::StrBlob(initializer_list i1)
:data(make_shared>(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
#include
#include
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* pv = new vector{ 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
#include
#include
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
#include
#include
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
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
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
#include
using namespace std;
vector* new_vector(void)
{
return new (nothrow) vector;
}
void read_ints(vector* pv)
{
int v[] = { 1,2,3,4,5 };
pv->insert(pv->begin(),begin(v), end(v));
}
void print_ints(vector* pv)
{
for (auto &v : *pv)
cout << v << " ";
cout << endl;
}
int main()
{
/*练习12.6:编写函数,返回一个动态分配的 int 的vector。将此vector传递给另
一个函数,这个函数读取标准输入,将读入的值保存在 vector 元素中。再将vector
传递给另一个函数,打印读入的值。记得在恰当的时刻delete vector。*/
vector* pv = new_vector();
if (!pv)
{
cerr << "内存不足" << endl;
return -1;
}
read_ints(pv);
print_ints(pv);//输出
delete pv;
pv = nullptr;
return 0;
}
=====================================================================
//QQ108201645编写
#include
#include
using namespace std;
shared_ptr> new_vector(void)
{
return make_shared>();
}
void read_shared(shared_ptr>& spv)
{
int v[] = { 5,6,7,8,9 };
spv->insert(spv->begin(), begin(v), end(v));
}
void print_ints(shared_ptr>& spv)
{
for (auto &s : *spv)
cout << s << " ";
cout << endl;
}
int main()
{
/*练习12.7:重做上一题12.6,这次使用 shared_ptr 而不是内置指针。*/
shared_ptr> 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(42), r2 = make_shared(100);
r2 = q2;
q赋值给r,r所指的内存没有释放导致内存泄露。q2赋值给r2,r2的引用计数减1后为0,自动销毁。
*/
=====================================================================
第12章 动态内存 412页 shared_ptr和new结合使用
=====================================================================
//QQ108201645编写
#include
#include
using namespace std;
shared_ptr clone(int p)
{
//return new int(p);//错误:隐式转换为shared_ptr
return shared_ptr(new int(p));//正确:显式地用int*创建shared_ptr
}
int main()
{
//shared_ptr p1 = new int(1024);//错误:必须使用直接初始化形式
shared_ptr p2(new int(1024));//正确:使用了直接初始化形式
shared_ptr p3 = make_shared(5);
cout << *p3 << endl;
return 0;
}
=====================================================================
表12.3:定义和改变shared_ptr的其他方法 |
|
shared_ptr |
p管理内置指针q所指向的对象;q必须指向new分配的内存,且能够转换为T*类型 |
shared_ptr |
p从unique_ptr u那里接管了对象的所有权;将u置为空 |
shared_ptr |
p接管了内置指针q所指向的对象的所有权。q必须能转换为T*类型(参见4.11.2节,第143页)。p将使用可调用对象d(参见10.3.2节,第346页)来代替delete |
shared_ptr |
如表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
using namespace std;
void process(shared_ptr ptr)
{
cout << ptr.use_count();//输出引用计数
//使用ptr
}//ptr离开作用域,被销毁
int main()
{
shared_ptr 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
process(shared_ptr(x));//合法的的,但内存会被释放
int j = *x;//未定义的:x是一个空悬指针!
return 0;
}
=====================================================================
第12章 动态内存 413页 也不要使用get初始化另一个智能指针或为智能指针初始化
=====================================================================
//QQ108201645编写
#include
#include
using namespace std;
int main()
{
shared_ptr p(new int(42));//引用计数为1
int *q = p.get();//正确:但使用q时要注意,不要让它管理的指针被释放
{
//新程序块
//未定义:两个独立的shared_ptr指向相同的内存
shared_ptr(q);
}//程序块结束,q被销毁,它指向的内存被释放
int foo = *p;//未定义:p指向的内存已经被释放
//p = new int(1024);//错误:不能将一个指针赋予shared_ptr
p.reset(new int(1024));//正确:p指向一个新对象
string newVal = " world";
shared_ptr 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
using namespace std;
void process(shared_ptr ptr)
{
}
int main()
{
/*练习12.10:下面的代码调用了第413页中定义的process 函数,解释此调用是否正
确。如果不正确,应如何修改?
shared_ptr p(new int(42));
process(shared_ptr(p));
*/
/*正确的。利用p创建一个临时的shared_ptr赋予process的参数
ptr,p和ptr都指向相同的int对象,引用计数被正确的置为2.process执行
完毕后,ptr被销毁,引用计数减1,这是正确的--只有p指向它
*/
/*
练习12.11:如果我们像下面这样调用 process,会发生什么?
process(shared_ptr(p.get()));
将会导致智能指针所指的内存被释放2次。
*/
shared_ptr p(new int(42));
process(shared_ptr(p));
//process(shared_ptr(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();
(a) process(sp);
(b) process(new int());
(c) process(p);
(d) process(shared_ptr(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
(d)合法,但是 是错误的程序,p是一个指向int对象的普通指针,被用来创建一个临时shared_ptr。传递给process
的参数ptr,引用计数为1.当process执行完毕,ptr被销毁,引用计数变为0,int被销毁,p变为空悬指针
*/
/*
练习12.13:如果执行下面的代码,会发生什么?
auto sp = make_shared();
auto p = sp.get();
delete p;
第二行用get获取sp指向的int对象的地址,第三行用delete释放这个地址。意味着sp的引用计数仍为1,但其
指向的int对象已经被释放了。sp成为类似空悬指针的shared_ptr。
*/
}
=====================================================================
第12章 动态内存 415页 智能指针与异常
=====================================================================
//QQ108201645编写
#include
using namespace std;
void f()
{
shared_ptr 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 p;
shared_ptr p1;
{
shared_ptr p = make_shared(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 p(&c, end_connection);
//使用连接
//当f退出时(即使是由于异常而退出),connection会被关闭
}
不使用相同的内置指针值初始化(或reset)多个智能指针。
不delete get() 返回的指针。
不使用get() 初始化或reset另一个智能指针。
如果你使用get() 返回的指针,记住当最后一个对应的智能指针销毁后,你的
指针就变为无效了。 空悬指针
如果你使用智能指针管理的资源不是new分配的内存,记住传递给它一个删除
器。
#endif
=====================================================================
第12章 动态内存 417页 练习题
=====================================================================
//QQ108201645编写
/*练习12.14:编写你自己版本的用 shared_ptr 管理 connection 的函数。*/
#include
#include
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_ptrp(&c, end_connection);
//忘记调用disconnect关闭连接
cout << endl;
}
int main()
{
destination d;
f(d);
f1(d);
return 0;
}
=====================================================================
//QQ108201645编写
#include
#include
#include
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 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 p(&conn, end_connection);
cout << "马上连接(" << p.use_count() << ")" << endl;
}
int main()
{
destination dest("198.161.1.1", 80);
f(dest);
}
=====================================================================
//QQ108201645编写
#include
#include
#include
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_ptrpConn(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 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
#include
#include
using namespace std;
int main()
{
unique_ptr p1;//可以指向一个double的uunique_ptr
unique_ptrp2(new int(42));//p2指向一个值为42的int
unique_ptrp3(new string("Stegosaurus"));
//unique_ptr p4(p3);//错误,unique_ptr不支持拷贝
unique_ptr p5;
//p5 = p3;//错误,unique_ptr不支持赋值
}
=====================================================================
表12.4:unique_ptr操作(另见表12.1,第401页) |
|
unique_ptr |
空unique_ptr,可以指向类型为T的对象。u1会使用delete来释放它的指针;u2会使用一个类型为D的可调用对象来释放它的指针 |
unique_ptr |
|
unique_ptr |
空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
#include
#include
using namespace std;
int main()
{
unique_ptrp1(new string("hello"));
unique_ptrp2(p1.release());//release将p1置为空
cout << *p2 << endl;
unique_ptrp3(new string("Trex"));
//将所所有权从p3转移给p2
p2.reset(p3.release());//reset释放了p2原来指向的内存
cout<<*p2<
=====================================================================
第12章 动态内存 417页 传递unique_ptr参数和返回unique_ptr
=====================================================================
//QQ108201645编写
#include
#include
using namespace std;
unique_ptr clone(int p)
{
//正确:从int*创建一个unique_ptr
return unique_ptr(new int(p));
}
unique_ptr clone1(int p)
{
unique_ptrret(new int(p));
return ret;
}
int main()
{
unique_ptr p = clone(5);
unique_ptrp1 = clone1(6);
cout << *p << " " << *p1 << endl;
}
=====================================================================
第12章 动态内存 417页 向unique_ptr传递删除器
=====================================================================
//QQ108201645编写
#include
#include
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
p(&c, end_connection);
}
int main()
{
destination d;
f(d);
unique_ptr p(new connection, end_connection);
return 0;
}
=====================================================================
第12章 动态内存 419页 练习题
=====================================================================
//QQ108201645编写
/*练习12.16:如果你试图拷贝或赋值 unique_ptr,编译器并不总是能给出易于理解的
错误信息。编写包含这种错误的程序,观察编译器如何诊断这种错误。*/
#include
#include
using namespace std;
int main()
{
shared_ptr p1 = make_shared(5);
shared_ptr p2(p1);
shared_ptr p3 = p2;
unique_ptr p4(new int(15));
//unique_ptr p5(p4);//错误不支持拷贝
unique_ptr p5;
//p5 = p4;//不支持赋值
p5.reset(p4.release());
cout << *p5 << endl;
//unique_ptr p6(p5);
//unique_ptr p6 = p5;
/*
严重性 代码 说明 项目 文件 行 禁止显示状态
错误(活动) E1776 无法引用 函数 "std::unique_ptr<_Ty, _Dx>::unique_ptr(const std::unique_ptr<_Ty, _Dx> &) [其中 _Ty=int, _Dx=std::default_delete]" (已声明 所在行数: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>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)”: 尝试引用已删除的函数 01cpp c:\users\administrator\source\repos\01cpp\01cpp\main.cpp 20
*/
}
=====================================================================
//QQ108201645编写
#include
#include
using namespace std;
int main()
{
/*
练习12.17:下面的 unique_ptr 声明中,哪些是合法的,哪些可能导致后续的程序错
误?解释每个错误的问题在哪里。
int ix = 1024, *pi = &ix, *pi2 = new int(2048);
typedef unique_ptr 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 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 |
空weak_ptr,指向类型为T的对象 |
weak_ptr |
与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
#include
using namespace std;
int main()
{
auto p = make_shared(5);
weak_ptr wp(p);//wp弱指针共享p;p的引用计数未改变
cout << wp.use_count() << endl;//显示引用次数
cout << wp.expired() << endl;//引用次数不为0返回0,0才返回true
if (shared_ptr np = wp.lock())
{
cout << "如果np不为空则条件成立" << endl;
}
weak_ptr p1 = wp.lock();
cout << p.use_count() << endl;
cout << wp.use_count() << endl;
}
=====================================================================
第12章 动态内存 420页 weak_ptr
=====================================================================
//QQ108201645编写
#include
#include
#include
using namespace std;
int main()
{
vector v1{ 1,2,3 }, v2{ 4,5,6 };
shared_ptr> sv1 = make_shared>(v1);
shared_ptr> sv2(new vector(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
#include
#include
#include
using namespace std;
class StrBlobPtr;//对于StrBlob中的友元声明,这个前置声明是必须的
class StrBlob
{
public:
friend class StrBlobPtr;//声明StrBlobPtr是StrBlob的友元类
typedef vector::size_type size_type;
StrBlob();
StrBlob(initializer_list 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>data;
void check(size_type i, const string& msg)const;
};
StrBlob::StrBlob() :data(make_shared>()) {}
StrBlob::StrBlob(initializer_list i1)
: data(make_shared>(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> check(size_t, const string&)const;
//保存一个weak_ptr,意味着底层vector可能会被销毁
weak_ptr> wptr;
size_t curr;//在数组中的当前位置
};
shared_ptr> 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
#include
#include
#include
#include
using namespace std;
//提前声明,StrBlob的友元类
class StrBlobPtr;
class StrBlob
{
friend class StrBlobPtr;
public:
typedef vector::size_type size_type;
StrBlob() :data(make_shared>()) {}
StrBlob(initializer_list i1)
:data(make_shared>(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> 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> check(size_t, const string&)const;
//保存一个weak_ptr,意味着底层vector可能会被销毁
weak_ptr> wptr;
size_t curr;//在数组中的当前位置
};
inline shared_ptr> 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
#include
#include
#include
#include
#include
#include
using namespace std;
class StrBlobPtr;//前向声明
class StrBlob
{
friend class StrBlobPtr;//声明StrBlobPtr是StrBlob的友元类
typedef vector::size_type size_type;
public:
StrBlob() :data(make_shared>()) {};
StrBlob(initializer_list i1)
:data(make_shared>(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> 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> check(size_t i, const string& msg)const
{
shared_ptr> 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> 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
#include
#include
#include
#include
#include
#include
using namespace std;
class constStrBlobPtr;//前向声明
class StrBlob
{
friend class constStrBlobPtr;//声明StrBlobPtr是StrBlob的友元类
typedef vector::size_type size_type;
public:
StrBlob() :data(make_shared>()) {};
StrBlob(initializer_list i1)
:data(make_shared>(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> 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> check(size_t i, const string& msg)const
{
shared_ptr> 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> 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
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
#include
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<
=====================================================================
//QQ108201645编写
#include
#include
using namespace std;
int main()
{
unique_ptrup(new int[10]);
for (size_t i = 0; i != 10; ++i)
up[i] = i;
up.release();//自动调用delete[]销毁其指针
//>指up指向一个int数组而不是一个int,当up销毁它管理的指针时,会自动使用delete[]。
system("pause");
return 0;
}
=====================================================================
第12章 动态内存 426页 指向数组的unique_ptr
=====================================================================
//QQ108201645编写
表12.6:指向数组的unique_ptr |
|
指向数组的unique_ptr不支持成员访问运算符(点和箭头运算符)。 |
|
其它unique_ptr操作不变。 |
|
unique_ptr |
u可以指向一个动态分配的数组,数组元素类型为T |
unique_ptr |
u指向内置指针p所指向的动态分配的数组。p必须能转换为类型T*(参见4.11.2节,第143页) |
u[i] |
返回u拥有的数组中i处的对象 u必须指向一个数组 |
=====================================================================
//QQ108201645编写
#include
#include
using namespace std;
int main()
{
/*shared_ptr不直接支持管理动态数组,如果希望使用shared_ptr
管理一个动态数组,必须提供自己定义的删除器*/
shared_ptr 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
#include
#include
#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
#include
#include
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 alloc;//可以分配string的allocator对象
auto const p = alloc.allocate(n);//分配n个未初始化的string
//allocate调用为n个string分配了内存
system("pause");
return 0;
}
=====================================================================
//QQ108201645编写
表12.7:标准库allocator类及其算法 |
|
allocator |
定义了一个名为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
#include
#include
using namespace std;
int main()
{
/*标准allocator类在memory中*/
const size_t n = 5;
allocator 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<
=====================================================================
第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
#include
#include
#include
using namespace std;
int main()
{
vector vi{ "hello","world","hi" };
const size_t len = vi.size() * 2;
//分配比vi中元素所占用空间大一倍的动态内存
allocator 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
#include
#include
using namespace std;
int main()
{
/*练习题12.26:使用allocator重写427页中的程序。*/
allocator 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
#include
#include
#include
#include
using namespace std;
vector svec = { "abc","def","hello","world","123" };
const int N = svec.size()*2;
int main()
{
allocator 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
#include
#include
#pragma warning(disable:4996)
using namespace std;
int main()
{
string s("hello world 123 456 789 111 ");//带空格的6个元素
int n = 6;
allocator 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
#include
#include
#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 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
#include
#include
#include
#include
#include
=====================================================================
//QQ108201645编写
/*练习题12.27与12.30:TextQuery和QueryResult类的使用,我们已经介绍过语和标准
库特性。不要提前看后续章节内容,只用已经学到的知识对这两个类编写你自己的版本*/
/*练习题12.31:如果用vector代替set保存行号,会有什么差异,哪种更好,为什么
虽然set比vector会维护关键字的顺序,但我们是逐行读取文本输入,因此每个单词
出现的行号是自然按升序加入到容器中,不用特意用关联容器来保证行号的升序。
set是基于红黑树实现的,插入操作时间复杂性为O(logn)(n为
容器中的元素数目),而vector的push_back可达到常量时间。
另外,一个单词在同一行中可能出现多次。set自然可保证关键字不重复,但对vector这也
构不成障碍,每次添加行号前与最后一个行号比较一下就可以,同一行就不插入,总体性能还是vector更优*/
#include
#include
=====================================================================
/*练习12.28:编写程序实现文本查询,不要定义类来管理数据。你的程序应该接受一个
文件,并与用户交互来查询单词。使用vector、map和set容器来保存来自文件的数
据并生成查询结果。*/
#include
#include
#include
#include
#include
#include
=====================================================================
//QQ108201645编写
/*练习题12.32重写TextQuery和QueryResult类,用StrBlob代替vector
保存输入文件*/
#include
#include
#include
=====================================================================