c++ primer 第12章 动态内存 练习答案参考

练习12.1:在此代码的结尾,b1和b2各包含多少个元素?

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

b2被销毁了,b1还有4个元素。

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

#include "StrBlob1.h"

    StrBlob1::StrBlob1() :data(std::make_shared<std::vector<std::string>>()){}
    StrBlob1::StrBlob1(std::initializer_list<std::string> il):
        data(std::make_shared<std::vector<std::string>>(il)){}
    void StrBlob1::check(size_type i, const std::string &msg) const {
        if (i >= data->size())
            throw std::out_of_range(msg);
    }
    std::string& StrBlob1::front() {
        check(0,"front on empty StrBlob");
        return data->front();
    }
    const std::string & StrBlob1::front() const {
        check(0,"front on empty StrBlob");
        return data->front();
    }
    std::string& StrBlob1::back() {
        check(0,"back on empty StrBlob");
        return data->back();
    }
    const std::string& StrBlob1::back() const {
        check(0,"back on empty StrBlob");
        return data->back();
    }
    void StrBlob1::pop_back() {
        check(0,"pop_back on empty StrBlob");
        data->pop_back();
}
#ifndef TEST_STRBLOB1_H
#define TEST_STRBLOB1_H


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

class StrBlob1{
public:
    typedef std::vector<std::string>::size_type size_type ;
    StrBlob1();
    StrBlob1(std::initializer_list<std::string> il);
    size_type size(){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();

    const std::string &front() const;

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


#endif //TEST_STRBLOB1_H

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

 这个data是一个指针,加上const 则变成了一个常量指针,即指向常量的指针,在push_back和pop_back的时候并没有修改指向对象的数据。所以加不加都行。加了也用不到。

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

 因为size_type是个无符号的,输入一个负的会转换为一个比0大的数。

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

explicit阻止了构造函数的隐式转换,只能将函数直接初始化,不能拷贝初始化。这种设计让程序变得容易使用但是很难dubug。

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

vector<int>* _dyn_ivec(){return new vector<int>(10, 1);}
vector<int>* _in_ivec(istream& in, vector<int> *p){
    int i ;
    cin>>i;
    p->push_back(i);
    return p;
}
void _out_ivec(ostream& out, vector<int> *p){
    for (auto &i:*p) {
        cout<<i<<" ";
    }
    delete p;
}
int main()
{
    _out_ivec(cout,_in_ivec(cin,_dyn_ivec()));
    
    return 0;
}

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

shared_ptr<vector<int>> _dyn_ivec(){return make_shared<vector<int>>(10,1);}
shared_ptr<vector<int>> _in_ivec(istream& in, shared_ptr<vector<int>> p){
    int i ;
    cin>>i;
    p->push_back(i);
    return p;
}
void _out_ivec(ostream& out, shared_ptr<vector<int>>p){
    for (auto &i:*p) {
        cout<<i<<" ";
    }
}
int main()
{
    _out_ivec(cout,_in_ivec(cin,_dyn_ivec()));

    return 0;
}

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

bool b(){
int* p = new int;
//...
return p;
}

 这个地方将p转换为了bool值,离开了b函数p指针消失,但内存还有,造成了内存泄漏。

练习12.9 解释下面的代码执行结果:

int *q = new int(42), *r = new int(10);//手动开辟两个动态内存
r = q;// r 和q都指向 42的这个内存空间,10的内存空间造成泄漏
auto q2 = make_shared<int>(42), r2=make_shared<int>(100);//定义了两个智能指针
r2 = q2;                          //r2 指向的100的内存空间被释放,r2和q2都指向了42的内存空间

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

shared_ptr<int> p(new int(42));
process(shared_ptr<int>(p));
void process(std::shared_ptr<int> ptr)
{
    std::cout << "inside the process function:" << ptr.use_count() << "\n";
}

int main()
{
    shared_ptr<int> p(new int(42));
    process(shared_ptr<int>(p));//这里我理解为进行了类型转换,但是p本来就是shared_ptr,在实参这个地方有没有shared_ptr<int>都一样
    
    std::cout << p.use_count() << "\n";
    auto q = p;
    std::cout << p.use_count() << "\n";
    std::cout << "the int p now points to is:" << *p << "\n";
    return 0;
}

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

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

 会发生错误。get函数返回了一个内置指针,指向智能指针所管理的对象。将一个智能指针绑定到get返回的指针上是错误的。这是因为这两个智能指针独立创建,独自计数,却指向了同一个内存,当一个智能指针计数为0释放掉内存后,另一个智能指针可能还存在,变成空悬指针。

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

auto p = new int();
auto sp = make_shared<int>();
(a)process(sp);//正确 将智能指针传入
(b)process(new int());//不对,内置指针不能隐式转换为智能指针
(c)process(p);//不对,内置指针不能隐式转换为智能指针
(d)process(shared_ptr<int>(p));//正确,显示地将内置指针转换为智能指针,但最好不要这样混用智能指针和内置指针。

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

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

sp是一个智能指针, p是一个与sp指向同一内存的内置指针。delete p将 p所指向对象及内存释放,但是智能指针sp还在,造成了sp空悬,会报错。

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

struct connection {
    std::string ip;
    int port;
    connection(std::string ip_, int port_):ip(ip_), port(port_){ }
};
struct destination {
    std::string ip;
    int port;
    destination(std::string ip_, int port_):ip(ip_), port(port_){ }
};

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 conn = connect(&d);
    std::shared_ptr<connection> p(&conn, end_connection);
    std::cout << "connecting now(" << p.use_count() << ")" << std::endl;
}

int main()
{
    destination dest("202.118.176.67", 3316);
    f(dest);
}

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

struct connection {
    std::string ip;
    int port;
    connection(std::string ip_, int port_):ip(ip_), port(port_){ }
};
struct destination {
    std::string ip;
    int port;
    destination(std::string ip_, int port_):ip(ip_), port(port_){ }
};

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 conn = connect(&d);
    std::shared_ptr<connection> p(&conn, [](connection *p){ disconnect(*p);});
    std::cout << "connecting now(" << p.use_count() << ")" << std::endl;
}

int main()
{
    destination dest("202.118.176.67", 3316);
    f(dest);
}

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

unique_ptr<string> p1(new string("Stegosaurus"));
    unique_ptr<string> p2(p1);

 error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = std::__cxx11::basic_string<char>; _Dp = std::default_delete<std::__cxx11::basic_string<char> >]'
     unique_ptr<string> p2(p1);

         note: declared here
               unique_ptr& operator=(const unique_ptr&) = delete;

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

 (a) 没有从int到new int()的隐式转换;

练习12.18 shared_ptr为什么没有release成员?

练习12.23 编写一个程序 ,连接两个字符串字面常量,将结果保存在一个动态分配的char数组中。重写这个程序,连接两个标准库string对象。 

string s1 = "this ";
    string s2 = "is ";
    unique_ptr<char[]> ch(new char[s1.size() + s2.size()]);
    for (int i = 0; i < s1.size() + s2.size(); ++i) {
        if (i < s1.size())
            ch[i] = s1[i];
        else
            ch[i] = s2[i - s1.size()];
        cout<<ch[i];
    }
int main(){
    char *ch(new char[strlen("hello""world") + 1]);
    strcat(ch,"hello ");
    strcat(ch,"world");
    cout<<ch<<endl;

    std::string str1{ "hello " }, str2{ "world" };
    std::cout << str1 + str2 << std::endl;
    return 0;
}

练习12.24 编写一个程序,从标准输入读取一个字符串,存入一个动态分配的字符数组中。描述你的程序如何处理变长输入。测试你的程序,输入一个超出你分配的数组长度的字符串。

int sz;
    cout<<"lenth:"<<endl;
    cin>>sz;
    char * ch = new char[sz + 1];
    cout<<"string:"<<endl;
    char *s ;
    cin>>s;
    strcat(ch,s);
    cout<<ch;

练习12.25 给定下面的new表达式,你应该如何释放pa?

 int * pa = new int[10];

delete [] pa;

练习12.26 用allocator重写第427页中的程序。

int main(){
    int n = 5;
    allocator<string> alloc ;
    auto const p = alloc.allocate(n);
    string s ;
    auto q = p;
    while(cin>>s && q != p + n)
         alloc.construct(q++,s);
    while(q != p){
        alloc.destroy(q--);
    }
    alloc.deallocate(p,n);
    return 0;
}

allocator的使用多少有点双指针的意思,首先为p创建了一块n的内存,p作为一个参考不动了,创建一个指针q指向与q相同的区域,然后再通过q为这片内存初始化,通过q和p的比较来判断有哪些内存初始化了,哪些值没有被初始化。从而可以为这片内存初始化和释放掉内存上的值。

练习12.27 TestQuery和QueryResult类只使用了我们已经介绍过的语言和标准库特性。不要提前看后续章节内容,只用已经学到的知识对这两个类编写你自己的版本。

 只在一个类中定义并实现查找会简单一些。因为没有txt文件,将文件读取替换为了cin命令行读入。第一版代码如下:

#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <memory>

class TestQuery {
private:


    std::vector<std::string> text;
    std::map<std::string, std::set<int>> word_to_row;
    std::string find_word;

public:
    void look_up_word();
    //friend QueryResult;
    TestQuery(std::istream &is,std::string fword);
};

#include <sstream>
#include "TestQuery.h"
TestQuery::TestQuery(std::istream &is,std::string fword):find_word(fword) {
    std::string file;

    while(std::getline(is,file)){
        text.push_back(file);
        int n = text.size() - 1;
        std::istringstream line(file);
        std::string word;

        while(line >> word){
            word_to_row[word].insert(n);
        }

    }
    look_up_word();
}

void TestQuery::look_up_word() {
    if (word_to_row.count(find_word)){
        for (auto row : word_to_row[find_word]) {
            std::cout<<text[row]<<std::endl;
        }
    }
    else
        std::cout<<"Don't have the word"<<std::endl;
}

using namespace  std;

int main(){
    TestQuery n(cin,"the");
    return 0;
}

练习12.29 我们曾经用do while 循环来编写管理用户交互的循环,while和do while 更倾向于哪个版本,为什么?

 都一样。

练习12.31 如果用vector代替set保存行号,会有什么差别?哪种方法更好?为什么?

 vector不能不保证行号不重复,set就可以保证行号不重复。

这一章不会的内容较多,需要抽空再看看。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值