一、深拷贝与浅拷贝

说得简单点,假设一个类有指针成员,如果在拷贝的时候顺带连指针指向的内存也分配了,就称为深拷贝;如果只是分配指针本身的内存,那就是浅拷贝。浅拷贝造成的问题是有两个指针指向同块内存,delete 其中一个指针,那么剩下的指针将成为野指针。编译器合成的默认拷贝构造函数和赋值运算符是浅拷贝的,如果只是普通成员的赋值,浅拷贝也是可以的。

 C++ Code 
1
2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

 
#ifndef _STRING_H_


#define _STRING_H_

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_;

};

#endif 
// _STRING_H_

 C++ Code 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

 
#include 
"String.h"


//#include <string.h>

#include <cstring>


#include <iostream>


using 
namespace std;

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) + 
;

    
char *tmp = 
new 
char[len];

    memset(tmp, 
, len);

    strcpy(tmp, str);

    
return tmp;

}

void String::Display()

{

    cout << str_ << endl;

}

 C++ Code 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

 
#include 
"String.h"

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 
;

}

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

二、空类

空类默认产生的成员:

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

 C++ Code 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

 
#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 
;

}

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

从零开始学C++之构造函数与析构函数(三):深拷贝与浅拷贝、空类的更多相关文章

  1. 从零开始学C++之构造函数与析构函数(一):构造函数、析构函数、赋值与初始化、explicit关键字

    一.构造函数.默认构造函数 (1).构造函数 构造函数是特殊的成员函数 创建类类型的新对象,系统自动会调用构造函数 构造函数是为了保证对象的每个数据成员都被正确初始化 函数名和类名完全相同 不能定义构 ...

  2. 从零开始学C++之构造函数与析构函数(二):初始化列表(const和引用成员)、拷贝构造函数

    一.构造函数初始化列表 推荐在构造函数初始化列表中进行初始化 构造函数的执行分为两个阶段 初始化段 普通计算段 (一).对象成员及其初始化  C++ Code  1 2 3 4 5 6 7 8 9 1 ...

  3. C++基础-4-封装(构造函数与析构函数,深拷贝与浅拷贝,静态成员,this,友元,const修饰成员函数)

    4. 封装 4.1.1 封装的意义 1 #include<iostream> 2 #include<string> 3 using namespace std; 4 5 con ...

  4. 从零开始学C++之运算符重载(三):完善String类([]、 +、 += 运算符重载)、>>和<<运算符重载

    在前面文章中使用过几次String类的例子,现在多重载几个运算符,更加完善一下,并且重载流类运算符. []运算符重载 +运算符重载 +=运算符重载 <<运算符重载 >>运算符重 ...

  5. 从零开始学 Web 之 JS 高级(三)apply与call,bind,闭包和沙箱

    大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...

  6. 从零开始学 Web 之 移动Web(三)Zepto

    大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...

  7. 从零开始学 Web 之 Vue.js(三)Vue实例的生命周期

    大家好,这里是「 从零开始学 Web 系列教程 」,并在下列地址同步更新...... github:https://github.com/Daotin/Web 微信公众号:Web前端之巅 博客园:ht ...

  8. C++ Primer笔记9_构造函数_拷贝构造(深拷贝与浅拷贝)

    1.构造函数: >构造函数是一个特殊的.与类同名的成员函数,用于给每一个成员设置适当的初始值. >构造函数不能有返回值,函数名与类名同样. >缺省构造函数时,系统将自己主动调用该缺省 ...

  9. 从零开始学安全(四十四)●TCP三次握手四次挥手

    wireshark:Beyond Compare是一个网络封包分析软件.网络封包分析软件的功能是撷取网络封包,并尽可能显示出最为详细的网络封包资料.Wireshark使用WinPCAP作为接口,直接与 ...

随机推荐

  1. jquery 部分效果

    $(selector).hide()            隐藏被选元素 $(selector).show()           显示被选元素 $(selector).toggle()        ...

  2. ubuntu忘记密码,无法sudo的解决方法

    想要安装一个sublime Text Editor,发现需要root权限,而且sudo用户的密码输进去没有作用!@ubuntu 14.04 LTS 这个时候怎么办呢? 打开终端,在终端中使用 sudo ...

  3. css-fixed兼容写法

    解决IE6中fixed闪动问题(效果稍微好一点,不能完全解决闪动问题) *{background-image:url(about:blank);background-attachment:fixed; ...

  4. 四个漂亮的CSS样式表

    1. 单像素边框CSS表格 这是一个非经常常使用的表格样式. 源码: <!-- CSS goes in the document HEAD or added to your external s ...

  5. Web学习-apache视图log刊物

    视图apache刊物 apache日志位置 不同的系统位置不同. widnows 假如是windows的话,xampp下应该是都存在的,直接去找apache的folder/log/access.log ...

  6. 【转】Android的Merge讲解与实例

    原文:http://blog.sina.com.cn/s/blog_62f987620100sf13.html 单独将<merge />标签做个介绍,是因为它在优化UI结构时起到很重要的作 ...

  7. 关于安装Redmine服务启动和邮件设置

    关于安装Redmine服务启动和邮件设置 分类: Redmine2009-06-01 10:37 5658人阅读 评论(0) 收藏 举报 authentication邮件服务器serviceexcha ...

  8. Introducing ASP.NET vNext and MVC 6

    [译]Introducing ASP.NET vNext and MVC 6 原文:http://www.infoq.com/news/2014/05/ASP.NET-vNext?utm_source ...

  9. Object Pool

    设计模式之美:Object Pool(对象池)   索引 意图 结构 参与者 适用性 效果 相关模式 实现 实现方式(一):实现 DatabaseConnectionPool 类. 意图 运用对象池化 ...

  10. wcf跨机器访问的问题

    wcf跨机器访问的问题 在wcf跨机器的访问中遇到了各种无法访问的问题,本人也是在通过个人解决问题的基础上发表一下自己的经验,如果还有其他方面可能影响wcf跨机器的问题,还希望大家多多发言! 好了废话 ...