我是靠谱客的博主 虚拟绿茶,最近开发中收集的这篇文章主要介绍C++Primer第13章:拷贝控制(习题解答)13.1.1节练习13.1.6节练习13.1.3节练习13.1.4节练习13.1.16节练习13.2节练习13.2.1节练习13.2.2节练习13.3节练习13.4节练习13.5节练习13.6.1节练习13.6.2节练习13.6.3节练习,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

13.1.1节练习

13.1:构造拷贝函数是什么?什么时候使用它?
答:如果构造函数的第一个参数是自身类型的引用,且所有其他参数(如果有的话)都有默认值,则此构造函数是拷贝构造函数。拷贝构造函数在以下几种情况下会被使用 1.拷贝初始化(用=定义变量) 2.将一个对象作为实参传递给非引用类型的实参 3.一个返回类型为非引用类型的函数返回一个对象 4.用花括号列表初始化一个数组中的元素或者一个聚合类中的成员 5.初始化标准库容器或调用其insert/push操作时,容器会对其元素进行初始化
13.2:解释下为什么下面的声明是非法的

Sale_data::Sales_data(Sales_data rhs)

答:我们需要调用拷贝构造函数,但是永远都不会成功,因为其自身的参数也是非引用类型,为了调用它,必须拷贝其实参,而为了拷贝其实参,又需要调用拷贝构造函数,也就是自身,从而造成死循环。
13.3:当我们拷贝一个StrBlob类时,会发生什么?拷贝一个StrBlobPtr呢?
答:这两个类都没有定义拷贝构造函数,因此编译器为他们定义了合成拷贝构造函数。合成的拷贝构造函数逐个拷贝非const成员,对内置类型的成员,直接进行内存拷贝,对类类型的成员,调用其拷贝构造函数进行拷贝,因此拷贝一个StrBlob时,拷贝其唯一的成员data,使用其shared_ptr的拷贝构造函数来进行拷贝,因此其引用计数加1,拷贝一个StrBlobPtr时,拷贝成员wptr,用weak_ptr的拷贝构造函数进行拷贝,引用计数不变,然后拷贝curr,直接进行内存复制
13.4:假定Point是一个类类型,它有一个public的拷贝构造函数,指出下面的程序片段中哪些地方使用了拷贝构造函数

Point global;
Point foo_bar(Point arg)
{
Point local=arg,*heap=new Point(global);//将arg拷贝给local
*heap=local;//将local拷贝给*heap
Point pa[4]={local,*heap};//将local和*heap拷贝给pa的前两个元素
return *heap//函数的返回需要拷贝
}

答:如上
13.5:给定下面的类框架,编写一个拷贝构造函数。拷贝所有成员。你的构造函数应该动态分配一个新的string,并将对象拷贝到ps指向的位置

class HasPtr{
public:
HasPtr(const std::string &s=std::string()):ps(new std::string(s),i(0){}
private:
std::string*ps;
int i;
}

答:

class HasPtr{
public:
HasPtr(const std::string &s=std::string()):ps(new std::string(s),i(0){}
HasPtr(const HasPtr &rhs)
{
i=rhs.i;
ps=new string(*rhs.ps);
}
private:
std::string*ps;
int i;
}

13.1.6节练习

13.6:拷贝赋值运算符是什么?什么时候使用它?合成拷贝赋值运算符完成什么工作?什么时候会生成合成拷贝赋值运算符?
答:拷贝赋值运算符本身是一个重载的赋值运算符,定义为类的成员,左侧运算对象绑定到隐含的this参数,而右侧运算对象是所属类类型的,作为函数的参数,参数返回指向其右侧运算符对象的引用。 通常情况下,合成的拷贝赋值运算符会将右侧对象的非static成员逐个赋予左侧对象对应的成员,这些赋值操作时由成员类型的拷贝构造赋值运算符来完成的。 若一个类未定义自己的拷贝赋值运算符,编译器会为其合成拷贝赋值运算符,完成赋值操作,但对于某些类,还会起到该类型对象赋值的效果。
13.7:当我们将一个StrBlob赋值给另一个StrBlob时,会发生什么?赋值StrBlobPtr
答:编译器会为其定义合成拷贝构造函数
13.8:为HasPtr编写赋值运算符。类似拷贝构造函数,你的赋值运算符应该将对象拷贝到ps指向的位置
答:

#include<iostream>
using namespace std;
class HasPtr
{
public:
HasPtr(const std::string&s=std::string()):ps(new std::string(s)),i(0){};
HasPtr&operator=(const HasPtr&);
private:
std::string *ps;
int i;
};
HasPtr& HasPtr::operator=(const HasPtr& rhs)
{
auto newps=new string(*rhs.ps);
delete ps;
i=rhs.i;
return *this;
}
int main()
{
HasPtr rhs1,rhs2;
rhs1=rhs2;
return 0;
}

13.1.3节练习

13.9:析构函数是什么?合成析构函数完成什么工作?什么时候发生合成析构函数?
答:析构函数完成与构造函数相反的工作:释放对象资源,销毁非静态数据成员。从语法上来看,它是类的一个成员函数,名字是波浪号接类名,没有返回值,当一个类没有定义析构函数时,编译器会为它合成析构函数。合成的析构函数体为空,但这并不意味着它啥也不干。当函数体执行完后,非静态数据会逐个销毁。也就是说,成员是在析构函数之后隐含的析构阶段中进行销毁的。
13.10:当一个StrBlob对象销毁时会发生什么?一个StrBlobPtr对象销毁时呢?
答:这两个类都没有定义析构函数,因此编译器会为他们合成析构函数
13.11:为前面练习的HasPtr类添加一个析构函数
答:

#include<iostream>
using namespace std;
class HasPtr
{
public:
~HasPtr(){delete ps;};
HasPtr(const std::string&s=std::string()):ps(new std::string(s)),i(0){};
HasPtr&operator=(const HasPtr&);
private:
std::string *ps;
int i;
};
HasPtr& HasPtr::operator=(const HasPtr& rhs)
{
auto newps=new string(*rhs.ps);
delete ps;
i=rhs.i;
return *this;
}
int main()
{
HasPtr rhs1,rhs2;
rhs1=rhs2;
return 0;
}

13.12:在下面的代码片段会发生几次析构函数调用?

bool fcn(const Sales_data *trans,Sales_data accum)
{
Sales_data item1(*trans),item2(accum);
return item1.isbn()!=item2.isbn();
}

答:形参两次,函数中定义两次,考虑到trans是指针,它指向的对象的生命周期没有结束,所以调用三次析构函数
13.13理解拷贝控制成员和构造函数的一个好方法是定义一个简单的类,为该类定义这些成员,每个成员都打出自己的名字?
答:

#include<iostream>
#include<fstream>
#include<iterator>
#include<vector>
using namespace std;
struct x
{
x(){cout<<"x()"<<endl;}
x(const x& ){cout<<"(const x&)"<<endl;}
x&operator=(const x &rhs){cout<<"=(const x&)"<<endl;return *this;}
~x(){cout<<"~x()"<<endl;}
};
void f1(x num)
{
}
void f2(x &num)
{
}
int main(){
cout<<"1:"<<endl;
x x1;
cout<<endl;
cout<<"2:"<<endl;
f1(x1);
cout<<endl;
cout<<"3"<<endl;
f2(x1);
cout<<endl;
cout<<"4"<<endl;
x*ps=new x();
cout<<endl;
cout<<"5"<<endl;
vector<x>vec;
vec.push_back(x1);
cout<<endl;
cout<<"6"<<endl;
delete ps;
cout<<endl;
cout<<"7"<<endl;
x y1=x1;
y1=x1;
cout<<endl;
return 0;
}

13.1.4节练习

13.14:假定numbered是一个类,它有一个默认构造函数,能为每个对象生成一个唯一的序号,保存在名为mysn的数据成员中。假定numbered使用合成的拷贝构造函数控制成员,并给定如下函数

void f(numbered s){cout<<s.mysn<<endl;}

则下列代码输出哪些内容?

numbered a,b=a,c=b;
f(a);f(b);f(c);

答:

输出的内容相同

13.15:假定numbered定义了一个拷贝构造函数,能生成一个新的序号,这回改变上一题中调用的输出结果吗?
答:

初始化拷贝,然后函数调用又有拷贝

13.16:如果f中的参数是const numbered&会发生什么结果?
答:

函数中没有拷贝了,但是初始化有拷贝

13.17:分别编写前三题中所描述的numberedf,验证你的预测?
答:

#include<iostream>
using namespace std;
class numbered
{
public:
numbered():mysn(rand()){};
numbered(const numbered&):mysn(rand()){};
public:
int mysn;
};
void f(const numbered& s){cout<<s.mysn<<endl;}
int main()
{
numbered a,b=a,c=b;
f(a);f(b);f(c);
return 0;
}

13.1.16节练习

13.18:定义一个Employee类,它包含雇主的姓名和唯一的id,为这个类定义默认构造函数,以及接受一个表示雇员姓名的string的构造函数。每个构造函数应该通过递增一个static数据成员生成一个唯一的证号
答:

static int num=0;
class Employee
{
public:
Employee():id(num){num++;};
Employee(string s):id(num){num++;};
private:
string name;
int id;
};
int main()
{
//numbered a,b=a,c=b;
//f(a);f(b);f(c);
Employee a;
return 0;
}

13.19:你的Employee类需要定义它自己的拷贝控制成员?如果需要为什么?
答:

我觉得不需要-.-,会让拷贝的序号相同

13.20:解释当我们拷贝,赋值或销毁TextQueryQueryResult类对象时会发生什么?
答:

由于两个类都未定义拷贝控制成员,所以会定义其合成版本。

13.21:你认为TextQueryQueryResult类需要定义他们自己版本的拷贝控制成员吗?
答:

他们都采用智能指针管理共享的动态对象。而这些标准库都有良好的拷贝控制成员,用合成的拷贝控制成员简单地拷贝赋值销毁,即可得到正确的资源管理。

13.2节练习

13.22:假定我们希望HasPtr的行为像一个值,即,对于对象所指向的string成员,每个对象都有一份自己的拷贝。我们将在下一节介绍拷贝控制成员的定义,但是,你已经学习了这些成员需要的知识,在继续学习下一节之前,为HasPtr编写拷贝构造函数和拷贝赋值运算符。
答:

#include<bits/stdc++.h>
using namespace std;
class HasPtr
{
public:
HasPtr(const string&s=string()):ps(new string(s)),i(0){};
HasPtr(const HasPtr&p):ps(new string(*p.ps)),i(p.i){};
HasPtr&operator=(const HasPtr&);
~HasPtr(){delete ps;};
private:
string *ps;
int i;
};
HasPtr& HasPtr::operator=(const HasPtr& rhs)
{
delete ps;
ps=new string(*rhs.ps);
i=(rhs.i);
return *this;
}
int main()
{
//Employee a;
HasPtr has;
return 0;
}

13.2.1节练习

13.23:比较上一节练习中你编写的拷贝控制成员和这一节的代码?确定你理解了你的代码和我们的代码之间的差异
答:

理解了差异,主要是如果是拷贝自己的话,会把自己空间释放掉,这样再拷贝时本来需要拷贝的资源就没了。

13.24:如果本节中的HasPtr版本未定义析构函数,将会发生什么?如果未定义拷贝构造函数,将会发生什么
答:

编译器会自动合成析构函数,这样对于指针无法释放掉其内存

13.25:假定希望定义StrBlob的类值版本,而且希望继续使用shared_ptr,这样我们的StrBlobPtr类就仍能指向vectorweak_ptr了,你修改后的类将需要一个拷贝构造函数和一个拷贝赋值函数,但不需要析构函数,解释拷贝构造函数和拷贝赋值运算符必须要做什么。解释为什么不需要析构函数。
答:

不需要析构函数时因为其数据是智能指针,其自身已有其优秀的内存释放机制。

13.26:编写你自己的StrBlob类,编写你自己的版本

不想写-.-

13.2.2节练习

13.27:定义你自己的使用引用计数版本的HasPtr
答:

#include<bits/stdc++.h>
#include<iostream>
#include<string>
using namespace std;
class HasPtr
{
public:
HasPtr(const string
&s=string()):ps(new string(s)),i(0),use(new size_t(1)){};
HasPtr(const HasPtr&p):ps(p.ps),i(p.i),use(p.use){++*use;}
HasPtr&operator=(const HasPtr&);
~HasPtr();
int
get_use(){
return *use;
}
private:
string *ps;
int i;
size_t *use;
};
HasPtr::~HasPtr()
{
if(--*use==0)
{
delete ps;
delete use;
}
}
HasPtr&HasPtr::operator=(const HasPtr&rhs)
{
++*rhs.use;
if(--*use==0)
{
delete ps;
delete use;
}
ps=rhs.ps;
i=rhs.i;
use=rhs.use;
return *this;
}
int main()
{
HasPtr has;
HasPtr temp(has);
cout<<temp.get_use()<<endl;
return 0;
}

13.28:给定下面的类,为其实现一个默认构造和必要的拷贝构造成员
答:

(a)class TreeNode{
private:
std::string value;
int count;
TreeNode *left;
TreeNode *right;
};
(b)class BinStrTree{
private:
TreeNode*root;
}

答:

#include<bits/stdc++.h>
using namespace std;
class TreeNode
{
private:
string value;
int count;
TreeNode *left;
TreeNode*right;
public:
TreeNode(const string&s=string()):value(s),count(0),left(nullptr),right(nullptr){};
TreeNode(const TreeNode&rhs):value(rhs.value),count(rhs.count)
{
delete left;
delete right;
value=rhs.value;
count=rhs.count;
left=new TreeNode(*rhs.left);
right=new TreeNode(*rhs.right);
}
};
class BinStrTree
{
private:
TreeNode*root;
public:
BinStrTree():root(nullptr){};
BinStrTree(const BinStrTree&rhs)
{
delete root;
root=new TreeNode(*rhs.root);
}
};
int main()
{
TreeNode node;
BinStrTree node1;
TreeNode node2(node);
BinStrTree node3(node1);
return 0;
}

13.3节练习

13.29:解释swap(HasPtr&,HasPtr&)中对swap``的调用不会产生递归循环
答:

参数的类型不同,属于函数重载

13.30:为你的类值版本的HasPtr编写swap函数,并测试它。为你的swap函数添加一个打印语句,指出函数什么时候执行。
答:

#include<bits/stdc++.h>
using namespace std;
class HasPtr
{
friend void swap(HasPtr&,HasPtr&);//其他成员定义
private:
int i;
string *ps;
public:
HasPtr(const string &s=std::string()):i(0),ps(new string(s)){};
HasPtr(const HasPtr&rhs)
{
delete ps;
ps=new string(*rhs.ps);
i=rhs.i;
}
HasPtr&operator=(HasPtr);
//inline void swap(HasPtr&lhs,HasPtr&rhs);
};
HasPtr&HasPtr::operator=(HasPtr rhs)
{
swap(*this,rhs);
return *this;
}
inline void swap(HasPtr&lhs,HasPtr&rhs)
{
using std::swap;
swap(lhs.ps,rhs.ps);
swap(lhs.i,rhs.i);
}
int main()
{
HasPtr has1,has2;
has1=has2;
return 0;
}

13.31:为你的HasPtr类定义一个<运算符,并定义一个HasPtrvector,为这个vector添加一些元素,并对它执行sort,注意何时会调用swap

#include<bits/stdc++.h>
using namespace std;
class HasPtr
{
friend void swap(HasPtr&,HasPtr&);
public:
HasPtr(const string&s=string()):ps(new string(s)),i(0){};
HasPtr(const HasPtr&p):ps(new string(*p.ps)),i(p.i){};
HasPtr&operator=(const HasPtr&);
HasPtr&operator=(const string&);
string&operator*();
bool operator<(const HasPtr&)const;
~HasPtr();
private:
string *ps;
int i;
};
HasPtr::~HasPtr()
{
delete ps;
}
inline HasPtr& HasPtr::operator=(const HasPtr&rhs)
{
auto newps=new string(*rhs.ps);
delete ps;
ps=newps;
i=rhs.i;
return *this;
}
HasPtr&HasPtr::operator=(const string &rhs)
{
*ps=rhs;
return *this;
}
string &HasPtr::operator*()
{
return *ps;
}
inline void swap(HasPtr&lhs,HasPtr&rhs)
{
using std::swap;
cout<<"交换"<<*lhs.ps<<" "<<*rhs.ps<<endl;
swap(lhs.ps,rhs.ps);
swap(lhs.i,rhs.i);
}
bool HasPtr::operator<(const HasPtr&rhs)const
{
return *ps<*rhs.ps;
}
int main()
{
vector<HasPtr>vh;
int n;
cin>>n;
for(int i=0;i<n;i++)
{
vh.emplace_back(to_string(n-i));
}
for(auto p:vh)
{
cout<<*p<<" ";
}
cout<<endl;
sort(vh.begin(),vh.end());
for(auto p:vh)
{
cout<<*p<<" ";
}
cout<<endl;
return 0;
}

13.32:类指针的HasPtr版本会从swap函数受益吗?如果会,得到了什么?
答:

默认swap版本交换对其类指针的引用计数正常处理,能够正确处理类指针的交换,专用并不能带来收益。

13.4节练习

13.33:为什么Message的成员saveremove的参数是一个Folder&
答:

因为需要将Folder的指针添加到当前的Message的集合,这样话必须要引用类型,如果不是,则添加的就是拷贝,哪地址就变了,而由于需要该改变数据,也不能是const

13.34:编写本节描述的Message
答:

//头文件实现,其本身有问题,但是我不知到出现再哪了-.-
#pragma once
#ifndef MESSAGE_H_INCLUDED
#define MESSAGE_H_INCLUDED
#include<string>
#include"Folder1.h"
using namespace std;
class Message
{
friend class Folder;
public:
explicit Message(const std::string& str = "") :contents(str) {};
Message(const Message&);
Message& operator=(const Message&);
~Message();
void save(Folder&);
void remove(Folder&);
private:
string contents;
set<Folder*>folders;
void add_to_Folders(const Message&);
void remove_from_Folders();
};
void Message::save(Folder&f)
{
folders.insert(&f);
f.addMsg(this);//将信息添加到f所在的存储信息的集合中
}
void Message::remove(Folder&f)
{
folders.erase(&f);
f.removeMsg(this);
}
void Message::add_to_Folders(const Message& m)
{
for (auto f : m.folders)
{
f->addMsg(this);
}
}
Message::Message(const Message& m):contents(m.contents), folders(m.folders)
{
add_to_Folders(m);
}
void Message::remove_from_Folders()
{
for (auto f : folders)
{
f->removeMsg(this);
}
}
Message::~Message()
{
remove_from_Folders();
}
Message& Message::operator=(const Message& rhs)
{
remove_from_Folders();
contents = rhs.contents;
folders = rhs.folders;
add_to_Folders(rhs);
return *this;
}
#endif // MESSAGE_H_INCLUDED

13.36:如果Message使用合成的拷贝控制成员,会发生什么
答:

拷贝set,对Message来说应该是没啥问题的,但是对于Folder来说,并未将这个拷贝的数据存入其set中

13.36:设计并实现对应的Folder类,此类应该保存每一个指向Folder中包含的Messageset
答:

//头文件实现,依旧有问题,但是不知道出现在哪
#pragma once
#ifndef FOLDER_H_INCLUDED
#define FOLDER_H_INCLUDED
#include<iostream>
#include<set>
#include<algorithm>
#include"Message1.h"
typedef Message mmm;
using namespace std;
class Folder
{
public:
friend class Message;
Folder() {};
Folder(const Folder& p) {
messages = p.messages;
}
private:
set<Message*>messages;
void
addMsg(mmm*);
void removeMsg(mmm*);
};
void Folder::addMsg(mmm *m)
{
messages.insert(m);
}
void Folder::removeMsg(mmm *m)
{
messages.erase(m);
}
#endif // FOLDER_H_INCLUDED

13.37:为Message类添加成员,实现向folders添加或删除一个给定的Folder*。这两个成员类似Folder类的addMsgremMsg操作
答:

不想写

13.38:我们并未使用拷贝并交换方式来设计Message的赋值运算符。你认为其原因是啥?
答:

需要自己定义swap?

13.5节练习

13.39:编写你自己的版本的StrVec,包括自己版本的reservecapacityresize
答:

#include<iostream>
#include<string>
#include<algorithm>
#include<atlalloc.h>
//#define alloc alloccc
//using namespace std;
class StrVec {
public:
StrVec() :
elements(nullptr), first_free(nullptr), cap(nullptr) {};//默认初始化
StrVec(const StrVec&);//拷贝构造函数
StrVec& operator=(const StrVec&);//拷贝赋值运算符
~StrVec();//析构函数
void push_back(const std::string&);//拷贝元素
size_t size() const { return first_free - elements; };
size_t capacity() const { return cap - elements; };
std::string* begin()const { return elements; };
std::string* end() const { return first_free; };
void reserve(std::string*, std::string*);
void resize(int n);
std::allocator<std::string>alloc;//被添加的元素使用
private:
void chk_n_alloc() {
if (size() == capacity())reallocate();//工具函数,被拷贝构造函数
}
std::pair<std::string*, std::string*>alloc_n_copy(const std::string*, const std::string*);
void free();//销毁并释放内存
void reallocate();//获得更多内存并拷贝已有的元素
std::string* elements;//指向数组首元素的指针
std::string* first_free;//指向数组第一个空闲元素的指针
std::string* cap;//指向数组尾后位置的指针
};
void StrVec::push_back(const std::string& s) {
chk_n_alloc();//确保有空间容纳新的元素
alloc.construct(first_free++, s);//在first_free指向的元素中构造s的副本
}
//using std::string;
std::pair<std::string*,std::string*>
StrVec::alloc_n_copy(const std::string* b, const std::string* e) {
//分配空间保存给定范围内的元素
auto data = alloc.allocate(e - b);
//
return { data, uninitialized_copy(b, e, data) };
}
void StrVec::free() {
if (elements) {
for (auto p = first_free; p != elements; alloc.destroy(--p));
alloc.deallocate(elements, cap - elements);
}
}
StrVec::StrVec(const StrVec& s) {
auto newdata = alloc_n_copy(s.begin(), s.end());
elements = newdata.first;
first_free = cap = newdata.second;
}
StrVec::~StrVec() {
free();
}
StrVec& StrVec::operator=(const StrVec& rhs) {
auto data = alloc_n_copy(rhs.begin(), rhs.end());
free();
elements = data.first;
first_free = cap = data.second;
return *this;
}
//自己编写
void StrVec::reserve(std::string* start, std::string* beg) {
if (start >= begin() && start <= end() && beg >= begin() && beg <= end()) {
beg--;
while (start < beg) {
swap(*start, *beg);
start++;
beg--;
}
}
}
void StrVec::resize(int n) {
auto newsize = n;
auto newdata = alloc.allocate(newsize);
auto dest = newdata;
auto elem = elements;
size_t i;
for ( i = 0; i != size()&&i!=newsize; i++) {
alloc.construct(dest++, std::move(*elem++));
}
for (; i < newsize; i++) {
alloc.construct(dest++, "");
}
free();
elements = newdata;
first_free = cap = dest;
}
//end
void StrVec::reallocate() {
//分配当前大小两倍的内存空间
auto newcapacity = size() ? 2 * size() : 1;
//分配内存
auto newdata = alloc.allocate(newcapacity);
//将数据从旧内存移动到新内存
auto dest = newdata;//指向数组中下一个空闲位置
auto elem = elements;//指向就数组中下一个位置
for (size_t i = 0; i != size(); i++) {
alloc.construct(dest++, std::move(*elem++));
}
free();
elements = newdata;
first_free = dest;
cap = elements + newcapacity;
}
int main() {
using std::cout;
using std::endl;
StrVec s;
s.push_back("hello");
s.push_back("world");
s.reserve(s.begin(), s.end());
s.resize(5);
for (auto start = s.begin(); start != s.end(); start++)cout << *start << " ";
cout << endl;
return 0;
}

13.40:为你的StrVec类添加一个构造函数,它接受一个``initializer_list< string >参数。
答:

StrVec::StrVec(initializer_list<string>s) {
resize(s.size());
auto dest = elements;
for (auto iter = s.begin(); iter != s.end(); iter++) {
alloc.construct(dest++, *iter);
}
}

13.41:在push_back中,我们为什么在construct调用后置递增运算符,如果使用前置递增运算符,会发生什么?
答:

后置运算符会操作到每个元素,而前置运算符会把第一个漏掉

13.42:在你的TextQueryQueryResult类中用StrVec代替``vector< string >
答:

不想写-.-

13.43:重写free函数,用for_eachlambda代替for循环destroy元素。
答:

for_each(elements, first_free,
[this](string &s) { this->alloc.destroy(&s); });

13.44:编写标准库string类的简化版本,命名为String。你的类应该至少有一个默认的构造函数和一个接受c风格字符串指针参数的构造函数,使用allocator为你的String列分配所需内存。
答:

写了最基本的要求
#include<iostream>
using namespace std;
class String {
public:
String() :elements(nullptr) {};
String(const char*);
allocator<char>alloc;
private:
char* elements;
};
String::String(const char* s) {
auto newdata = alloc.allocate(strlen(s));
elements = newdata;
auto dest = elements;
for (int i = 0;i<=strlen(s); i++) {
alloc.construct(dest++, s[i]);
}
}
int main() {
String s("hello");
return 0;
}

13.6.1节练习

13.45:解释右值引用和左值引用的区别?
答:

所谓右值引用就是必须绑定到右值的引用,通常通过&&获取,右值引用只能绑定到一个将要销毁的对象中去,因此可以自由的移动其资源,左值引用,也就是常规引用,不能绑定到要转换的表达式,字面常量,或者返回右值的表达式,而右值引用恰好相反,可以绑定到这类中。返回左值的表达式包括返回左值引用的类型的函数和算术,关系,位,后置递增,递减,可以看到,左值的特点是有持久化的状态,而右值是短暂的。

13.46:什么类型的引用可以绑定到下面的初始化器上。

int f()
vector<int>vi(100)
int ?r1=f()
int ?r2=vi[0]
int ?r3=r1;
int ?r4=vi[0]*f()

答:

1.右值
2.左值
3.左值
4.右值
左值具有持久性,右值具有短暂性

13.47:不打算做这个-.-
13.48:定义一个vector<string>并在其上多次调用push_back。运行你的程序,并观察string被拷贝了多少次?
答:

理解拷贝何时发生

13.6.2节练习

13.49:为你的StrVec类添加一个移动构造函数和移动赋值函数
答:

StrVec::StrVec(StrVec&& s) noexcept{
elements = s.elements;
first_free = s.first_free;
s.elements = 0;
s.first_free = 0;
}
StrVec& StrVec::operator=(StrVec&& rhs) noexcept{
if (this != &rhs) {
if (elements)alloc.deallocate(elements, first_free-elements);
elements = rhs.elements;
first_free = rhs.first_free;
rhs.elements = 0;
rhs.first_free = 0;
}
return *this;
}

13.50:不打算写-.-
13.51:虽然unique_ptr不能拷贝,但是我们定义了一个clone函数,它以值的方式返回一个unique_ptr。解释为什么函数时合法的。
答:

unique_ptr不能拷贝,但是将要销毁的unique_ptr是可以拷贝的,因此clone返回局部unique_ptr对象ret是可以的,因为ret马上就要销毁了。而此时的
拷贝,其实是触发移动构造函数。

13.52:详细解释HasPtr对象的赋值发生了什么,特别是,一步一步描述hp,hp1以及HasPtr的赋值运算符参数rhs发生了什么变化?
答:

在进行拷贝赋值时,先通过拷贝构造创造出hp2的拷贝,然后再交换hp和rhs,rhs作为一个中间媒介,只是起到了将值从hp2传递给hp的作用
是一个冗余的操作,移动赋值页类似。

13.54如果我们为HasPtr定义移动赋值运算符,但并未改变拷贝并交换元素符,会发生什么?编写代码验证你的答案。
答:

会产生编译错误,因为产生了二义性。

13.6.3节练习

13.56:为你的StrBlob添加一个右值引用版本的push_back
答:

#include<iostream>
#include<vector>
#include<string>
#include<algorithm>
using namespace std;
class StrBlob {
public:
typedef std::vector<std::string>::size_type size_type;
StrBlob();
StrBlob(std::initializer_list<std::string> i1);
size_type size() const { return data->size(); };
bool empty() const {
return data->empty();
};
void push_back(const std::string& t) { data->push_back(t); };
void push_back(string&&);
void pop_back();
//元素访问
std::string& front() const {
return data->front();
}
std::string& back() const {
return data->back();
}
private:
std::shared_ptr<std::vector<std::string>>data;
void check(size_type i, const std::string& msg) const;
};
inline StrBlob::StrBlob() :data(make_shared<vector<string>>()) {};
void StrBlob::push_back(string&& rhs) {
cout << "移动" << endl;
data->push_back(move(rhs));
}
int main() {
//测试
StrBlob a;
string s = "hello";
a.push_back(s);
a.push_back("hello");
//a.push_back();
return 0;
}

13.56:如果sorted定义如下,会发生什么

Foo Foo:sorted()const&{
Foo ret(*this);
return ret.sorted();
}

答:

编译器认为是左值,于是会递归下去

13.57:如果sorted定义如下,会发生什么?

Foo Foo::sorted()const &{return Foo(*this).sorted();}

答:

副本是一个右值,所以会正常执行。

13.58:编写新版本的Foo类,其sorted中有打印语句,测试这个类,来验证自己的答案
答:

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
class Foo {
public:
Foo sorted()&&;
Foo sorted()const&;
Foo() {
data.emplace_back(3);
data.emplace_back(4);
}
private:
vector<int>data;
};
Foo Foo::sorted()&&
{
cout << "222" << endl;
sort(data.begin(), data.end());
return *this;
}
Foo Foo::sorted() const&
{
//cout << "111" << endl;
//return Foo(*this).sorted();
Foo ret(*this);
return ret.sorted();
}
int main() {
Foo f1;
f1.sorted();
return 0;
}

最后

以上就是虚拟绿茶为你收集整理的C++Primer第13章:拷贝控制(习题解答)13.1.1节练习13.1.6节练习13.1.3节练习13.1.4节练习13.1.16节练习13.2节练习13.2.1节练习13.2.2节练习13.3节练习13.4节练习13.5节练习13.6.1节练习13.6.2节练习13.6.3节练习的全部内容,希望文章能够帮你解决C++Primer第13章:拷贝控制(习题解答)13.1.1节练习13.1.6节练习13.1.3节练习13.1.4节练习13.1.16节练习13.2节练习13.2.1节练习13.2.2节练习13.3节练习13.4节练习13.5节练习13.6.1节练习13.6.2节练习13.6.3节练习所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(56)

评论列表共有 0 条评论

立即
投稿
返回
顶部