大多数类应定义复制构造函数和默认构造函数
不定义复制构造函数和/或默认构造函数,会严重局限类的使用:不允许复制的类对象只能作为引用传递给函数或从函数返回,它们也不能用作容器的元素。
一般来说,最好显式或隐式定义默认构造函数和复制构造函数。只有不存在其他构造函数时才合成默认构造函数。如果定义了复制构造函数,也必须定义默认构造函数。
二、赋值操作符
与复制构造函数一样,如果类没有定义自己的赋值操作符,则编译器会合成一个!
1、介绍重载赋值
重载操作符是一些函数,其名字为operator后跟着所定义的操作符的符号。因此,通过定义名为operator=的函数,我们可以对赋值进行定义。像任何其他函数一样,操作符函数有一个返回值和一个形参表。形参表必须具有与该操作符数目相同的形参(如果操作符是一个类成员,则包括隐式this形参)。赋值是二元运算,所以该操作符函数有两个形参:第一个形参对应着左操作数,第二个形参对应右操作数。
大多数操作符可以定义为成员函数或非成员函数。当操作符为成员函数时,它的第一个操作数隐式绑定到this指针。有些操作符(包括赋值操作符)必须是定义自己的类的成员。因为赋值必须是类的成员,所以this绑定到指向左操作数的指针。因此,赋值操作符接受单个形参,且该形参是同一类类型的对象。右操作数一般作为const引用传递。
赋值操作符也返回对同一类类型的引用。
class Sales_item
{
public:
Sales_item &operator=(const Sales_item &);
};
2、合成赋值操作符
合成赋值操作符会执行逐个成员赋值:右操作数对象的每个成员赋值给左操作数对象的对应成员。除数组之外,每个成员用所属类型的常规方式进行赋值。对于数组,给每个数组元素赋值。如:
Sales_item &Sales_item::operator=(const Sales_item &rhs)
{
isbn = rhs.isbn;
units_sold = rhs.units_sold;
revenue = rhs.revenue;
return *this;
}
3、复制和赋值常一起使用
实际上,应该将复制构造函数和赋值操作符看做一个单元,如果需要其中一个,我们几乎也可以肯定需要另一个!
//P412 习题13.9
NoName &NoName::operator=(const NoName &rhs)
{
if (pstring)
{
delete pstring;
}
pstring = new string();
*pstring = *(rhs.pstring);
i = rhs.i;
d = rhs.d;
return *this;
}
//习题13.10
class Employee
{
public:
typedef unsigned int num_type;
Employee(const std::string Name = "NoName"):name(Name),mark(count)
{
set();
}
Employee(const Employee &rhs):name(rhs.name),mark(count)
{
set();
}
~Employee()
{
-- count;
}
Employee &operator=(const Employee &rhs)
{
name = rhs.name;
return *this;
}
ostream &output(ostream &os)
{
os << "Name: " << name << "\t\tMark: " << mark << endl;
return os;
}
private:
std::string name;
num_type mark;
static num_type count;
void set()
{
++ count;
}
};
Employee::num_type Employee::count = 0;