C++primer第十三章(第五版)答案

999999999999999## C++primer第十三章(第五版)答案
13.1
拷贝构造函数定义了当用同类型的对象初始化本对象的时候做什么。如果构造函数的第一个参数是自身的引用,且所有参数都有默认值,则这个构造函数是一个拷贝构造函数。
拷贝构造函数发生在需要拷贝初始化的时候,拷贝初始化发生的条件如下

  • 使用=定义变量时
  • 将一个对象作为实参传递给一个非引用类型的形参
  • 从一个返回类型为非引用类型的函数返回一个对象
  • 用花括号列表初始化一个数组中的元素或一个聚合类中的成员
    13.2
    拷贝构造函数的第一个参数必须是一个引用类型
    13.3
    拷贝StrBlob时,其成员shared_ptr的引用计数加一。
    拷贝StrBlboPtr时,成员unique_ptr引用计数不变。
    13.4
    发生拷贝构造的条件
  • 使用=定义变量时
  • 将一个对象作为实参传递给一个非引用类型的形参
  • 从一个返回类型为非引用类型的函数返回一个对象
  • 用花括号列表初始化一个数组中的元素或一个聚合类中的成员
Point global;
Point foo_bar(Point arg)//形参为非引用类型,调用拷贝构造					
{
   
   
    Point local = arg, *heap = new Point(global);	//使用了“=”初始化对象,调用拷贝构造
    *heap = local;
    Point pa[4] = {
   
   local, *heap};	//使用了“=”初始化对象,调用拷贝构造		
    return *heap;	//返回值为非引用类型,调用拷贝构造				
}

13.5

#include <iostream>
using namespace std;
class HasPtr
{
   
   
public:
    HasPtr(const string& s = string()) :
        ps(new string(s)), i(0) {
   
   };
    HasPtr(const HasPtr& h) :ps(new string(*(h.ps))), i(h.i) {
   
   }
private:
    string* ps;
    int i;
};

13.6

  • 拷贝赋值运算符是重载”=“运算符,即为一个名为operator=的函数,其参数与其所在类的的类型相同。

  • 在发生赋值操作的时候使用。

  • 合成拷贝赋值的工作:将右侧运算对象的每个非static成员赋予左侧运算对象的对应成员。

  • 当类未定义自己的拷贝赋值运算符,编译器会生成一个合成拷贝运算符。
    13.7
    发生浅拷贝,赋值运算符两侧的指针指向同一块内存空间。shared_ptr引用计数加1,unique_ptr计数不变。

13.8

    HasPtr& operator=(const HasPtr& has)
    {
   
   
        delete ps;
        ps = new string(*has.ps);
        i = has.i;
        
        return *this;
    }

13.9

  • 析构函数是类的一个成员函数,名字由波浪号接类名构成。它没有返回值,也不接受参数。

  • 通常的,析构函数负责释放对象在生存期分配的所有资源。对于某些类,合成析构函数被用来阻止该类型的对象被销毁。如果不是这种情况,合成析构函数的函数体就为空。

  • 当一个类未定义自己的析构函数时,编译器会为它定义一个合成析构函数。

13.10
所有对象的数据成员被销毁,智能指针的计数减一。
所有对象的数据成员被销毁,弱指针不影响计数器。
13.11

 ~HasPtr() {
   
   
        delete ps;
    }

13.12
三次,分别为item1,item2,accum。
当指向一个对象的引用或指针离开作用域时,析构函数不会执行。所以trans不会执行析构函数。
13.13
可以看到除了拷贝赋值运算符的不算,因为它并没有构造一个对象,构造函数和拷贝构造函数一共7个,而析构函数也有7个。其中引用形参传递没有调用拷贝构造函数,而非引用形参调用了拷贝构造函数,在离开了函数体时,对象调用析构函数,销毁对象。我们动态分配的内存空间需要亲自删除,如果没有删除,内存空间泄露,析构函数不会调用(去除最后的delete,析构函数会变为5个)。

#include <iostream>
#include<vector>
using namespace std;
struct X
{
   
   
    int v;
    X(int i):v(i) {
   
    cout << "构造函数" << endl; }
    X(const X&) {
   
    cout << "拷贝构造函数" << endl; }
    X& operator=(const X&) {
   
    cout << "拷贝复制运算符" << endl; return *this; }
    ~X() {
   
    cout << "析构函数" << endl; }
};
void reference(X&) {
   
     };
void unreference(X) {
   
     };
int main()
{
   
   
    X x1(1);
    X x2 = x1;
    X x3(x2);
    X * x4 = new X(2);
    X* x5 = new X(x1);
    x2 = x1;
    vector<X>x6;
    x6.push_back(x1);
    cout << "引用传值  " << endl << endl;
    reference(x1);  
    cout << "非引用传值" << endl;
    unreference(x1);
    cout << "程序结束" << endl;
    delete x4,x5;
}

输出结果如下

构造函数
拷贝构造函数
拷贝构造函数
构造函数
拷贝构造函数
拷贝复制运算符
拷贝构造函数
引用传值

非引用传值
拷贝构造函数
析构函数
程序结束
析构函数
析构函数
析构函数
析构函数
析构函数

13.14
相同的内容
13.15
会改变,因为定义了一个拷贝构造函数,会生成新的序号。输出三个不同的序号。
13.16
如果有拷贝构造函数还是会生成三个不同的序号,因为b和c是通过拷贝初始化的。
13.17

#include <iostream>
#include<time.h>
using namespace std;
class numbered
{
   
   
public:
	int mysn;
	numbered() {
   
    mysn = rand() % 100000; };
	/*numbered(numbered& a) {
		mysn = rand() % 100000;
	}*/

};

// void f(numbered s) { 
// 	cout << s.mysn << endl;
// }
void f(const numbered& s) {
   
   
	cout << s.mysn << endl;
}
int main() {
   
   
	srand(time(0));
	numbered a, b = a, c = b;
	f(a); f(b); f(c);

	return 0;
}

13.18
静态数据成员在类内定义,在类外声明。

#include <iostream>
using namespace std;
class Employee
{
   
   
	Employee() = default;
	Employee(const string& s)
private:
	string name;
	int idNum;
	static int id ;
};
int Employee::id = 0;
Employee::Employee()
{
   
   
	idNum = id++;
}
Employee::Employee(const string& s):name(s)
{
   
   
	idNum = id++;
}

13.19
不需要,因为每个雇员都是唯一的。

#include <iostream>
using namespace std;
class Employee
{
   
   
	Employee() = default;
	Employee(const string& s);
	Employee(const Employee&) = delete;
	Employee& operator= (const Employee&) = delete;
private:
	string name;
	int idNum;
	static int id ;
};
int Employee::id = 0;
Employee::Employee()
{
   
   
	idNum = id++;
}
Employee::Employee(const string& s):name(s)
{
   
   
	idNum = id++;
}

13.20
TextQuery和QueryResult的所有数据成员被拷贝,包括智能指针和容器,智能指针的引用计数加一。
析构智能指针引用计数减一,其他数据成员被销毁。
13.21
判断一个类是否需要定义他们自己版本的拷贝控制成员,可以看一个类是否需要自定义析构函数,如果一个类需要自定义析构函数,几乎可以肯定他也需要自定义拷贝控制函数成员。本类中的内存控制由智能指针自动控制,不需要定义析构函数,用合成默认析构函数即可。而且智能指针的拷贝不需要自定义拷贝构造函数,所以都设为=default即可。

13.22
编写一个赋值运算符时,一个好的模式是先将右侧的运算对象拷贝到一个局部临时对象中,当拷贝完成号,销毁左侧运算对象的现有成员就是安全的了。一旦左侧运算对象的资源被销毁,就只剩下将数据从临时对象拷贝到左侧运算对象的成员中了。

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

13.23
第一次我就是直接释放本对象的内存空间。明显本节的代码安全性更强,在出现自我赋值时也可以正确工作。
13.24
未定义析构函数会导致内存泄漏。
未定义拷贝构造函数,会发生浅拷贝,也就是两个指针指向同一块区域,当发生析构时,会导致对同一块空间释放两次,也就是释放了非法内存。

13.25
拷贝构造函数和拷贝赋值运算符需要给对象分配一块新的内存空间。
不需要析构函数是因为智能指针能够自己控制内存释放,不需要delete。

13.26

StrBlob::StrBlob(const StrBlob& str)
{
   
   
	data = make_shared<vector<string>>(*(str.data));//分配一块新的内存空间给对象,并用被拷贝对象的值初始化
}
StrBlob& StrBlob::operator=(const StrBlob&str) 
{
   
   
	data = make_shared<vector<string>>(*(str.data));//智能指针不需要亲自释放内存空间,=右侧的引用计数会减少,为零自动释放。
	return*this;
}

13.27

#include <iostream>
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& has) :ps(has.ps), i(has.i),use(has.use)
    {
   
   
       ++*use;
    }
    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;
    }
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值