由浅拷贝讨论到深拷贝再讨论到接口(一):浅拷贝和深拷贝

接口ICloneable为我们实现了拷贝的梦想。

(一)如何实现浅拷贝?

新建学校对象(School),实现接口ICloneable,如果我们这样写,即完成了浅拷贝:return base.MemberwiseClone();

    public class School : ICloneable    {        public object Clone()        {            return base.MemberwiseClone();        }    }

完整代码:

代码

1 using System;
2
3  namespace NY
4 {
5 class Program
6 {
7 staticvoid Main(string[] args)
8 {
9 School a =new School();
10 a.Id =1;
11 a.Name ="A school";
12 a.Student.Name ="Li lei";
13
14 Console.WriteLine("-----------a对象原始值-----------");
15 Console.WriteLine("a.Id:"+ a.Id);
16 Console.WriteLine("a.Name:"+ a.Name);
17 Console.WriteLine("a.Student.Name:"+ a.Student.Name);
18
19 School b = (School)a.Clone();
20
21 Console.WriteLine("-----------a对象当前值-----------");
22 Console.WriteLine("a.Id:"+ a.Id);
23 Console.WriteLine("a.Name:"+ a.Name);
24 Console.WriteLine("a.Student.Name:"+ a.Student.Name);
25 Console.WriteLine("-----------b对象当前值-----------");
26 Console.WriteLine("b.Id:"+ b.Id);
27 Console.WriteLine("b.Name:"+ b.Name);
28 Console.WriteLine("b.Student.Name:"+ b.Student.Name);
29 }
30 }
31
32 publicclass School : ICloneable
33 {
34 privateint _id =0;
35 privatestring _name =string.Empty;
36 private Student _student =new Student();
37
38 publicint Id
39 {
40 get { return _id; }
41 set { _id = value; }
42 }
43 publicstring Name
44 {
45 get { return _name; }
46 set { _name = value; }
47 }
48 public Student Student
49 {
50 get { return _student; }
51 set { _student = value; }
52 }
53
54 publicobject Clone()
55 {
56 returnbase.MemberwiseClone();
57 }
58 }
59
60 publicclass Student : ICloneable
61 {
62 privatestring _name =string.Empty;
63
64 publicstring Name
65 {
66 get { return _name; }
67 set { _name = value; }
68 }
69
70 publicobject Clone()
71 {
72 returnbase.MemberwiseClone();
73 }
74 }
75 }
76

结果如下:

结论:

(1)  我们只用了一句话,即轻松地将a对象的值拷贝给了b对象:School b = (School)a.Clone();

(二)值类型--浅拷贝会创建副本

对于值类型int,我们把b对象Id的值修改为2(20行),看看会有什么结果

代码

1 using System;
2
3  namespace NY
4 {
5 class Program
6 {
7 staticvoid Main(string[] args)
8 {
9 School a =new School();
10 a.Id =1;
11 a.Name ="A school";
12 a.Student.Name ="Li lei";
13
14 Console.WriteLine("-----------a对象原始值-----------");
15 Console.WriteLine("a.Id:"+ a.Id);
16 Console.WriteLine("a.Name:"+ a.Name);
17 Console.WriteLine("a.Student.Name:"+ a.Student.Name);
18
19 School b = (School)a.Clone();
20 b.Id =2;
21
22 Console.WriteLine("-----------a对象当前值-----------");
23 Console.WriteLine("a.Id:"+ a.Id);
24 Console.WriteLine("a.Name:"+ a.Name);
25 Console.WriteLine("a.Student.Name:"+ a.Student.Name);
26 Console.WriteLine("-----------b对象当前值-----------");
27 Console.WriteLine("b.Id:"+ b.Id);
28 Console.WriteLine("b.Name:"+ b.Name);
29 Console.WriteLine("b.Student.Name:"+ b.Student.Name);
30 }
31 }
32
33 publicclass School : ICloneable
34 {
35 privateint _id =0;
36 privatestring _name =string.Empty;
37 private Student _student =new Student();
38
39 publicint Id
40 {
41 get { return _id; }
42 set { _id = value; }
43 }
44 publicstring Name
45 {
46 get { return _name; }
47 set { _name = value; }
48 }
49 public Student Student
50 {
51 get { return _student; }
52 set { _student = value; }
53 }
54
55 publicobject Clone()
56 {
57 returnbase.MemberwiseClone();
58 }
59 }
60
61 publicclass Student : ICloneable
62 {
63 privatestring _name =string.Empty;
64
65 publicstring Name
66 {
67 get { return _name; }
68 set { _name = value; }
69 }
70
71 publicobject Clone()
72 {
73 returnbase.MemberwiseClone();
74 }
75 }
76 }
77

结果如下:

结论:

(1)  a对象Id的值仍然为1,b对象Id的值却为2,说明对于值类型来说,浅拷贝会创建副本。

 (三)引用类型string--浅拷贝会创建副本

对于引用类型string,我们把b对象Name的值修改为b school(21行),看看会有什么结果

代码

1 using System;
2
3  namespace NY
4 {
5 class Program
6 {
7 staticvoid Main(string[] args)
8 {
9 School a =new School();
10 a.Id =1;
11 a.Name ="A school";
12 a.Student.Name ="Li lei";
13
14 Console.WriteLine("-----------a对象原始值-----------");
15 Console.WriteLine("a.Id:"+ a.Id);
16 Console.WriteLine("a.Name:"+ a.Name);
17 Console.WriteLine("a.Student.Name:"+ a.Student.Name);
18
19 School b = (School)a.Clone();
20 b.Id =2;
21 b.Name ="B school";
22
23 Console.WriteLine("-----------a对象当前值-----------");
24 Console.WriteLine("a.Id:"+ a.Id);
25 Console.WriteLine("a.Name:"+ a.Name);
26 Console.WriteLine("a.Student.Name:"+ a.Student.Name);
27 Console.WriteLine("-----------b对象当前值-----------");
28 Console.WriteLine("b.Id:"+ b.Id);
29 Console.WriteLine("b.Name:"+ b.Name);
30 Console.WriteLine("b.Student.Name:"+ b.Student.Name);
31 }
32 }
33
34 publicclass School : ICloneable
35 {
36 privateint _id =0;
37 privatestring _name =string.Empty;
38 private Student _student =new Student();
39
40 publicint Id
41 {
42 get { return _id; }
43 set { _id = value; }
44 }
45 publicstring Name
46 {
47 get { return _name; }
48 set { _name = value; }
49 }
50 public Student Student
51 {
52 get { return _student; }
53 set { _student = value; }
54 }
55
56 publicobject Clone()
57 {
58 returnbase.MemberwiseClone();
59 }
60 }
61
62 publicclass Student : ICloneable
63 {
64 privatestring _name =string.Empty;
65
66 publicstring Name
67 {
68 get { return _name; }
69 set { _name = value; }
70 }
71
72 publicobject Clone()
73 {
74 returnbase.MemberwiseClone();
75 }
76 }
77 }
78

结果如下:

结论:

(1)  a对象Name的值仍然为a school,b对象Name的值却为b school,说明对于引用类型string来说,浅拷贝会创建副本。

(四)思考:如果把小写string改成大写String呢?
这里直接给出结论:

(1)  对于大写String来说,浅拷贝会创建副本。

(2)  由于在工作中常会用的是小写string,较少用大写String,所以,关于两者差异,请见笔者另一篇文章《浅析string与String》

(五)其他引用类型--浅拷贝不会创建副本

对于其他new出来的引用类型,我们把b对象的Student的Name的值修改为Han meimei(22行),看看会有什么结果

代码

1 using System;
2
3  namespace NY
4 {
5 class Program
6 {
7 staticvoid Main(string[] args)
8 {
9 School a =new School();
10 a.Id =1;
11 a.Name ="A school";
12 a.Student.Name ="Li lei";
13
14 Console.WriteLine("-----------a对象原始值-----------");
15 Console.WriteLine("a.Id:"+ a.Id);
16 Console.WriteLine("a.Name:"+ a.Name);
17 Console.WriteLine("a.Student.Name:"+ a.Student.Name);
18
19 School b = (School)a.Clone();
20 b.Id =2;
21 b.Name ="B school";
22 b.Student.Name ="Han meimei";
23
24 Console.WriteLine("-----------a对象当前值-----------");
25 Console.WriteLine("a.Id:"+ a.Id);
26 Console.WriteLine("a.Name:"+ a.Name);
27 Console.WriteLine("a.Student.Name:"+ a.Student.Name);
28 Console.WriteLine("-----------b对象当前值-----------");
29 Console.WriteLine("b.Id:"+ b.Id);
30 Console.WriteLine("b.Name:"+ b.Name);
31 Console.WriteLine("b.Student.Name:"+ b.Student.Name);
32 }
33 }
34
35 publicclass School : ICloneable
36 {
37 privateint _id =0;
38 privatestring _name =string.Empty;
39 private Student _student =new Student();
40
41 publicint Id
42 {
43 get { return _id; }
44 set { _id = value; }
45 }
46 publicstring Name
47 {
48 get { return _name; }
49 set { _name = value; }
50 }
51 public Student Student
52 {
53 get { return _student; }
54 set { _student = value; }
55 }
56
57 publicobject Clone()
58 {
59 returnbase.MemberwiseClone();
60 }
61 }
62
63 publicclass Student : ICloneable
64 {
65 privatestring _name =string.Empty;
66
67 publicstring Name
68 {
69 get { return _name; }
70 set { _name = value; }
71 }
72
73 publicobject Clone()
74 {
75 returnbase.MemberwiseClone();
76 }
77 }
78 }
79

结果如下:

结论:

(1)  大家注意看,a.Studnet.Name也被改成Han meimei了,说明,对于引用类型,浅拷贝不会创建副本。

(六)思考:我们修改b对象的时候,不希望把a对象的值也给改了

上例的结论,也许并不是我们期望的,我们期望当b.Student.Name改成Han meimei的时候,不要改变a.Student.Name的值,因此,我们引入深拷贝的概念。

(七)如何实现深拷贝

既然ICloneable接口已经提供了Clone方法,那么,我们只需要重写这个方法,即可实现深拷贝。

publicclass School : ICloneable
{
publicobject Clone()
{
School cloned =new School();
cloned.Id =this._id;
cloned.Name =this._name;
cloned.Student = (Student)this._student.Clone();
return cloned;
}
}

完整代码:

代码

1 using System;
2
3  namespace NY
4 {
5 class Program
6 {
7 staticvoid Main(string[] args)
8 {
9 School a =new School();
10 a.Id =1;
11 a.Name ="A school";
12 a.Student.Name ="Li lei";
13
14 Console.WriteLine("-----------a对象原始值-----------");
15 Console.WriteLine("a.Id:"+ a.Id);
16 Console.WriteLine("a.Name:"+ a.Name);
17 Console.WriteLine("a.Student.Name:"+ a.Student.Name);
18
19 School b = (School)a.Clone();
20 b.Id =2;
21 b.Name ="B school";
22 b.Student.Name ="Han meimei";
23
24 Console.WriteLine("-----------a对象当前值-----------");
25 Console.WriteLine("a.Id:"+ a.Id);
26 Console.WriteLine("a.Name:"+ a.Name);
27 Console.WriteLine("a.Student.Name:"+ a.Student.Name);
28 Console.WriteLine("-----------b对象当前值-----------");
29 Console.WriteLine("b.Id:"+ b.Id);
30 Console.WriteLine("b.Name:"+ b.Name);
31 Console.WriteLine("b.Student.Name:"+ b.Student.Name);
32 }
33 }
34
35 publicclass School : ICloneable
36 {
37 privateint _id =0;
38 privatestring _name =string.Empty;
39 private Student _student =new Student();
40
41 publicint Id
42 {
43 get { return _id; }
44 set { _id = value; }
45 }
46 publicstring Name
47 {
48 get { return _name; }
49 set { _name = value; }
50 }
51 public Student Student
52 {
53 get { return _student; }
54 set { _student = value; }
55 }
56
57 publicobject Clone()
58 {
59 School cloned =new School();
60 cloned.Id =this._id;
61 cloned.Name =this._name;
62 cloned.Student = (Student)this._student.Clone();
63 return cloned;
64 }
65 }
66
67 publicclass Student : ICloneable
68 {
69 privatestring _name =string.Empty;
70
71 publicstring Name
72 {
73 get { return _name; }
74 set { _name = value; }
75 }
76
77 publicobject Clone()
78 {
79 Student cloned =new Student();
80 cloned.Name =this._name;
81 cloned.Age =this._age;
82 return cloned;
83 }
84 }
85 }
86

结果如下:

结论:

(1)  深拷贝为我们实现了对象的完整复制,彻底解决了浅拷贝对于引用类型只拷贝地址带来的问题。

(八)或许有人就要问了

常常听博客园高手说针对接口编程,但,到我自己使用的时候,想不到要用接口。比如这个例子,不设计接口,我也能实现深拷贝,比如在Framework层新写个方法MyClone(),然后这样调用School b = (School)MyClone(a);。那为什么还要去设计接口呢?给谁用呢?怎么用呢?为什么比不设计接口要好呢?

下篇博文,会有精彩解答,敬请期待!

时间: 2024-10-27 19:22:20

由浅拷贝讨论到深拷贝再讨论到接口(一):浅拷贝和深拷贝的相关文章

getch()和getchar()之再讨论

原文:getch()和getchar()之再讨论 在C语言的字符处理函数中,getch()和getchar()是经常让人迷惑的两个函数,他们都有一些“奇怪的”特点让初学者摸不着头脑.两个函数有很多相似之处,却又有很大的不同.下面是两个函数的说明: int getch( void ); Get a character from the console without echo int getchar ( void );   Get character from stdin, Returns the

Rocket - debug - DebugCustomXbar再讨论

https://mp.weixin.qq.com/s/YPFa6kE6I_Ud_MJGvzmS-g 简单讨论输入边/输出边Bundle的方向. 1. 上游节点的地址不重复 仔细看了一下sourceFn的实现: 其中要求上游节点中不存在重复的地址: 这样也就不存在decoded中存在两个元素同时为真的情况: 2. source.addr := sink.addr 在lazy module的实现中,把sink.addr输入到source.addr中: 而在DebugCustomBundle的定义中,

Scratch 3.6角色碰到边缘反弹方向再讨论

引言 在Scratch中角色的移动是按一定方向移动的,所以方向在角色的移动中很重要,特别是在绘制一些几何图案的时候,不仅要考虑好移动距离,还必须确定好移动方向.下图给出Scratch 3.X中精灵的方向规定. Scratch积木"碰到边缘就反弹" Scratch的基本积木模块"碰到边缘就反弹",其反弹遵循物理学上光线的反射规律,即"入射角等于反射角"(入射方向与法线夹角为入射角:反射方向与法线夹角为反射角).参考下图: 为了下面讨论方便,我们特别

算法初步:再讨论一点动态规划

原创 by zoe.zhang 动态规划真的很好用啊,但是需要练习,还有很多技巧要学习. 1.滚动数组 动态规划是用空间换取时间,所以通常需要为DP数组开辟很大的内存空间来存放数据,但有的时候空间太大超过内存限制,特别是在OJ的时候,容易出现MLE的问题.而在一些动规的题目中,我们可以利用滚动数组来优化空间. 适用条件:DP状态转移方程中,仅仅需要前面若干个状态,而每一次转移后,都有若干个状态不会再被用到,也就是废弃掉,此时可以使用滚动数组来减少空间消耗. 优点:节约空间. 缺点:在时间消耗上没

Tsinsen_A1024. 瓷砖问题再讨论

问题描述 有一长度为N(1<=N<=10)的地板,给定三种不同瓷砖:一种长度为1,一种长度为2,另一种长度为3,数目不限.要将这个长度为N的地板铺满,并且要求长度为1的瓷砖不能相邻,一共有多少种不同的铺法?在所有的铺设方法中,一共用了长度为1的瓷砖多少块? 例如,长度为4的地面一共有如下4种铺法,并且,一共用了长度为1的瓷砖4块: 4=1+2+1 4=1+3 4=2+2 4=3+1 编程求解上述问题. 输入格式 只有一个数N,代表地板的长度 输出格式 第一行有一个数,代表所有不同的瓷砖铺放方法

TAQSkinScrollBar 类美化滚动条再讨论

再说:TAQSkinScrollBar 类美化滚动条,http://www.138soft.com/?p=156  里面有人提到不可以滚动 滚动的改善方法: unit AQSkinScrollBar; (* 说明:本单元提取自TdsaSkinAdapter控件,版权归原作者所有. 提取:www.138soft.com *) {$R Scroll.RES} interface uses ComCtrls, Windows, Messages, SysUtils, Classes, Graphics

课堂讨论--beta版总结讨论

在课堂上,我们团队针对前段时间开发过程中存在的问题进行了讨论. 回顾发现,我们存在的问题有: (1)上传图片时无法存入数据库,所以无法再主页上显示图片. (2)无法进行注册登录,不能限制用户权限. (3)没有界面美化,所以界面很简洁. (4)没有实现智能推送功能. 我们挑选了前三个问题当做我们的下一阶段目标,因为智能推送功能暂时不知道如何实现,所以将前三个作为重点.

字符串类的深拷贝(2种)和浅拷贝(3种)

#include<iostream> using namespace std; //深拷贝: //class String //{ //public: // //缺省构造 // String(char *string="") // :_str(new char[strlen(string)+1]) // { // strcpy(_str, string); // } // //如果不写拷贝构造函数或赋值构造函数,系统默认值拷贝,为浅拷贝 // //初始化列表,如果不写,如果

【转载】游戏并发编程的讨论 &amp; Nodejs并发性讨论 &amp; 语法糖术语

知乎上这篇文章对于游戏后端.性能并发.nodejs及scala等语言的讨论,很好,值得好好看. https://www.zhihu.com/question/21971645 经常了解一些牛逼技术人员的分享,比闭门造车或者停留在陈旧技术固步自封,要好多了. 下面是一些摘录: 用C++开发,1000并发是基本. C++的优点是与硬件近,所以处理图形时速度快很多, 如果是处理业务,其实主要是看选用的算法的时间复杂度. 各语言之间的执行效率差别没那么大,同样用过C++跟JAVA,相比较的话JAVA的学