C++浅拷贝和深拷贝
在C++编程中,对象的拷贝是一项常见的操作。深拷贝和浅拷贝是两种常用的拷贝方式,对于理解对象拷贝的内部机制和避免潜在的问题至关重要。本文将深入解析C++中的深拷贝和浅拷贝的概念、原理以及使用场景,帮助读者更好地掌握和运用这两种拷贝方式。
浅拷贝(Shallow Copy)是指在拷贝对象时,只是复制了对象中的成员变量的值的引用或指针。浅拷贝后的对象和原对象共享一份数据,修改一个对象可能会影响另一个对象。
深拷贝(Deep Copy)是指在拷贝对象时,会创建一个新的独立的对象,并复制原对象中的所有成员变量的值。深拷贝后的对象和原对象是完全独立的,修改一个对象不会影响另一个对象。
深拷贝的实现
复制构造函数(Copy Constructor):深拷贝通常通过复制构造函数来实现。复制构造函数是一种特殊的构造函数,用于在创建对象时进行拷贝初始化。在复制构造函数中,通过分配新的内存空间,并将原对象的值复制到新对象中,实现对象的深拷贝。
浅拷贝的实现
默认拷贝构造函数:如果没有显式定义复制构造函数,C++会提供一个默认的复制构造函数,它执行的是浅拷贝。默认的复制构造函数将简单地将原对象的成员变量的值复制给新对象,导致新对象和原对象共享相同的数据。
接下来我们通过一个例子来理解
#include<iostream> #include<string.h> using namespace std; class MyString{ private: char *buffer; public: // 构造函数 // const char * 指向字符串的常量指针 在内存中是常量不可以修改 MyString(const char * initstring){ buffer=NULL; cout << "constructor mystring" << endl; if(initstring != NULL){ // 动态分配内存 buffer = new char[strlen(initstring)+1]; strcpy(buffer,initstring); // 为新对象分配新的内存,并复制原始对象的所有数据到这个新内存中 cout << (unsigned int*)buffer << endl; } else{ buffer = NULL; } } //复制构造函数 MyString(const MyString ©){ buffer = NULL; cout << "copy" << endl; if (copy.buffer != NULL){ buffer = new char[strlen(copy.buffer)+1]; strcpy(buffer,copy.buffer); cout << (unsigned int*)buffer << endl; } }
// 析构函数 ~MyString(){ cout << "delete!" << endl; if (buffer!=NULL){ delete [] buffer; } } int Getlength(){ return strlen(buffer); } const char* GetString(){ return buffer; } }; void UseString(MyString str){ cout << "usestring string is:" << str.GetString() << endl; cout << "buffer is:" << str.Getlength() << endl; } int main(){ MyString sayHello("Hello from string class"); UseString(sayHello); }
浅拷贝的问题,在Usestring中,对象sayHello被复制到形参str。UseString在传递时,被声明为按值传递(非引用传递),此时编译器执行二进制复制(整型、字符和原始指针等POD数据),此时sayHello.buffer包含的指针被复制到str中。二进制复制不复制指向的内存单元,因此sayHello和str指向同一个内存单元,当UseString()返回,形参str不在作用域,因此被销毁。当Mystring类调用析构函数的delete[]销毁给buffer的内存时,会显示sayHello的内存指向无效。
深拷贝解决,使用复制构造函数实现深拷贝,复制构造函数用于创建一个对象的副本,通过一个已经存在的对象来初始化一个新的对象。sayHello和str不指向同一个内存单元,当UseString()返回,形参str被销毁时,析构函数对复制构造函数分配的内存地址调用delete[],没有影响sayHello的内存。