C++ Prime 0x0C 练习题解

Posted 鱼竿钓鱼干

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++ Prime 0x0C 练习题解相关的知识,希望对你有一定的参考价值。

📔 C++ Prime 0x0C 练习题解

更好的阅读体验(实时更新与修正)

推荐阅读 《C++ Primer 5th》知识点总结&练习题解

12.1 动态内存与智能指针

12.1.1 shared_ptr 类

12.1 在此代码的结尾,b1b2 各包含多少个元素?

StrBlob b1;

	StrBlob b2 = "a", "an", "the";
	b1 = b2;//b1,b2共享相同的元素
	b2.push_back("about");//b1,b2都有4个元素
//b2被析构了,但是不影响b1

如果两个对象共享底层数据,当某个对象被销毁是,我们不能单方面地销毁底层数据

12.2 编写你自己的StrBlob 类,包含const 版本的 frontback

#include <stdexcept>
#include <string>
#include <vector>
#include <iostream>
#include <initializer_list>
#include <memory>
#include <exception>

class StrBlob
public:
	typedef std::vector<std::string>::size_type size_type;

	StrBlob();
	StrBlob(std::initializer_list<std::string> il);

	size_type size()constreturn data->size();
	bool empty()constreturn data->empty();

	void push_back(const std::string &t)data->push_back(t);
	void pop_back();

	std::string& front();
	std::string& front()const;

	std::string& back();
	std::string& back()const;

private:
	std::shared_ptr<std::vector<std::string>>data;
	void check(size_type i,const std::string &msg)const;
;

StrBlob::StrBlob():data(std::make_shared<std::vector<std::string>>())
StrBlob::StrBlob(std::initializer_list<std::string> il)
	:data(std::make_shared<std::vector<std::string>>(il))

void StrBlob::check(size_type i,const std::string &msg)const
	if(i >= data->size())
		throw std::out_of_range(msg);


std::string& StrBlob::front()
	check(0,"front on empty StrBlod");
	return data->front();

std::string& StrBlob::front()const
	check(0,"front on empty StrBlod");
	return data->front();

std::string& StrBlob::back()
	check(0,"back on empty StrBlod");
	return data->back();

std::string& StrBlob::back()const
	check(0,"back on empty StrBlod");
	return data->back();


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



int main()
	const StrBlob a"this","is","a","const","StrBlob";
	StrBlob b"this","is","not","a","const","StrBlob";

	std::cout << a.front() << " " << a.back() << std::endl;
	std::cout << b.front() << " " << b.back() << std::endl;

	return 0;

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

不需要,push_backpop_back会改变对象内容

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

size_type是无符号整型,传负数会变成正数

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

优点:不使用explicitinitializer_list可以隐式转换为StrBlob,这个逻辑上是可行的

缺点:如果我们确实需要用initializer_list,那么编译器会自动转为StrBlod

12.1.2 直接管理内存

12.6 编写函数,返回一个动态分配的 intvector。将此vector 传递给另一个函数,这个函数读取标准输入,将读入的值保存在 vector 元素中。再将vector传递给另一个函数,打印读入的值。记得在恰当的时刻delete vector

#include <vector>
#include <iostream>
#include <memory>

std::vector<int>* alloc_vector()
	return new std::vector<int>();


void read_vector(std::vector<int>* p)
	for(int x;std::cin >> x;)
		p->push_back(x);
	


void print_vector(const std::vector<int>* p)
	for(auto i:*p)std::cout << i << std::endl;


int main()
	auto p = alloc_vector();

	read_vector(p);
	print_vector(p);
	
	delete p;

	return 0;

12.7 重做上一题,这次使用 shared_ptr 而不是内置指针。

#include <vector>
#include <iostream>
#include <memory>

using T = std::vector<int>;
std::shared_ptr<T> alloc_vector()
	return std::make_shared<T>();


void read_vector(std::shared_ptr<T> p)
	for(int x;std::cin >> x;)
		p->push_back(x);
	


void print_vector(const std::shared_ptr<T> p)
	for(auto i:*p)std::cout << i << std::endl;


int main()
	auto p = alloc_vector();
	read_vector(p);
	print_vector(p);
	
	return 0;

12.8 下面的函数是否有错误?如果有,解释错误原因。

bool b() 
	int* p = new int;
	// ...
	return p;//被强制转为 bool 没有释放 p 指向的对象

12.9 解释下面代码执行的结果。

int *q = new int(42), *r = new int(100);//q 指向 42,r 指向100
r = q;//r q 都指向 42,r 指向 100的内存空间没有释放,内存泄漏
auto q2 = make_shared<int>(42), r2 = make_shared<int>(100);
r2 = q2;//r2,q2都指向42, 100的内存空间不被r2引用的时候自动释放

12.1.3 shared_ptr 和 new 结合使用

12.10 下面的代码调用了第413页中定义的process 函数,解释此调用是否正确。如果不正确,应如何修改?

shared_ptr<int> p(new int(42));
process(shared_ptr<int>(p));

正确,传入的shared_ptr<int>(p)创建一个临时对象,和p引用同一个对象,引用计数为2

函数结束,临时的智能指针被销毁,此时引用计数为1

12.11 如果我们像下面这样调用 process,会发生什么?

process(shared_ptr<int>(p.get()));

p.get()返回一个内置指针

shared_ptr<int>(p.get())创建一个新的智能指针对象,传入的时候引用计数为1,传出的时候引用计数为0,指向的内存被释放,此时p变成了空悬指针

12.12 psp 的定义如下,对于接下来的对 process 的每个调用,如果合法,解释它做了什么,如果不合法,解释错误原因:

auto p = new int();
auto sp = make_shared<int>();
(a) process(sp);//合法,传入的时候引用计数为2,结束的时候引用计数为1
(b) process(new int());//不合法,内置指针不能隐式转为智能指针
(c) process(p);//不合法,内置指针不能隐式转为智能指针
(d) process(shared_ptr<int>(p));//对于这个函数而言可以,但是会造成p成为空悬指针

12.13 如果执行下面的代码,会发生什么?

auto sp = make_shared<int>();
auto p = sp.get();
delete p;

sp指向的内存空间已经被释放,再用sp操作会出错

12.1.4 智能指针和异常

12.14 编写你自己版本的用 shared_ptr 管理 connection 的函数。

#include <vector>
#include <iostream>
#include <memory>

struct connection//连接需要的信息
	std::string ip;
	int port;
	connection(std::string i,int p):ip(i),port(p)
;

struct destination//正在连接什么
	std::string ip;
	int port;
	destination(std::string i,int p):ip(i),port(p)
;

connection connect(destination* pDest)//打开连接
	std::shared_ptr<connection> pConn(new connection(pDest->ip,pDest->port));
	std::cout << "creating connection(" << pConn.use_count() << ")" << std::endl;
	return *pConn;
;

void disconnect(connection pConn)//关闭给定连接
	std::cout << "connection close(" << pConn.ip << ":" << pConn.port << ")" << std::endl;	
;

void end_connection(connection* pConn)//自定义删除器
	disconnect(*pConn);


void f(destination &d)
	connection c = connect(&d);
	std::shared_ptr<connection>p(&c,end_connection);
	std::cout << "connecting now(" << p.use_count() << ")" << std::endl;

int main()
	destination dest("220.181.111.111",10086);
	f(dest);
	
	return 0;

12.15 重写上一题的程序,用 lambda 代替end_connection 函数。

#include <vector>
#include <iostream>
#include <memory>

struct connection//连接需要的信息
	std::string ip;
	int port;
	connection(std::string i,int p):ip(i),port(p)
;

struct destination//正在连接什么
	std::string ip;
	int port;
	destination(std::string i,int p):ip(i),port(p)
;

connection connect(destination* pDest)//打开连接
	std::shared_ptr<connection> pConn(new connection(pDest->ip,pDest->port));
	std::cout << "creating connection(" << pConn.use_count() << ")" << std::endl;
	return *pConn;
;

void disconnect(connection pConn)//关闭给定连接
	std::cout << "connection close(" << pConn.ip << ":" << pConn.port << ")" << std::endl;	
;

void f(destination &d)
	connection c = connect(&d);
	std::shared_ptr<connection>p(&c,[](connection* pConn)disconnect(*pConn););
	std::cout << "connecting now(" << p.use_count() << ")" << std::endl;

int main()
	destination dest("220.181.111.111",10086);
	f(dest);

	return 0;

12.1.5 unique_ptr

12.16 如果你试图拷贝或赋值 unique_ptr,编译器并不总是能给出易于理解的错误信息。编写包含这种错误的程序,观察编译器如何诊断这种错误。

#include <string>
#include <vector>
#include <iostream>
#include <memory>

int main()
	std::unique_ptr<std::string>p(new std::string("abc"));
	// std::unique_ptr<std::string>q(p);//拷贝
    // unique_ptr' has been explicitly marked deleted here unique_ptr(const unique_ptr&) = delete;
	std::unique_ptr<std::string>r;
	//r = p;//赋值
	//candidate function not viable: no known conversion from 'std::unique_ptr<std::string>' (aka 'unique_ptr<basic_string<char>>') to 'std::nullptr_t' (aka 'nullptr_t') for 1st argumentoperator=(nullptr_t) noexcept
	return 0;

12.17 下面的 unique_ptr 声明中,哪些是合法的,哪些可能导致后续的程序错误?解释每个错误的问题在哪里。

int ix = 1024, *pi = &ix, *pi2 = new int(2048);
typedef unique_ptr<int> IntP;
(a) IntP p0(ix);//不合法,当我们在定义一个 unique_ptr 时,需要将其绑定到一个new 返回的指针上。
(b) IntP p1(pi);//不合法,当我们在定义一个 unique_ptr 时,需要将其绑定到一个new 返回的指针上。
(c) IntP p2(pi2);//合法,但pi2可能会变成空悬指针
(d) IntP p3(&ix);//不合法,当 p3 被销毁时,它试图释放一个栈空间的对象。
(e) IntP p4(new int(2048));//合法
(f) IntP p5(p2.get());//不合法,p5 和 p2 指向同一个对象,但是他们互相独立,p5被销毁的时候也释放了p2指向的对象,p2变成空悬

12.18 shared_ptr 为什么没有 release 成员?

release放弃对指针的控制权,返回指针

unique_ptr一个对象只能被一个指针控制,需要释放控制权来给其他指针

shared_ptr允许多个指针指向同一个对象,不需要转移控制权

12.1.6 weak_ptr

12.19 定义你自己版本的 StrBlobPtr,更新 StrBlob 类,加入恰当的 friend 声明以及 beginend 成员。

12.20 编写程序,逐行读入一个输入文件,将内容存入一个 StrBlob 中,用一个 StrBlobPtr 打印出 StrBlob 中的每个元素。

#include <cstddef>
#include <stdexcept>
#include <string>
#include <vector>
#include <iostream>
#include <initializer_list>
#include <memory>
#include <exception>
#include <fstream>


class StrBlobPtr;

class StrBlob以上是关于C++ Prime 0x0C 练习题解的主要内容,如果未能解决你的问题,请参考以下文章

C++ Prime 0x0A 练习题解

C++ Prime 0x0A 学习笔记

C++练习2022年蓝桥杯选拔赛

C++ Primer 0x03 练习题解

C++ Primer 0x0B 练习题解

C++ Primer 0x0D 练习题解