13.1
拷贝构造函数:第一个参数是自身类型的引用,且任何额外参数都有默认值
拷贝初始化是通过拷贝构造函数或移动构造函数完成的
13.2
第一个参数必须的引用
13.3
拷贝StrBlob时,智能指针的计数器加一
拷贝StrBlobPtr时,智能指针计数器不增加
13.4
记住4种使用拷贝构造函数的情况:
1. 用 = 初始化对象
2. 对象作实参传递给非引用类型
3. 对象作返回值返回给返回值为非引用类型的函数
4. 花括号初始化对象
13.5
HashPtr(const HashPtr& orig):
ps(new string(*orig.ps)),
i(orig.i)
{ }
13.6
拷贝赋值运算符: 一种函数,函数名是 opetator=,注意它的返回值是引用
当对象赋值时使用它
合成拷贝赋值运算符:也是函数,功能1: 禁止赋值
功能2: 将右边对象的非static成员赋值给左边对象的对应成员
当类没有定义拷贝运算符石,编译器会自动生成一个合成拷贝运算符
13.7
如13.3
13.8
HashPtr& operator=(const HashPtr& rhs)
{ //这里很多细节操作,参考454页
auto temp = new string(*rhs.ps);
delete ps; //需要先delete已有的ps指向的内存,因为咱这是赋值操作,说明早就初始化了!!!
ps = temp; //为啥咱们要弄个临时的呢,因为如果是复制自己,执行delete后,它就没法复制了
i = rhs.i;
return *this;
}
13.9
析构函数:形如 ~fun()的函数,作用于=与构造函数相反
13.10
析构StrBlob时,智能指针的计数器减一,为0时自动释放内存
拷贝StrBlobPtr时,智能指针计数器不减少,自然内存也不会被释放
13.11
~HashPtr() { delete ps;}
13.12
3次,分别是 accum, item1, item2 。
trans是指针,所以没它啥事
13.13
#include <iostream>
#include <string>
#include <vector>
using namespace std;
struct X
{
X() {cout << "调用 构造" << endl;}
X(const X&) {cout << "调用 拷贝" << endl;}
X& operator=(const X&)
{
cout << "调用 赋值" << endl;
return *this;
}
~X() {cout << "调用 析构" << endl;}
};
void fun(const X x1, const X &x2)
{
cout << " 2222222222 " << endl;
vector<X> vec;
cout << " 3333333333 " << endl;
vec.push_back(x1);
cout << " 4444444444 " << endl;
vec.push_back(x2);
cout << " 5555555555 " << endl;
}
int main(int argc, char *argv[])
{
X *px = new X;
cout << " 1111111111 " << endl;
fun(*px, *px);
cout << " 6666666666 " << endl;
X x3 = *px;
x3 = *px;
delete px;
return 0;
}
/*输出:
调用 构造 //构造我们申请的第一个X,用指针px指向它
1111111111
调用 拷贝 //只执行一次拷贝,因为引用传递不拷贝
2222222222
3333333333
调用 拷贝 //push_back(x1) : 调用一次拷贝
4444444444
调用 拷贝 //push_back(x2) : 正常情况只有一个调用 拷贝,为啥多了东西呢??
调用 拷贝 //因为vector是动态增加,加了x1后,capacity变为1,再加x2,会执行容量增加,
调用 析构 //该操作是把原vec拷贝到更大的空间,再析构原来的空间,因此多出一个拷贝和析构
5555555555 //如果之前先设置 vec.reserve(2); 会发现444,555之间只有一句 调用 拷贝
调用 析构
调用 析构
调用 析构 //函数fun生命期结束,析构 x1, 还有vec内的两个X
6666666666
调用 拷贝 //拷贝初始化x3
调用 赋值 //给x3赋值
调用 析构 //用delete析构最初动态申请的X
调用 析构 //main结束,自动析构x3
*/
13.14
输出三个相同的数字,因为b,c采用拷贝初始化,在调用函数时仍然是拷贝初始化,而拷贝构造函数是系统合成的,只是简单拷贝所以都相同
#include <iostream>
using namespace std;
class numbered{
public:
numbered()
{
static int unique = 10;//共有变量,构造一次加1,保证每次构造值都不同
mysn = unique++;
}
int mysn;
};
void f(numbered s)
{
cout << s.mysn << endl;
}
int main(int argc, char *argv[])
{
numbered a, b = a, c = b;
f(a);f(b);f(c);
return 0;
}
/*输出:
10
10
10
*/
13.15
如果自己定义了拷贝构造函数,那自然按自己设定的来,输出结果自然会改变
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class numbered{
public:
numbered()
{
static int unique = 10;//共有变量
mysn = unique++;
}
numbered(const numbered& n){mysn = n.mysn + 1;}
int mysn;
};
void f(numbered s)
{
cout << s.mysn << endl;
}
int main(int argc, char *argv[])
{
numbered a, b = a, c = b; //此时a中的数据为10,b中为11,c中为12
f(a);f(b);f(c); //拷贝初始化a中为11,b中为12,c中为13
return 0;
}
/*输出:
11
12
13
*/
13.16
采用引用传递,每个对象都少了一次拷贝,所以输出为10 11 12
#include <iostream>
using namespace std;
class numbered{
public:
numbered()
{
static int unique = 10;//共有变量
mysn = unique++;
}
numbered(const numbered& n){mysn = n.mysn + 1;}
int mysn;
};
void f(numbered &s)
{
cout << s.mysn << endl;
}
int main(int argc, char *argv[])
{
numbered a, b = a, c = b;
f(a);f(b);f(c);
return 0;
}
/*输出:(以后不写这两字了)
10
11
12
*/
13.17
见前三题
13.18
被static弄的很烦,初始化的方式太奇怪了
Employee.h
#ifndef PRO1_EMPLOYEE_H
#define PRO1_EMPLOYEE_H
#include <string>
#include <iostream>
using namespace std;
class Employee {
public:
Employee();
explicit Employee(const string &s);
void print();
private:
static int unique;
string name;
int num;
};
#endif //PRO1_EMPLOYEE_H
Employee.cpp
#include "Employee.h"
int Employee::unique = 10; //试了n种办法,好像只能这样弄了
Employee::Employee()
{
num = unique++;
}
Employee::Employee(const string &s)
{
name = s;
num = unique++;
}
void Employee::print()
{
cout <<"name: "<< name << " num: " << num << endl;
}
main.cpp
#include <iostream>
#include <string>
#include "Employee.h"
using namespace std;
int main(int argc, char *argv[])
{
Employee e1;
e1.print();
Employee e2("Tom");
e2.print();
return 0;
}
/*
name: num: 10
name: Tom num: 11
*/
13.19
不需要,因为现实中一个是不会复制另一个人的,所以我们把它设为delete
Employee(const Employee&) = delete;
Employee& operator=(const Employee&) = delete;
13.20
它们的数据是标准容器和智能指针,它们都有自己的拷贝,赋值,析构函数,所以会都正确执行
13.21
参见13.21 自然就不需要了
注意三/五法则,如果析构需要自定义,那拷贝和赋值也需要自定义
13.22
以前内容的汇总,我们可以看到它是重新申请了新的内存存放string,自然是行为像一个值
class HasPtr {
public:
HasPtr(const string& s = string()) : ps(new string(s)), i(0) {}
HasPtr(const HasPtr& hp) : ps(new string(*hp.ps)), i(hp.i) {}
HasPtr& operator=(const HasPtr& hp)
{
auto new_p = new string(*hp.ps);
delete ps;
ps = new_p;
i = hp.i;
return *this;
}
~HasPtr() { delete ps; }
private:
string* ps;
int i;
};
13.24
未定义析构: 内存泄漏
未定义拷贝: 咱们就只是简单拷贝了指针,没有新建自己的内存,当别人销毁时,咱也就玩完了;
这个已经有点像类似指针的对象,但它是有问题的;
正确操作是使用智能指针,效果是当最后一个使用它的人销毁后才会释放内存
13.25
我的理解啊 : 智能指针执行拷贝赋值时 默认只是拷贝智能指针(浅拷贝,共用内存)
所以为了实现类值版本,我们就需要用智能指针申请新的内存
不需要析构函数,是因为智能指针自动会析构申请的内存
13.26
加这两个函数,从浅拷贝变成了深拷贝
StrBlob(const StrBlob& sb)
: data(std::make_shared<vector<string>>(*sb.data)){}
StrBlob& operator=(const StrBlob& sb)
{
data = std::make_shared<vector<string>>(*sb.data);
return *this;
}
13.27
#include <iostream>
#include <string>
using namespace std;
class HasPtr {
public:
explicit HasPtr(const string& s = string()) : ps(new string(s)), i(0), use(new size_t(1)) {}
HasPtr(const HasPtr& hp) : ps(hp.ps), i(hp.i) , use(hp.use){++*use; }
HasPtr& operator=(const HasPtr& hp)
{
++*hp.use; //递增右侧对象的引用计数
if(--*use == 0) //递减左侧对象的引用计数,如果左侧引用计数为0,析构左边分配的内存
{
delete ps;
delete use;
}
ps = hp.ps;
i = hp.i;
use = hp.use;
return *this;
}
~HasPtr()
{
if(--*use == 0)
{
delete ps;
delete use;
}
}
void print()
{
cout << *ps << " 引用次数为:";
cout << *use << endl;
} //自己加的用于测试
private:
string* ps;
int i;
size_t *use;
};
int main(int argc, char *argv[])
{
HasPtr p1("cat"); //直接初始化,次数为1
p1.print();
{
HasPtr p2 = p1; //拷贝初始化,次数加1,变为2
p1.print(); //p1,p2都可以,因为输出是一样的
p2 = p1; //赋值运算,左边引用次数加1,右边减1 ,次数仍然是2
p1.print();
}
p1.print(); //p2析构,次数变为1
return 0;
}
/*
cat 引用次数为:1
cat 引用次数为:2
cat 引用次数为:2
cat 引用次数为:1
*/
13.28
(a)
class TreeNode
{
public:
//这里构造函数中left和right必须用nullptr,因为如果用new TreeNode(),就会一直调用调用调用...
TreeNode():value(string()), count(new int(1)), left(nullptr), right(nullptr){}
TreeNode(const TreeNode& bst):value(bst.value), count(bst.count), left(bst.left), right(bst.right){++*count;}
TreeNode&operator=(const TreeNode& rhs)
{
++*rhs.count;
if(--*count == 0)
{ //这里有的迷
if(left)
{
delete left; //析构原来的值
left = nullptr; //这里有点迷,以前也没加过
}
if(right)
{
delete right;
right = nullptr;
}
delete count;
count = nullptr;
}
count = nullptr;
value = rhs.value;
left = rhs.left;
right = rhs.right;
count = rhs.count;
}
~TreeNode()
{
if(--*count == 0)
{
if(left)
{
delete left;
left = nullptr;
}
if(right)
{
delete right;
right = nullptr;
}
delete count;
count = nullptr;
}
}
private:
string value;
int *count; //题目这里没有* 。。。和上题一样,它作引用次数
TreeNode *left;
TreeNode *right;
};
(b)
这个就是简单的值传递类
class BinStrTree
{
public:
BinStrTree():root(new TreeNode()){}
BinStrTree(const BinStrTree& bst):root(new TreeNode(*bst.root)){}
BinStrTree& operator=(const BinStrTree& rhs)
{
auto new_root = new TreeNode(*rhs.root); //处理自赋值
delete root; //删除旧的
root = new_root; //赋新值
return *this;
}
~BinStrTree() { delete root; }
private:
TreeNode *root;
};
13.29
因为加了 using std::swap; 所以
swap(lhs.ps, rhs.ps); //采用标准库的swap(string*, string*)
swap(lhs.i, rhs.r); //采用标准库的swap(int,int)
12.30
#include <iostream>
#include <string>
using namespace std;
class HasPtr {
friend void swap(HasPtr &lhs, HasPtr &rhs);
public:
HasPtr(const string& s = string()) : ps(new string(s)), i(0) {}
HasPtr(const HasPtr& hp) : ps(new string(*hp.ps)), i(hp.i) {}
HasPtr& operator=(HasPtr rhs)
{
swap(*this, rhs);
return *this;
}
~HasPtr() { delete ps; }
private:
string* ps;
int i;
};
void swap(HasPtr &lhs, HasPtr &rhs)
{
using std::swap;
swap(lhs.ps,rhs.ps);
swap(lhs.i, rhs.i);
cout << "运行swap" << endl;
}
int main(int argc, char *argv[])
{
HasPtr p1("cat");
cout << "--------" << endl;
p1 = p1;
cout << "--------" << endl;
return 0;
}
/*
--------
运行swap
--------
*/
13.31
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
class HasPtr {
friend void swap(HasPtr &lhs, HasPtr &rhs);
friend bool operator<(const HasPtr &lhs, const HasPtr &rhs);
public:
explicit HasPtr(const string& s = string()) : ps(new string(s)), i(0) {}
HasPtr(const HasPtr& hp) : ps(new string(*hp.ps)), i(hp.i) {}
HasPtr& operator=(HasPtr rhs)
{
swap(*this, rhs);
return *this;
}
~HasPtr() { delete ps; }
void print(){cout << *ps << endl;} //自己定义的,用于检验结果
private:
string* ps;
int i;
};
void swap(HasPtr &lhs, HasPtr &rhs)
{
using std::swap;
swap(lhs.ps,rhs.ps);
swap(lhs.i, rhs.i);
cout << "运行swap" << endl;
}
bool operator<(const HasPtr &lhs, const HasPtr &rhs)
{
return *lhs.ps < *rhs.ps;
}
int main(int argc, char *argv[])
{
vector<HasPtr> vec = {HasPtr("cat"), HasPtr("dog"), HasPtr("apple")};
sort(vec.begin(),vec.end());
for(auto i : vec)
i.print();
return 0;
}
/*
运行swap
运行swap
apple
cat
dog
*/
13.32
不会,因为类指针的HasPtr的拷贝赋值只是拷贝指针,不涉及动态内存申请释放