13.22
注意拷贝赋值运算符中左侧对象的指针先释放再指向右侧新的指针
#include <iostream>
class HasPtr{
public:
HasPtr(const std::string &s = std::string()):ps(new std::string(s)),i(0){}
HasPtr(const HasPtr &a):ps(new std::string(*a.ps)),i(a.i){}
HasPtr& operator=(const HasPtr &hp)
{
auto p = new std::string(*hp.ps);
delete ps;//释放=左侧对象的旧的内存
ps = p;//指向右侧对象指针所指
i = hp.i;//赋值i
return *this;//返回当前对象
}
~HasPtr()
{
delete ps;
}
private:
std::string *ps;
int i;
};
13.23
注意释放operator=要是释放左侧对象占用的资源,再去赋值
并且最好在销毁做猜测对象资源之前拷贝右侧对象成员
13.24
没有定义析构函数,每个对象销毁时就会调用隐式的析构函数,但是这个隐式的析构函数智智能删除指针,不能释放指向的那块内存,所以会造成内存泄漏,
没有定义拷贝构造函数,会使得所有对象的指针都指向通一块内存,这是我们不希望看到的
13.25
拷贝构造函数和拷贝赋值运算要动态分配内存,如果使用合成的拷贝构造函数不会动态分配内存,而是多个对象的指针指向同一个对象
可以不使用析构函数的原因:因为析构函数一般用来释放内存,但是因为这两个类使用的是智能指针,引用值到0会自动销毁指针,释放内存
13.26
在StrBlob类中加入下面的拷贝构造函数和拷贝赋值函数
StrBlob(initializer_list<string> il);
//拷贝构造函数
StrBlob(const StrBlob& a){
data = make_shared<vector<string>>(*a.data);
}
//拷贝赋值运算符
StrBlob& operator=(const StrBlob & a){
data =make_shared<vector<string>>(*a.data);
return *this;
}
13.26
书上的版本
#include <iostream>
using std::string;
class HasPtr{
public:
//HasPtr(const string &s= new string()):ps(new string(s)),i(0),use(new std::size_t(1)){}
HasPtr(const string &s= string()){
std::cout<<"默认构造函数"<<std::endl;
ps = new string(s);
i =0;
use = new std::size_t(1);
}
HasPtr(const HasPtr &p){
ps = p.ps;
i = p.i;
use = p.use;
*use++;
}
HasPtr& operator=(const HasPtr &p){
++*use;//递增右侧运算对象的引用计数
if(--*use == 0){//引用为零
delete ps;
delete use;
}
ps = p.ps;
i = p.i;
use = p.use;
return *this;
}
~HasPtr(){
if(--*use == 0){
delete ps;
delete use;
}
}
private:
string *ps;
int i;
std::size_t *use;
};
int main()
{
std::cout << "Hello world!" <<std::endl;
return 0;
}
13.28
(a)这个题目还是考察对默认构造函数(直接给出默认值),拷贝构造函数(确定好是拷贝指针还是拷贝指针指向的内存,这里是要拷贝指针),拷贝赋值运算符(判断应用次数是否为0并进行相应操作)还有析构函数(释放内存,置指针为空指针)的定义,定义的时候根据代码功能去写即可
注意节点指针指向的内存释放和置空节点指针
#include<iostream>
using std::string;
class TreeNode{
private:
std::string value;//节点内容
int *count;//节点使用次数,使用次数为0即可放弃
TreeNode *left;//左孩子
TreeNode *right;//右孩子
public:
TreeNode(){
value =string();//定义内容
count = new int(1);//申请放次数的内存空间
left = nullptr;//左右孩子都默认为空指针
right = nullptr;
}
TreeNode(const TreeNode& a){//拷贝构造函数,拷贝完成后被拷贝的对象的引用加1
value = a.value;
count = a.count;
left = a.left;
right = a.right;
(*a.count)++;
}
TreeNode & operator=(const TreeNode &a){
++*a.count;
if(--*count == 0){//若是引用为0;则释放左右指针指向的内存并且指针置空指针
if(left){delete left;left = nullptr;}
if(right){delete right;right = nullptr;}
}
value = a.value;
count = a.count;
left = a.left;
right = a.right;
return *this;
}
~TreeNode(){
if(--*count == 0){
if(left){delete left;left = nullptr;}
if(right){delete right;right = nullptr;}
}
delete count;
count = nullptr;
}
};
(b)这里注意定义拷贝赋值运算符的时候先用新的指针指向右边对象的拷贝内存,delete本对象指向节点内存,重新指向新申请的拷贝内存,最后返回本对象,
#include<iostream>
#include"TreeBode.h"
class BinStrTree{
private:
TreeNode *root
public:
BinStrTree(){
root = new TreeNode();
}
BinStrTree(const BinStrTree &a ){
root = new BinStrTree(*a.root);
}
BinStrTree &operator=(const BinStrTree &a){
TreeNode newT = new TreeNode(*a.root);
delete root;
root = newT;
return *this;
}
~BinStrTree(){
delete root;
}
};
13.29
总的来说是因为这是三个不一样的函数,所以不会导致递归
最外层的函数是Foo类中定义的
std::swap是标准库函数
最后一个swap是针对HasPtr类的
在swap执行时会根据范围进行进行扫描,根据参数选择参数选择。因为最内层的参数是lhs.h,rhs.h
,是HasPtr类,所以先从HasPtr类开始扫描,HasPtr有的话就用HasPtr类中的swap若是没有就用std中的swap的方法,所以这并是不同一个方法,所以不会产生无限递归的情况
void swap(Foo &lhs,Foo &rhs){
using std::swap;
swap(lhs.h,rhs.h);
}
13.30
#include <iostream>
using std::string;
class HasPtr{
public:
friend void swap(HasPtr& a,HasPtr& b);
//HasPtr(const string &s= new string()):ps(new string(s)),i(0),use(new std::size_t(1)){}
HasPtr(const string &s= string()){
std::cout<<"默认构造函数"<<std::endl;
ps = new string(s);
i =0;
use = new std::size_t(1);
}
HasPtr(const HasPtr &p){
ps = p.ps;
i = p.i;
use = p.use;
*use++;
}
HasPtr& operator=(const HasPtr &p){
++*use;//递增右侧运算对象的引用计数
if(--*use == 0){//引用为零
delete ps;
delete use;
}
ps = p.ps;
i = p.i;
use = p.use;
return *this;
}
~HasPtr(){
if(--*use == 0){
delete ps;
delete use;
}
}
private:
string *ps;
int i;
std::size_t *use;
};
inline void swap(HasPtr& a,HasPtr& b){
using std::swap;
std::cout<<"swap函数执行"<<std::endl;
swap(a.ps,b.ps);//交换指针
swap(a.i,b.i);//交换int成员
}
main函数
#include <iostream>
#include"HasPtr.h"
using namespace std;
int main()
{
HasPtr a("liufengzhi");
HasPtr b("swewds");
swap(a,b);
return 0;
}
13.31
首先是HasPtr代码
#include <iostream>
using std::string;
class HasPtr{
public:
friend bool operator<(const HasPtr& a,const HasPtr& b);
//HasPtr(const string &s= new string()):ps(new string(s)),i(0),use(new std::size_t(1)){}
HasPtr(const string &s= string()){
std::cout<<"默认构造函数"<<std::endl;
ps = new string(s);
i =0;
use = new std::size_t(1);
}
HasPtr(const HasPtr &p){
ps = p.ps;
i = p.i;
use = p.use;
*use++;
}
HasPtr& operator=(const HasPtr &p){
++*use;//递增右侧运算对象的引用计数
if(--*use == 0){//引用为零
delete ps;
delete use;
}
ps = p.ps;
i = p.i;
use = p.use;
return *this;
}
void swap(HasPtr& b){
using std::swap;
std::cout<<"swap函数执行"<<std::endl;
swap(ps,b.ps);//交换指针
swap(i,b.i);//交换int成员
}
~HasPtr(){
if(--*use == 0){
delete ps;
delete use;
}
}
void show()const{
std::cout<<*ps<<std::endl;
}
private:
string *ps;
int i;
std::size_t *use;
};
bool operator<(const HasPtr& a,const HasPtr& b){
return *a.ps<*b.ps;
}
void swap(HasPtr& a,HasPtr& b){
a.swap(b);
}
然后是main函数
#include <iostream>
#include"HasPtr.h"
#include<vector>
#include<algorithm>
using std::string;
using std::vector;
int main()
{
HasPtr a("d");
HasPtr b("c");
HasPtr c("b");
HasPtr d("a");
vector<HasPtr> ivec;
//ivec.resize(4);
ivec.reserve(4);
ivec.push_back(a);
ivec.push_back(b);
ivec.push_back(c);
ivec.push_back(d);
sort(ivec.begin(),ivec.end());
for(auto c:ivec){
c.show();
}
return 0;
}
13.32
不会,因为类指针版本本来交换的就是指针,而定义自己的swap后交换的也还是指针,所以不会有收益
网友评论