深拷贝与浅拷贝、空类与空数组

C++ Primer 学习笔记_20_类与数据抽象(6)_深拷贝与浅拷贝、空类与空数组

一、深拷贝与浅拷贝

浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。换而言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象。

深拷贝:被复制对象的所有变量都含有与原来的对象相同的值,除去那些引用其他对象的变量。那些引用其他对象的变量将指向被复制过的新对象,而不再是原有的那些被引用的对象。换而言之,深拷贝把要复制的对象所引用的对象都复制了一遍。

浅拷贝可能会导致运行时错误,特别时在对象的创建与删除过程中。

说得简单点,,假设一个类有指针成员,如果只是分配指针本身的内存,那就是浅拷贝,如下图。如果在拷贝的时候顺带连指针指向的内存也分配了,就称为深拷贝,如下图(t 从 s 拷贝而来)。

【举个例子】

struct Test{char *ptr;};void shallow_copy(Test& src, Test& dest){dest.ptr = src.ptr;}void deep_copy(Test& src, Test& dest){dest.ptr = malloc(strlen(src.ptr) + 1);memcpy(dest.ptr, src.ptr);}

浅拷贝造成的问题是有两个指针指向同块内存,delete 其中一个指针,那么剩下的指针将成为野指针。编译器合成的默认拷贝构造函数和赋值运算符是浅拷贝的,如果只是普通成员的赋值,浅拷贝也是可以的。

#include <string.h>#include <cstring>#include <iostream>using namespace std;class String{public:String( char *str = "");~String();String( const String &other);String & operator=( const String &other);void Display();private:char *AllocAndCpy( char *str);char *str_;};String::String( char *str /* = */){str_ = AllocAndCpy(str);}String::~String(){delete[] str_;}String::String( const String &other){str_ = AllocAndCpy(other.str_);}String &String:: operator =( const String &other){if ( this == &other)return * this;delete[] str_;str_ = AllocAndCpy(other.str_);return * this;}char *String::AllocAndCpy( char *str){int len = strlen(str) + 1;char *tmp = new char[len];memset(tmp, 0, len);strcpy(tmp, str);return tmp;}void String::Display(){cout << str_ << endl;}int main( void){String s1( "AAA");s1.Display();String s2 = s1;// 调用拷贝构造函数// 系统提供的默认拷贝构造函数实施的是浅拷贝 s2.str_ = s1.str_String s3;s3.Display();s3 = s2;// 调用等号运算符// 系统提供的默认等号运算符实施的是浅拷贝 s3.str_ = s2.str_;// s3.operator=(s2);s3.Display();// 要让对象是独一无二的,我们要禁止拷贝// 方法是将拷贝构造函数与=运算符声明为私有,并且不提供它们的实现return 0;}

运行结果:

AAA

AAA

解释:上面程序中String 类有一个char* str_ 成员,故实现了深拷贝,这样不会造成内存被释放两次的错误,或者修改指针指向的内存会影响另一个对象的错误。此外,如果我们想让对象是独一无二的,需要禁 止拷贝,只需要将拷贝构造函数和等号运算符声明为私有,并且不提供它们的实现。

注意:在编写派生类的赋值函数时,不要忘记对基类的数据成员重新赋值,可以通过调用基类的赋值函数来实现,比如在

Derived& Derived::operator=(const Derived& other) { } 中调用Base::operator=(other);

二、空类与空数组

空类默认产生的成员:

class Empty {};Empty(); // 默认构造函数Empty( const Empty& );// 默认拷贝构造函数~Empty(); // 默认析构函数Empty& operator=( const Empty& ); // 默认赋值运算符Empty* operator&();// 取址运算符const Empty* operator&() const; // 取址运算符 const

【例子】#include <iostream>using namespace std;class Empty{public:Empty * operator&(){cout << "AAAA" << endl;return this;}const Empty * operator&() const{cout << "BBBB" << endl;return this;}};int main( void){Empty e;Empty *p = &e;// 等价于e.operator&();const Empty e2;const Empty *p2 = &e2;cout << sizeof(Empty) << endl;return 0;}

运行结果:

AAAABBBB1

解释:可以看到分别调用了两个取地址运算符函数,而且空类的大小为1个字节。

【例子】

体会一下下面的程序结果:

最糟糕的行为是抱怨,最易见效 的努力是从自己做起。

深拷贝与浅拷贝、空类与空数组

相关文章:

你感兴趣的文章:

标签云: