如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数。
使用时候:
1.用=定义变量
2.将要给对象作为实参传递给一个非引用类型的实参
3.从一个返回类型为非引用类型的函数返回一个对象
4.用花括号列表初始化一个数组中的元素或一个聚合类中的成员
因为如果拷贝构造函数的参数不是引用类型,则调动永远也不会成功:为了调用拷贝构造函数,我们必须拷贝它的实参,但为了拷贝实参,我们又需要调用拷贝构造函数,如此无限循环。
拷贝其中的资源和对象;拷贝其中的指针。
都使用了拷贝构造函数
HasPtr(HasPtr& hp)
{
ps = new string(*hp.ps);
i = ps.i;
}
拷贝赋值运算符就是接受一个与其所在类相同类型的参数,返回一个指向其左侧运算对象的引用并名为operator=的函数;当使用=运算符时使用该函数; 合成拷贝赋值运算法类似拷贝构造函数;如果一个类未定义自己的拷贝赋值运算符,编译器会为它生成一个合成拷贝赋值运算符。
赋值其中的资源和对象; 赋值其中的指针对象
HasPtr& operator=(const HasPtr& hp)
{
ps = new string(*hp.ps);
i = ps.i;
}
析构函数是类的一个成员函数,名字由波浪号接类名构成,它没有返回值,也不接受参数;
合成析构函数被用来阻止该类型的对象被销毁;
当一个类未定义自己的析构函数时,编译器会为它定义一个合成析构函数
析构函数执行;
析构函数不会执行
~HasPtr() { }
3次
struct X {
X& operator=(const X&);
~X();
};
都输出同样的内容
会改变,拷贝构造函数生成的序号独一无二
如果类中有拷贝构造函数,则结果会改变;如果没有,则结果不变
/* 练习13.14 */
#include
using namespace std;
class numbered
{
public:
int mysn;
numbered() { mysn = rand() % 100; }
};
void f(numbered s)
{
cout << s.mysn << endl;
}
int main()
{
numbered a, b = a, c = b;
f(a);
f(b);
f(c);
system("pause");
return 0;
}
/* 练习13.15 */
#include
using namespace std;
class numbered
{
public:
int mysn;
numbered(numbered& nd)
{
mysn = rand() % 100;
}
};
void f(numbered s)
{
cout << s.mysn << endl;
}
int main()
{
numbered a, b = a, c = b;
f(a);
f(b);
f(c);
system("pause");
return 0;
}
/* 13.16 */
#include
using namespace std;
class numbered
{
public:
int mysn;
numbered() { mysn = rand() % 100; }
};
void f(const numbered s)
{
cout << s.mysn << endl;
}
int main()
{
numbered a, b = a, c = b;
f(a);
f(b);
f(c);
system("pause");
return 0;
}
class Employee
{
public:
Employee() { num++; }
Employee(string n) : name(n), id (num++) { }
private:
int id;
static int num;
string name;
};
int Employee::num = 0;
需要,我们需要独一无二的证号
class Employee
{
public:
Employee() { num++; }
Employee(string n) : name(n), id (num++) { }
Employee(const Employee &ep)
{
name = ep.name;
id = num++;
}
Employee& operator=(const Employee &ep)
{
name = ep.name;
return *this;
}
private:
int id;
static int num;
string name;
};
int Employee::num = 0;
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 &hp)
{
auto newp = new string(*hp.ps);
delete ps;
ps = newp;
i = hp.i;
return *this;
}
~HasPtr() { delete ps; }
private:
string *ps;
int i;
};
未定义析构函数:内存泄漏
未定义拷贝构造函数:多次释放同一指针资源
/* 书上例子敲一遍也是极好的 */
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();
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;
}
/* a选项 */
class TreeNode
{
public:
TreeNode() : count(0), left(nullptr), right(nullptr), use(new size_t(1)) { }
TreeNode(TreeNode &tn)
{
++*tn.use;
use = tn.use;
value = tn.value;
left = tn.left;
right = tn.right;
count = tn.count;
}
TreeNode& operator=(TreeNode &tn)
{
++*tn.use;
if (--*use == 0)
{
delete use;
if (left)
delete left;
if (right)
delete right;
}
count = tn.count;
value = tn.value;
left = tn.left;
right = tn.right;
}
~TreeNode()
{
if (--*use == 0)
{
delete use;
if (left)
{
delete left;
left = nullptr;
}
if (right)
{
delete right;
right = nullptr;
}
}
}
private:
string value;
int count;
TreeNode *left;
TreeNode *right;
size_t *use;
};
/* b选项 */
class BinStrTree
{
public:
BinStrTree() : root(new TreeNode()) { }
BinStrTree(BinStrTree &bst)
{
root = new TreeNode(*bst.root);
}
BinStrTree& operator=(BinStrTree& bst)
{
auto new_root = new TreeNode(*bst.root);
delete root;
root = new_root;
return *this;
}
~BinStrTree()
{
delete root;
}
private:
TreeNode *root;
};
交换了指针,而不是指针所指的数据
void swap(HasPtr &lhs, HasPtr &rhs)
{
using std::swap;
swap(lhs.i, rhs.i);
swap(lhs.ps, rhs.ps);
}
不会,类指针的HasPtr版本不用分配动态内存