cppPrimer第十二章习题

12.3 StrBlob需要const版本的push_back和pop_back吗?如果需要添加进去。否则解释为什么不需要?

不需要,因为push_back和pop_back都是修改data指向的vector的元素,用了const就无法修改了

12.4 (P407)在我们的check函数中,没有检查i是否大于0.为什么可以忽略这个检查?

size_type(底层是size_t)本身就是unsigned int类型,必然会 >= 0

12.5 我们未编写接受一个initializer_list explicit参数的构造函数。讨论这个设计策略的优缺点。

优点是给一个string对象的列表(initializer_list)可以通过调用接受该参数类型的构造函数生成该StrBlob临时对象返回。提供了一种简洁的生成StrBlob对象的方式

缺点是可能在某些地方我们确实需要initializer_list,而编译仍会将之转换为StrBlob

12.8 下面的函数是否有错误?如果有,解释错误原因。
bool b()
{
	int* p = new int;
	return p;
}

有错误,把int*类型对象当做bool值返回,实际效果是p如果不是一个空指针,那么就会返回true
其次是没有释放p所指向的动态内存空间

12.9 解释下面代码执行的结果
int* q = new int(42), * r = new int(100);
r = q;
auto q2 = make_shared<int>(42), r2 = make_shared<int>(100);
r2 = q2;

r所指向的原对象没有释放,会造成内存泄漏

r2所指向的源对象引用计数递减为0,会自动释放该对象。

12.10 解释下面调用是否正确,如果不正确应该如何修改?
void process(shared_ptr<int> ptr)
{

}
void p12_10()
{
	shared_ptr<int> p(new int(42));
	process(shared_ptr<int>(p));
}

没有问题,shared_ptr<int>(p)会创建一个临时的智能指针,这个指针与p引用同一个对象,此时引用计数为2,当表达式结束时,临时的智能指针被销毁,此时引用计数为1

12.11 如果我们像下面这样调用process,会发生什么?
void process(shared_ptr<int> ptr)
{

}
void p12_10()
{
	shared_ptr<int> p(new int(42));
	process(shared_ptr<int>(p.get()));
}

当调用process(shared_ptr<int>(p.get()));时,用p.get()返回的普通指针指向的对象来初始化临时的shared_ptr,此时引用计数为1,process退出时,局部指针ptr释放,则计数-1变为0,则释放掉p所指向的内存空间,造成智能指针p为空悬指针。

12.12 p和sp定义如下,对于接下来的对process的每个调用,如果合法,解释它做了什么,如果不合法解释错误原因
void process(shared_ptr<int> ptr){}
void p12_12()
{
	//内置指针
	auto p = new int();//值初始化为0
	//智能指针
	auto sp = make_shared<int>();
	process(sp); //合法,将sp拷贝给process的形参,函数里面引用计数为2,函数结束后引用计数为1
	process(new int()); //错误,不能由内置指针int *隐式转换为智能指针shared_ptr<int>
	process(p);//道理同上
	process(shared_ptr<int>(p));//正确,但智能指针和内置指针混用会出问题,在表达式结束后,引用计数为0,该智能指针被销毁,它所指向的对象(p所指向的对象)也被释放。而此时内置指针p依旧指向该内存空间。之后对内置指针p的操作可能会引发错误。
}
12.13 如果执行下面的代码会发生什么
shared_ptr<int> sp = make_shared<int>();
int* p = sp.get();
delete p;

通过delete p使得sp指向的内存被释放,运行到程序结尾sp会在再次释放这块内存从而造成错误。

12.14 编写你自己的版本的用shared_ptr管理connection函数
struct destination;
struct connection; //连接所需的信息
connection connect(destination *); //打开连接
void disconnect(connection); //关闭给定的连接

void end_connection(connection *p){ disconnect(*P); }

void f(destination &d)
{
    connection c = connect(&d);
    shared<connection> p(&c, end_connection);
}
12.15 重写上一题程序,用lambda代替end_connection
struct destination;
struct connection; //连接所需的信息
connection connect(destination *); //打开连接
void disconnect(connection); //关闭给定的连接

void f(destination &d)
{
    connection c = connect(&d);
    shared<connection> p(&c, 
                        [](connection *p){ disconnect(*p); });
}
12.16 如果你试图拷贝或赋值unique_ptr,编译器并不总是能给出易于理解的错误信息。编写包含这种错误的程序,观察编译器如何诊断这种错误。
unique_ptr<int> p(new int(11));
unique_ptr<int> p1(p);

提示尝试引用已删除的函数

12.17 下面的unique_ptr声明中,哪些是合法的,哪些可能导致后续的程序错误?解释每个错误的问题在哪里?
int ix = 1024, * pi = &ix, * pi2 = new int(2048);
typedef unique_ptr<int> IntP;
//IntP p0(ix); //必须利用new返回的指针来初始化unique_ptr
//IntP p1(pi); // 有错误,pi所指的对象是栈上分配的内存空间,不是通过new在动态内存上分配的
IntP p2(pi2);
//IntP p3(&ix); //有错误,&ix是栈上分配的对象的指针(地址)
IntP p4(new int(2048));
IntP p5(p2.get()); //不能用unique_ptr指向的对象去初始化零一个unique_ptr
12.18 sharerd_ptr为什么没有release成员?

因为shared_ptr可以指向已被其他shared_ptr所指的对象,所以不需要release函数,释放控制权并返回指向对象的指针。

因为一个对象只能被一个unique_ptr所指,而 release是unique_ptr放弃对当前所指对象的绑定,并返回指向该对象的指针用来初始化其他unique_ptr。

12.21 也可以这样编写StrBlobPtr的deref成员:
std::string& deref() const
{
	return (*check(curr, "dereference past end"))[curr];
}

我认为原来版本更好,虽然新版本代码更简短,但是把所有操作放在一行阅读起来会更麻烦,降低可读性。

12.23 编写一个程序,连接两个字符串字面常量,将结果保存在一个动态分配的char数组中。重写这个程序,连接两个标准库 string对象。
void p12_23()
{
	//C风格字符串
	const char* c1 = "Hello";
	const char* c2 = "World";
	unsigned len = strlen(c1) + strlen(c2) + 1;
	char* r = new char[len]();//值初始化
	strcat_s(r, len, c1);
	strcat_s(r, len, c2);
	cout << r << endl;
	delete[] r;
}

void p12_23_1()
{
	string str1 = "apple";
	string str2 = "watch";
	unsigned len = strlen(str1.c_str()) + strlen(str2.c_str()) + 1;
	char* r = new char[len]();

	strcpy_s(r, len, (str1 + str2).c_str());
	cout << r;
	cout << endl;
	delete[] r;
}
12.24 编写一个程序,从标准输入读取一个字符串,存入一个动态分配的字符数组中。描述你的程序如何处理变长输入。测试你的程序,输入一个超出你分配的数组长度的字符串。

如果存入的字符串比动态分配的字符数组的长度要短的话,剩余部分用空串初始化

如果字符串长度超出分配的字符数组的长度,则会抛出异常

12.26 用allocator重写427页的程序
#include<iostream>
#include<memory>
#include<string>

using std::cin;
using std::allocator;
using std::cout;
using std::endl;
using std::string;

int main()
{
	allocator<string> alloc;//创建一个分配器
	int n = 10;
	string* const p = alloc.allocate(n); // 分配了n个string对象内存
	string s;
	string* q = p;//q指向第一个分配的string内存
	while (cin >> s && q != p + n)
		alloc.construct(q++, s); //用输入s调用string构造函数在q指向的内存中构造一个对象
	const size_t size = q - p;
	
	//对q指向的对象执行析构
	while (q != p)
	{
		cout << *--q << endl;
		alloc.destroy(q);
	}
		
	//释放分配的内存
	alloc.deallocate(p, n);
}
12.29 我们曾经用do while循环来编写管理用户交互的循环。用do while重写本节程序,解释你倾向于哪个版本,为什么?
void runQueries1(ifstream& infile)
{
	TextQuery tq(infile);	//保存并建立查询map
	   //与用户交互:提示用户输入要查询的单词,完成并查询打印结果
	string s;
	cout << "enter word to look for, or q to quit: ";
	if (!(cin >> s) || s == "q")
		return;
	do
	{
		print(cout, tq.query(s)) << endl;
		cout << "enter word to look for, or q to quit: ";
	} while (!(cin >> s) && s != "q");

}

我认为原来版本好,因为用do while在循环外还要对第一次输入再做一个单独的判断

12.31 如果用vector代替set保存行号,会有什么差别?

如果一行有多个重复的单词,行号也会重复记录,导致会重复输出同一行内容,且行号不会排序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值