拷贝构造函数关于指针的拷贝问题

1.拷贝构造函数的定义:若是一个构造函数的第一个参数是自身类型的引用,且任何额外参数都有默认值。html

!拷贝构造函数的第一个参数必须是一个引用类型。
合成拷贝构造函数:
若是咱们没有为一个类定义拷贝构造函数,编译器会为咱们定义一个。编译器从给定对象中,依次将每一个非static成员拷贝到正在建立的对象中。

2.拷贝初始化:
拷贝初始化不只在咱们使用=定义变量时会发生,在下列状况下也会:
  • 将一个对象做为实参,传递给非引用类型的形参
  • 从一个返回类型为非引用类型的函数,返回一个对象
  • 用花括号列表初始化一个数组中的元素,或一个聚合类的成员
拷贝构造函数本身的参数必须是引用类型,若是其参数不是引用类型,则调用永远也不会成功——为了调用拷贝构造函数,咱们必须拷贝它的实参,但为了拷贝实参,咱们又必须调用拷贝构造函数,如此无限循环。

拷贝构造函数在用类的一个对象去初始化该类的另外一个对象时调用,如下三种状况至关于用一个已存在的对象去初始化新创建的对象,此时,调用拷贝构造函数:
1.当用类的一个对象去初始化该类的另外一个对象时。
2.若是函数的形参是类的对象,调用函数时,将对象做为函数实参传递给函数的形参时。
3.若是函数的返回值是类的对象,函数执行完成时,将返回值返回。

注意:
1>拷贝构造函数只是在用一个已存在的对象去初始化新创建的对象时调用,而对象进行赋值时,拷贝函数将不被调用。
2>用一个常量初始化新创建的对象时,调用构造函数,不调用拷贝构造函数
3>创建对象时,构造函数与拷贝构造函数只有一个被调用
4> 当对象做为函数的返回值时须要调用拷贝构造函数,此时C++将从堆中动态创建一个临时对象,将函数返回的对象拷贝给该临时对象,并把该临时对象的地址存储在寄存器里,从而由该临时对象完成函数返回值的传递

3.浅拷贝与深拷贝
默认的拷贝构造函数实现的只能是浅拷贝,即直接将原对象的数据成员值依次复制给新对象中对应的数据成员,并无为新对象另外分配内存资源。
这样,若是对象的数据成员是指针,两个指针对象实际上指向的是同一块内存空间。
在某些状况下,浅拷贝回带来数据安全方面的隐患。
当类的数据成员中有指针类型时,咱们就必须定义一个特定的拷贝构造函数,该拷贝构造函数不只能够实现原对象和新对象之间数据成员的拷贝,并且能够为新的对象分配单独的内存资源,这就是深拷贝构造函数。

浅拷贝存在的问题:
#include<iostream>

using namespace std;

class Rect
{
public:
    Rect()
    {
     p=new int(100);
    }
   
    ~Rect()
    {
     if(p!=NULL);
        delete p;
    }

private:
    int width;
    int height;
    int *p;
};
int main()
{
    Rect rect1;
    Rect rect2(rect1);
    return 0;
}
程序运行时会发生错误,在销毁对象时,两个对象的构造函数将对同一个内存空间释放两次。
因为合成的默认拷贝构造函数是浅拷贝,在使用rect1复制rect2时,因为执行的是浅拷贝,只是将成员的值进行赋值,这时 rect1.p = rect2.p,也即这两个指针指向了堆里的同一个空间。
因此必需要用深拷贝,本身定义拷贝构造函数:
#include<iostream>
using namespace std;
class Rect
{
public:
Rect()
{
	p=new int(100);
}
Rect(const Rect& r)
{
	width=r.width;
	height=r.height;
	p=new int(100);
	*p=*(r.p);
}
~Rect()
{
	if(p!=NULL)
	delete p;
}
private:
int width;
int height;
int *p;
};

3.防止默认拷贝发生
声明一个私有的拷贝构造函数,这样由于拷贝构造函数是私有的,若是用户试图按值传递或函数返回该类的对象,编译器会报告错误,从而能够避免按值传递或返回对象。
总结:
当出现类的等号赋值时,会调用拷贝函数,在未定义显示拷贝构造函数的状况下,系统会调用默认的拷贝函数 —— 即浅拷贝,它可以完成成员的一一复制。当数据成员中没有指针时,浅拷贝是可行的。但当数据成员中有指针时,若是采用简单的浅拷贝,则两类中的两个指针将指向同一个地址,当对象快结束时,会调用两次析构函数,而致使指针悬挂现象。因此,这时,必须采用深拷贝。

     深拷贝与浅拷贝的区别就在于深拷贝会在堆内存中另外申请空间来储存数据,从而也就解决了指针悬挂的问题。简而言之,当数据成员中有指针时,必需要用深拷贝ios


参考博客:http://blog.chinaunix.net/uid-28977986-id-3977861.html数组