在c++中,深拷贝和浅拷贝也算是一个难点,特别是对于初学者来说,往往在不知道两者区别的情况下而错误的使用了浅拷贝,从而导致了野指针之类的问题,但是又因为缺少理解所以很难定位到问题所在。
本文纯属个人理解,欢迎各位指正。
深拷贝和浅拷贝最根本的区别在于是否真正获取一个对象的完全复制实体,而不是引用。所谓完全复制实体即是在包含指针变量的时候,不是仅仅的把指针复制一下,而是要更深一步,把指针所指的内容同样复制过来。
平常编译器自动创建的拷贝构造函数或者是拷贝赋值函数里都是浅拷贝,这种浅拷贝只是单纯的复制此类中的一些类成员变量(类成员变量应该知道什么意思吧,不知道的自行百度一下),而不会再进行深一步的拷贝,这种拷贝平常也不会出现问题,但是当此类的类成员变量有指针变量的时候或者此类的类成员变量的类成员变量中有指针变量并且包含该指针变量的类的拷贝构造函数同样是浅拷贝的情况下(这里还是挺拗口的,希望你们可以仔细看一下)就会出现野指针的问题。
我还是举个例子吧
第一种情况:此类的类成员变量有指针变量的时候
class A
{
private:
int a;
int* b;
}
第二种情况:此类的类成员变量的类成员变量中有指针变量并且包含该指针变量的类的拷贝构造函数同样是浅拷贝的情况下
class A
{
private:
int a;
int* b;
}
class B
{
private:
int b;
A a;
}
上述第一种情况,类A中包含了一个指针变量b;第二种情况的例子中,类B虽然没有包含指针变量,但是它包含了变量a,a中包含了一个指针类型的成员变量b,同时类A的赋值构造函数并没有定义,因此会是默认的浅拷贝(这里要讲一下,第二种情况的例子中:在B的对象调用拷贝构造函数时,对象内部包含的变量b和a在被拷贝时同样会调用自身的拷贝构造函数来进行拷贝)。这两种情况下都非常有可能出现野指针的问题。
下面会继续介绍为什么会出现问题,以及怎样才算是深拷贝。
怎样才算是深拷贝 以及 为什么会出现问题
下面依旧通过例子给你们介绍深拷贝和浅拷贝。
先来看浅拷贝
class A
{
private:
int a;
int* b;
}
上方类A中没有自己定义拷贝构造函数,因此当我们调用拷贝构造函数时,会调用编译器自动生成的拷贝构造函数。
你可能就要问了,默认生成的拷贝构造函数会进行哪些操作呢?
比如:
class A
{
private:
int a;
int* b;
};
int main()
{
A a1;
A a2(a1);
}
上述代码第11行中即进行了一次拷贝构造函数的调用,同时因为没有自己定义拷贝构造函数,所以会使用系统默认的浅拷贝。
具体的操作有:
首先,将a1的a变量拷贝到a2的a变量中,这一步没有什么问题。
其次,会将a1的b变量的值拷贝到a2的b变量中,注意:这里只是将值拷贝到a2,指针变量的值代表的是此指针所指向的变量的内存地址,因此,拷贝完之后的a1和a2中的指针变量b同时指向同一个内存地址。
上述就是浅拷贝。
为什么会出现问题
看到这里你可能就要意识的问题了吧。
当两个指针同时指向同一块内存地址时,轻则:a1中的b指针对变量进行改变后,当使用a2中的b指针访问时,变量已经被a1改变,会引发一些意想不到的问题;重则:当a1将指针变量b释放后,a2若再使用指针变量b则会引发野指针问题,即访问非法内存地址。
再看深拷贝
如何实现深拷贝呢?
首先要知道,要实现深拷贝,需要自己定义拷贝构造函数,同时在实现函数时要对指针变量进行深层次的拷贝,即去拷贝指针所指向的位置的变量。
class A
{
private:
int a;
int* b;
public:
A(const A& c)
{
a=c.a;
int d=*c.b;
b=&d;
}
};
如上述代码,再进行拷贝构造时,会复制一个指针b所指向变量的复制体d,然后再将本身的指针变量b指向此复制体d,如此即为深拷贝。
进一步思考,上述所讲的第二种情况:
class A
{
private:
int a;
int* b;
public:
A(const A& c)
{
a=c.a;
int d=*c.b;
b=&d;
}
};
class B
{
private:
int b;
A a;
}
上述代码中类A定义了拷贝构造函数,同时是深拷贝因此不会出现野指针的问题,而如果类A没有实现深拷贝,同样会出现问题。
思考一下吧。
本篇到此为止,转载请标明出处