文章目的
案例
这里还是Date类为例
class Date
{
public:
//无参构造(利用了缺省参数)
Date(size_t year = 2023, size_t month = 10, size_t day = 22)
{
_year = year;
_month = month;
_day = day;
}
//可省略,因为全是内置类型
Date(const Date& d)
{
cout << "Date(Date& d)拷贝构造" << endl;
_year = d._year;
_month = d._month;
_day = d._day;
}
~Date()
{
_year = 0;
_month = 0;
_day = 0;
};
private:
size_t _year;
size_t _month;
size_t _day;
};
class Stack
{
public:
//构造函数...
~Stack()
{
free(_ptr);
_ptr = nullptr;
}
private:
int* _ptr;
int _top;
int _capacity;
}
说明这几个构造函数为什么
Date(const Date& d)
拷贝构造函数
拷贝构造函数的主要用途:为了解决两个指针指向同一片地址导致二次析构
对于这里的日期类可以不写拷贝构造函数,只依靠值拷贝(浅拷贝就能完成)。
对于上述给出的Stack类就要有自己的拷贝构造,如Stack st2(st1)
.将st1
赋值给st2
,如果还依靠先前的浅拷贝,就会导致st1._ptr
和st2._ptr
指向同一片内存地址,当程序结束(或除了各自当前的作用域时)各自调用析构函数,由于两个对象中的_ptr
指向同一片内存地址,无论谁现调用了析构函数,剩下的那个对象在调用析构函数时因为无法对同一片内存空间free()
两次,所以就出了问题。
那么问题就出来了,到底还是析构函数中的free()
导致的,可是对于指针变量我们不能不写,否则会浪费很大资源,因此,针对这种情况产生了构造函数(深拷贝),构造函数出现后,st1._ptr
和st2._ptr
不在指向同一片空间,后者不仅复制前者的资源,还会根据前者的大小自己开辟地址存放复制过来的资源。
1.格式 为:className (className& object)
因此对于Date(Date d)
这种写法首先在语法上就不合格,编译器直接报错,无法通过编译。其次这种写法在调用时会引起无穷递归。这里无法通过编译,所以我没法演示。
2.为什么参数要加引用:
这主要是引用可以防止无穷递归,避免上述那种情况
3.为什么要加const
Date( Date& d)
{
cout << "Date(Date& d)拷贝构造" << endl;
d._year = _year; //修改处
_month = d._month;
_day = d._day;
}
这里把原来`_year = d._year;`
改成d._year = _year; //修改处
如此一来不仅没改变*this,反而改变了传进来的参数,偷鸡不成蚀把米;
为了防止这种情况加了const,用以保护参数d
4.操作符、函数重载返回值要不要加引用
加还是不加的根据:加了就在出作用域时少一次拷贝,不加,在返回值那里会调用一次拷贝构造,以保留结果。
看下面两个例子感受一下
Date& operator+=(int day)
{
//...具体实现
return *this;
}
反回值类型为Date& 因为+=返回左操作数,即d1,而d1在main()中,所以除了此函数d1仍然存在,不需要拷贝。
倘若这里反回值类型为Date,那么在最后代码走到return tmp;先调用拷贝构造将*this传回main(),再调用析构函数,与上述相比回多一次拷贝构造的调用,也许在这里不明显,倘若遇到数据庞大、结构复杂的多一次拷贝就多浪费许多资源。
Date operator+(int day)
{
Date tmp(*this);
//...具体实现
return tmp;
}
反回值类型为Date, 因为+=返回新的对象,而这个新对象在此函数中产生,所以需要调用拷贝构造以保留。
倘若这里反回值类型为Date&,那么在最后代码走到return tmp;直接调用析构函数,main()中的d1没有任何改变。
Date d1;
d1+=20;
Date d2 = d1+10;
先到这里,有点乱,后续更新