01
拷贝构造函数
class A{
private:
int value;
public:
A(int n){ value = n; }
A(A other){ value = other.value; }
void Print() {cout<<value<<endl; }
};
int main(void)
{
A a = 10;
A b = a;
b.Print();
return 0;
}
该代码编译运行结果为编译错误,因为其拷贝构造函数会存在一个无限递归调用的错误。
拷贝构造函数A(A other)由于是值传递,在把形参复制到实参的过程中会调用拷贝构造函数,所以如果允许传值的话,就会在拷贝构造函数里面调用拷贝构造函数,造成无限递归调用。
C++的标准不允许复制构造函数传值参数,只能将构造函数修改为A(const A& other),也就是把传值参数改为常量引用。(注意:传指针也是不可以的,只能改为引用)。
例:
A object1 = 10;
A object2 = object1 ;
第二步相当于将object1当作参数传给object1.A(A other),也就是 A other = object1,但是other是未初始化的,所以势必又要用object1来初始化other,即把object1作为参数传递给other.A(A other),后面就是陷入无限递归调用的情形。
参考地址:https://blog.youkuaiyun.com/xiaoquantouer/article/details/70145348?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-2.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-2.control
1.对于一个类X,如果一个构造函数的第一个参数是下列之一:
a) X&
b) const X&
c) volatile X&
d) const volatile X&
且没有其他参数或其他参数都有默认值,那么这个函数是拷贝构造函数.
2.类中可以存在超过一个拷贝构造函数, 如果一个类中没有定义拷贝构造函数,那么编译器会自动产生一个默认的拷贝构造函数.
这个默认的参数可能为X::X(const X&)或X::X(X&),由编译器根据上下文决定选择哪一个.
默认拷贝构造函数的行为如下:
默认的拷贝构造函数执行的顺序与其他用户定义的构造函数相同,执行先父类后子类的构造.
拷贝构造函数对类中每一个数据成员执行成员拷贝(memberwise Copy)的动作.
a)如果数据成员为某一个类的实例,那么调用此类的拷贝构造函数.
b)如果数据成员是一个数组,对数组的每一个执行按位拷贝.
c)如果数据成员是一个数量,如int,double,那么调用系统内建的赋值运算符对其进行赋值.
深拷贝和浅拷贝
java
浅拷贝基础类型修改不影响,引用类型拷贝后还是指向同一个对象,实现Cloneable接口覆写clone()方法。
深拷贝对于引用类型会另外开辟空间,不与原对象一致,需要在引用类型的对象实现Cloneable接口覆写clone()方法。
c++
浅拷贝会把指针变量的地址复制; 深拷贝会重新开辟内存空间。
浅拷贝的时候注意重复释放空间,可能会释放空的内存地址导致错误。
运算符重载
返回值类型是否声明为该类型的引用,并在函数结束前返回实例自身的引用,如*this。因为只有返回一个引用才能做连续赋值操作,如str1=str2=str3。
是否将传入的参数设置成了常量引用,这样不仅可以减少一次拷贝构造函数的调用也能避免对传入参数的修改。
是否释放之前实例已有的内存。如何释放(手动、析构),别重复释放。
是否判断传入实例和自身是否是一个实例,若不判断就操作,在释放的时候就一起释放了。
CMyString& CMyString::operator = (const CMyString &str) {
if (this != &str) {
CMyString strTemp(str);
char* pTemp = strTemp.m_pData;
strTemp.m_pData = m_pData;
m_pData = pTemp;
}
}
判断是否自身,利用临时变量来存储当前实例之前指向的地址,在临时变量释放的时候释放该地址,减少new delete操作和可能的空间不足错误。