从上篇博客拷贝构造我们知道拷贝构造要传引用而不能传值,否则会发生无穷递归的情况。
1.若未显示定义拷贝构造函数,系统会生成默认的拷贝构造函数。默认的拷贝构造按内存序完成拷贝,我们称之为浅拷贝(值拷贝)。注:对内置类型按照字节方式拷贝,而自定义类型调用其的拷贝构造函数。
2.浅拷贝就是新拷贝的对象和原对象指向同一块空间,新拷贝的对象的值的改变也会引发原对象的值的改变。
3.深拷贝实质是在内存里重新为新拷贝的对象开辟一块空间,不指向同一块空间。
我们拷贝日期这样的类用浅拷贝是没问题的,年月日都是在栈上创建销毁由系统开空间的释放空间的,但是如果这样的类呢?
class String
{
public:
String(const char* str = "jack")
{
_str = (char*)malloc(strlen(str) + 1);
strcpy(_str, str);
}
~String()
{
cout << "~String()" << endl;
free(_str);
}
private:
char* _str;
};
int main()
{
String s1("hello");
String s2(s1);
}
_str是我们malloc出来在堆上的空间需要我们自己调用析构free掉,s2拷贝构造s1,编译器自动生成的默认拷贝函数会使s2和s1指向同一块空间,先定义的对象先析构,s1先析构掉这块开好的空间,s2再次析构时这块空间已经被释放掉了,程序报错。
C++为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似
函数名字为:关键字operator后面接需要重载的运算符符号。
函数原型:返回值类型 operator操作符(参数列表)
不能通过连接其他符号来创建新的操作符:比如operator@
重载操作符必须有一个类类型参数
用于内置类型的运算符,其含义不能改变,例如:内置的整型+,不 能改变其含义
作为类成员函数重载时,其形参看起来比操作数数目少1,因为成员函数的第一个参数为隐藏的this
.* :: sizeof ?: . 注意以上5个运算符不能重载。这个经常在笔试选择题中出现。
例如:以下在.h中声明,再cpp中实现的>运算符
bool Date::operator>(const Date& d)const
{
return (_year > d._year)
|| ((_year == d._year) && (_month > d._month))
|| (_year == d._year) && (_month == d._month) && (_day > d._day);
}
写一个>和==就足够了,剩下的代码复用就行.
赋值运算符主要注意以下几点
1.参数类型:const T&,传递引用可以提高传参效率
2.返回值类型:T&,返回引用可以提高返回的效率,有返回值目的是为了支持连续赋值
3.检测是否自己给自己赋值
4.返回*this :要符合连续赋值的含义
//d1=d3
Date& operator=(const Date& d)//引用作返回值,不用调用拷贝构造存临时变量
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
原因:赋值运算符如果不显式实现,编译器会生成一个默认的。此时用户再在类外自己实现一个全局的赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的成员数。
用户没有显式实现时,编译器会生成一个默认赋值运算符重载,以值的方式逐字节拷贝。注意:内置类型成员变量是直接赋值的,而自定义类型成员变量需要调用对应类的赋值运算符重载完成赋值。
同上浅拷贝和深拷贝的问题
Date& Date::operator++(int)
{
Date ret(*this);
*this += 1;
return ret;
}
Date& Date::operator++()
{
*this += 1;
return *this;
}
前置++先加加后使用,后置++先使用再加加,为了区分C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传递。