C++ Primer(第五版)|练习题答案与解析(第十二章:动态内存)

C++ Primer(第五版)|练习题答案与解析(第十二章:动态内存)

本博客主要记录C++ Primer(第五版)中的练习题答案与解析。
参考:C++ Primer
C++ Primer

练习题12.1

在此代码的结尾,b1和b2各包含多少元素?

StrBlob b1;
{
   
   
    strBlob b2 = {
   
   "a", "an", "the"};
    b1 = b2;
    b2.push_back("about");
}

“使用动态内存的原因:让多个对象共享相同的底层数据。也就是说拷贝的情况虽然发生,但是并不是元素的拷贝,而是将本身的指向拷贝给另一个指针对象,让这一个对象也指向自己所指向的对象,这样在本身释放以后,还有另一个对象指向自身原来所指向的对象。”
所以b1和b2都有4个,但是b2已经没了,如果接着使用b2会出错。

练习题12.2

编写你自己的StrBlob类,包含const版本的front和back。

#include <vector>
#include <string>
#include <initializer_list>
#include <memory>
#include <exception>
#include <iostream>
using namespace std;
class StrBlob {
   
   
public:
    using size_type = vector<string>::size_type;

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

    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();
    }
    string& front() {
   
   
        check(0, "front on empty StrBlob");
        return data->front();
    }

    string& back() {
   
   
        check(0, "back on empty StrBlob");
        return data->back();
    }

    const string& front() const {
   
   
        check(0, "front on empty StrBlob");
        return data->front();
    }
    const string& back() const {
   
   
        check(0, "back 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);
    }
private:
    shared_ptr<vector<string>> data;
};
int main(){
   
   
    const StrBlob csb{
   
    "train", "test", "overfit" };
    StrBlob sb{
   
    "train", "NN", "ML" };
    cout << csb.front() << " " << csb.back() << endl;//csb是const 使用pop_back报错
    sb.pop_back();
    cout << sb.front() << " " << sb.back() << endl;
    sb.back() = "CNN";
    cout << sb.front() << " " << sb.back() << endl;
    return 0;
}

测试:

train overfit
train NN
train CNN

练习题12.3

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

这两个函数不会对参数进行修改,所以无需加const。

练习题12.4

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

因为size_type本身就是unsigned类型的,非负数,即使赋值负数也会转换成大于0的数。

练习题12.5

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

explicit的作用就是抑制构造函数的隐式转换

  • 优点:不会自动的进行类型转换,阻止initializer_list自动转换为StrBlob,防止出错。
  • 缺点:必须用构造函数显示创建一个对象,不够方便简单

练习题12.6

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

#include <iostream>
#include <vector>
using namespace std;
vector<int>* applicateVector()
{
   
   
    return new (nothrow) vector<int>();
}
void readToVector(vector<int>* ivec)
{
   
   
    if (nullptr == ivec) {
   
   
        cout << "vector is illegal." << endl;
    }
    int num;
    while (cin >> num) {
   
   
        (*ivec).push_back(num);
    }
}
void printVector(vector<int>* ivec)
{
   
   
    if (nullptr == ivec) {
   
   
        cout << "vector is illegal." << endl;

    }
    for (auto i : *ivec) {
   
   
        cout << i << " ";
    }
    cout << endl;
}
int main(){
   
   
    vector<int> *ivec = applicateVector();//动态分配的int的vector
    if (nullptr == ivec) {
   
   
        cout << "vector is illegal." << endl;
    }
    readToVector (ivec);//读取 即输入
    printVector (ivec);//打印
    delete ivec;
    return 0;
}

测试:

12 12 10 5 3
^Z
12 12 10 5 3

练习题12.7

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

#include <iostream>
#include <vector>
#include <memory>
using namespace std;
shared_ptr<vector<int>> applicateVectorPtr()
{
   
   
    return make_shared<vector<int>>();
}

void readToVectorPtr(shared_ptr<vector<int>> ivec)
{
   
   
    if (!ivec) {
   
   
        cout << "vector is illegal." << endl;
    }

    int num;
    while (cin >> num) {
   
   
        ivec->push_back(num);
    }
}

void printVectorPtr(shared_ptr<vector<int>> ivec)
{
   
   
    if (!ivec) {
   
   
        cout << "vector is illegal." << endl;
    }

    for (auto i : *ivec) {
   
   
        cout << i << " ";
    }
    cout << endl;
}
int main(){
   
   
    shared_ptr<vector<int>> p;
    p = applicateVectorPtr();
    readToVectorPtr(p);
    printVectorPtr(p);
    return 0;
}

测试:

10 9 8 15 12
^Z
10 9 8 15 12

练习题12.8

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

函数定义的返回值类型与实际的返回类型不匹配。int* 会转换为bool类型。这会导致p将没有机会被delete,最终造成内存泄漏。

练习题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 = q,则r原来的空间没有指针指向,也不会被释放,因此造成内存泄漏。
  • r2 = q2, q2的引用计数+1,r2的引用计数-1,变为0,则r2原来指向的那块空间会被释放,不会造成内存泄漏。

练习题12.10

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

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

此调用正确。

练习题12.11

如果我们像下面这样调用process,会发生什么?
process(shared_ptr(p.get()));

p.get()初始化返回一个内置指针,传给process的是由p.get()初始化的一个新的shared_ptr,与p指向同一块内存。在process函数结束时,新的shared_ptr被释放,p就变成了一个空悬指针。P414。

练习题12.12

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

auto p = new int();
auto sp = make_shared<int>();

(a ) process (sp); 合法,将一个shared_ptr传给process。
(b ) process(new int()); 不合法,不能进行内置指针隐式转换为shared_ptr,P412。
(c ) process (p); 不合法,不能进行内置指针隐式转换为shared_ptr,P412。
(d ) process (shared ptr<int>(p)); 合法。但不建议这样使用,智能指针和常量指针混用容易引起问题,比如有可能被释放两次,P413。

练习题12.13

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

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

使用sp初始化p,p和sp指向同一块内存。delete p之后,这块内存被释放,sp也会被释放,导致同一块内存被释放两次。P414。

练习题12.14

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

#include <iostream>
#include <string>
#include <memory>
using namespace std;
struct connection {
   
   
    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<connection> pConn(new connection(pDest->ip, pDest->port));
    cout << "creating connection(" << pConn.use_count() << ")" << endl;
    return *pConn;
}
void disconnect(connection pConn)
{
   
   
    cout << "connection close(" << pConn.ip << ":" << pConn.port << ")" << endl;
}
void end_connection(connection *pConn)
{
   
   
    disconnect(*pConn);
}
void f(destination &d)
{
   
   
    connection conn = connect(&d);
    shared_ptr<connection> p(&conn, end_connection);
    cout << "connecting now(" << p.use_count() << ")" << endl;
}

int main()
{
   
   
    destination dest("202.192.01.02", 8888);
    f(dest);
}

测试:

creating connection(1)
connecting now(1)
connection close(202.192.01.02:8888)

练习题12.15

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

//其中f修改为:
void f(destination &d)
{
   
   
    connection conn = connect(&d);
    shared_ptr<connection> p(&conn, [](connection *p){
   
    disconnect(*p); });
    cout << "connecting now(" << p.use_count() << ")" << endl;
}

练习题12.16

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

#include <iostream>
#include <string>
#include <memory>
using namespace std;
int main()
{
   
   
    unique_ptr<int> p1 (new int(42));
    unique_ptr<int> p2 = p1;
    unique_ptr<int> p3 (p1);
    return 0;
}

在这里插入图片描述

练习题12.17

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

int ix = 1024,
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值