你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

C++——拷贝构造函数的浅copy和深copy

[复制链接]
gaosmile 发布时间:2020-8-31 23:43

一、深拷贝和浅拷贝构造函数总结:

1、两个特殊的构造函数:

(1)无参构造函数:

没有参数的构造函数

Class Test
- c9 J# F' E% W! i{1 \% _; n7 ]# l0 c
public:
( e3 Z; j! m5 E8 d     Test()6 l4 D8 J9 H2 ~. k2 r1 p
     {
: B) b; u$ j7 Y; y6 k& z" f       //这是一个无参构造函数
! w7 u1 |0 \+ A1 c) `) F8 Q     }
! C$ N0 T2 [: z9 @6 K; R% E; ^9 V};% O. R1 g5 @" w( Q7 O

当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空;换句话来说,就是我们在类中,不用我们程序猿自己写,编译就自动提供了无参构造函数(只是我们肉眼看不到!)

#include <iostream>6 m) J, ~# H' n( G# z, w) e
#include <string>
, z4 R  b9 C( f' t4 S' m2 n/ m) ?( B7 U/ |6 x2 M' c$ ^
class Test{
; x. }) S2 Z7 z2 k/ \0 E; b: X- R0 G0 I+ c0 P6 `4 k
//编译器默认给我们提供了一个无参构造函数,只是我们肉眼看不到
7 C7 P) {6 b' @) [$ y
/ k) E) M& ?6 g2 c; F# G6 D8 G};
+ P! k. q$ j/ j! @& ]4 @( d! j" g  f4 x& T& u

9 u. W' a4 V  |6 x; M7 ?1 {3 q  A* j1 }/ R1 R
int main()
  j. K% d4 |$ t9 `. s& {. v{
  f! }. R0 i9 }2 i+ J* E   Test t;
: [  E5 F. ]2 S4 j/ A
- A+ Y. D6 X% O5 k  return 0;
- a7 [/ K7 w( O# B. b/ j}. q6 N9 [# q' u: @, ~
# ~8 h3 S; y& i/ k. V: I* Z

结果输出(编译时能够通过的):

root@txp-virtual-machine:/home/txp# g++ test.cpp
3 [* x" _& G  Q# Z& mroot@txp-virtual-machine:/home/txp# - b+ H" }4 r9 Z2 F# P
" O  H% w: X& Z! z8 ?
8 I: N0 V( u8 E5 c  {) {! a3 @

(2)拷贝构造函数:

参数为const class_name&的构造函数

class Test{
. P. x6 b4 l& I: h7 c* D" j1 Ppublic:
" g) j9 ?8 d' q6 P  k   Test(const Test& p): K& z1 b; m% v3 t+ c
   {* Q  L6 T' N1 i
      
9 Z  z$ `: e4 Y( }3 D/ o   
" U: f% M4 l# o1 E2 t- g9 u   }+ K6 z$ G, q: u, ], c- |
}+ a1 B7 N% \; i7 e; j

当类中没有定义拷贝构造函数时,编译器默认提供了一个拷贝构造函数,简单的进行成员变量的值赋值

#include <iostream>* X* F+ g" y6 U% D) z7 R) ~
#include <string>
0 g. X: \( x  Q( @0 f$ H% ]0 o* d4 Z+ a+ Z% p
class Test{
( p! t5 \* V0 p) U  a1 j& O$ d
4 W2 C) S8 O9 o8 I# _" W0 l3 y3 fprivate:
4 c" d+ g- n8 ~2 M9 r* K. Z   int i;9 E' w4 ~& ~2 R: A
   int j;
3 m& N( j" Q& ?public:
# G5 X1 q2 l7 b# E /*  Test(const Test& p)编译器默认提供这样操作的
& |, l& i+ N- o3 h4 |7 s5 [* Q   {* V; M" X# |) ^# _* Q2 Z/ K3 a+ ^- [
      i = p.i;
7 S/ u. h& J6 k      j = p.j;
+ J6 \+ l2 l6 J' C1 J   }*/
$ E" X% B9 z5 ?2 e+ i5 Q+ a/ ^% `* r; I: h. j1 t
2 ]! @/ Q! W7 ^: K: }4 p
};
) U1 f3 i  O* a
$ _( p! }) }( m+ d  |% \+ Q* u- I3 |0 u/ r8 W

$ A) v$ j: u- G) X2 x$ Gint main()
4 u3 z1 C! l& w+ ]# d) y{
2 i; M/ G2 Q1 s7 ~4 C4 ?+ K   Test t;
6 R+ }0 r. U  `0 `: W5 s: t% f/ s7 i. Q/ F7 a

) w7 g! q6 L: v  s. z# A) P( Q4 w6 Z, H! c' _* `, Q, T
  return 0;- d+ c% X, R2 u& t) E4 K
}+ c5 }( T5 Z) [
( n  {" q  \* S' s2 v

输出结果(编译可以通过):

root@txp-virtual-machine:/home/txp# g++ test.cpp
- `8 a) [4 i2 {. w, vroot@txp-virtual-machine:/home/txp# , `$ c6 J3 ~" v* p7 x, w

(3)注意:

在写程序的时候,定义的类对象初始化时看属于哪种类型的:

Test t;//对应无参构造函数. N" V1 L4 a# u3 ~6 w  B
- h6 p0 g+ [6 a3 P/ f- W3 u& F
Test t(1);//对应有参构造函数
1 j& g2 U6 \8 ]4 f( m: G+ n3 i2 w$ t* b4 q, P7 T8 ^" Z
Test t1;: O4 r0 C; S2 a% e. d# E- a
Test t2=t1;//对应拷贝构造函数" U( I* o! L" J, f; |+ z: [

比如下面我定义的类对象属于无参构造函数(当然前提是你手写了其他构造函数,虽然说编译器会默认提供,但是既然要手写,那么三种构造函数就在定义类对象的时候按需求来写),如果只写了有参数构造函数,那么编译器就会报错:

#include <iostream>, j4 Q# C2 C7 p/ C. v) O$ S
#include <string>, a) I- g- ?0 L1 M( r/ t
' b) U" f: b; ^0 h9 W" f8 j9 H
class Test{4 @+ K0 o2 h' A+ M

* p, C9 `0 Q3 i  L4 Wprivate:9 }4 m+ n4 A% `7 n
   int i;5 |! P$ Q/ z, ~
   int j;; U2 B, S) k# s( g( k
public:+ R* d  X1 x' D1 P& y' m* S) O
  Test(int a)' D3 w% N2 c3 ?# n& |
  {
# Y" E/ P3 h: O3 i     i = 9;4 T7 Y- s% H; L3 Y5 Y; G
     j=8;+ u# v2 R3 P) \9 H6 C
  }. |4 d2 Y0 I$ U( ^
  Test(const Test& p)* D( U8 ]3 b/ X; \1 ~9 [# H' _0 S
   {9 c  k$ r7 b! w5 @' l" \0 n
      i = p.i;
5 Y2 J6 L& J( l1 _. o, i* T  p      j = p.j;
* n; L6 \+ c2 @( `  Y* U* m   }! j  E( z5 v3 e; x/ n4 J/ e
8 k) H9 C: L* Y+ R( O
) n7 v4 _+ q: u5 \+ Y- D
};1 g; G+ @  R" L3 B
int main()' [( U. j# D* _# p* {
{
8 F" T- W5 T7 Z   Test t;
! a, l5 W' F0 b$ p5 {  return 0;& g% J* ]& V+ c3 G! I$ W
}- S" `; L; H: L' C

( e* l2 {5 k! m5 a- l4 J" @; p, X, _5 M; `. t8 m4 ]! ~

输出结果:

root@txp-virtual-machine:/home/txp# g++ test.cpp
) k! F" V0 x2 o2 Ftest.cpp: In function ‘int main()’:6 ]) d' F+ K4 G. K
test.cpp:25:9: error: no matching function for call to ‘Test::Test()’
" c1 J- G: y+ F& G0 P5 I6 [4 F" I7 v    Test t;
8 G6 o1 b0 h0 w" f" G" Z+ @# a         ^
6 i$ o0 \( V& F* l! jtest.cpp:25:9: note: candidates are:# o" s6 A, [2 e9 P, l
test.cpp:15:3: note: Test::Test(const Test&)
0 R1 p* X) B% ^" g- |( d+ N7 B0 U& w   Test(const Test& p)
7 D$ {+ j9 C, M2 S   ^$ ^3 S- L; r* w1 Y; Q- {4 g
test.cpp:15:3: note:   candidate expects 1 argument, 0 provided
: S! E' Y0 @- H3 I0 e) S3 g' btest.cpp:10:3: note: Test::Test(int)
- z8 d3 y8 R  W3 c; b: D8 p4 d, J: s   Test(int a)
# Y% b1 l* ]+ G8 y" d5 `   ^
- q9 A; U; Z" [test.cpp:10:3: note:   candidate expects 1 argument, 0 provided. ^" A! U+ t$ }* f  g/ `
2 [0 C& S/ {) l1 R+ J# {: t

4、拷贝构造函数的意义:

(1)浅拷贝

拷贝后对象的物理状态相同

(2)深拷贝

拷贝后对象的逻辑状态相同

(3)编译器提供的拷贝构造函数只进行浅拷贝

代码版本一:

#include <stdio.h>" Z0 k, S* }' R, X: g/ L; T0 X8 ]
#include <string>/ r0 o3 C' T  a1 @" G3 z

. y8 x! @' u+ |; p3 }class Test{  A2 _; T  [# ?( _$ \# b8 s
private:
* Q  y* z" w, C$ @) \) C& R   int i;( M) J9 A4 J* E* k* }
   int j;( ]$ C- p/ \7 h; A
   int *p;0 @& p' C, v$ |8 S! t5 R' F) {$ z
public:
" A) `  ]  P2 m) e: J6 K. P3 J! f# d   int getI()
8 e  x* D$ w; b% b# R8 s   {8 p, r7 f, k0 ~) o
      return i;* P$ t1 v) B# c
   }0 ~" d& O  @# q' Y
   int getJ()
: `, s& M" A' R7 G   {: N, j$ W1 G- r# J4 k# t0 V1 l
      return j;
- k  H; p/ f( X, p" V   }/ R! |* E# b* D% K. s
   int *getP()
: y8 R0 S+ |/ r( m7 T6 Y   {4 P- f4 l. @! e4 f1 O, [- A
      return p;9 ?& s$ {; W% b# r: U
   }
1 J2 F4 H6 V  H   Test(int a)
/ o) \( z5 x" _9 N6 |  ^) [   {! L' a4 L; U0 I7 E5 {
      i = 2;
/ w/ n- s; Y- ~* W3 T      j = 3;% }3 a0 Z" J# R( e! }$ }4 g
      p = new int;
' q0 c4 t) d. U# }, \      *p = a;; ~# M! C. E+ L: I3 C' Y7 a
  }
+ v, n8 y. r9 D! W# y  void free()- u4 U3 m, A/ J4 R$ {
  {
8 u" c  h0 Z) o- P, M5 C. n3 ~% }/ d4 U9 ?
    delete p;
* |6 \' S+ \5 H  ^3 V  }3 t$ z. S0 o; L8 d: |0 t- |2 ?: @
};" I/ s$ t# A+ [" ?! T- I7 U
int main()
! k( w+ W! p  l3 R/ W{
6 D' p' {* y/ `' s0 V% B' S; s   Test t1(3);//Test t1 3;
6 m: A9 v7 G$ d3 ]   Test t2 = t1;2 I% q( f9 w0 F9 [4 W0 ?( D

$ t5 K8 O, @9 O2 j+ H" g# W, i  printf("t1.i = %d, t1.j = %d, t1.p = %p\n", t1.getI(), t1.getJ(), t1.getP());
2 R! ?) k- a7 t7 k0 \  printf("t2.i = %d, t2.j = %d, t2.p = %p\n", t2.getI(), t2.getJ(), t2.getP());$ N) ]/ A/ t# i2 r6 |0 j7 j

$ p9 w; `0 Q0 q- d- N# r8 W6 F   t1.free();
$ X4 V6 Y) s! F. H' p( _5 U   t2.free();
7 s: i' b" L7 p4 F9 C   
2 |- C- b8 d7 a( X) U; E9 K4 I' m   return 0;3 |) ?& W: l4 d5 N' {; H
}
# U, ^/ z% ~. s7 x% G3 W: C) x& ?

输出结果:

root@txp-virtual-machine:/home/txp# g++ test.cpp$ h  M0 C7 R$ Z- M
root@txp-virtual-machine:/home/txp# ./a.out5 R/ \! |9 L  H7 M
t1.i = 2, t1.j = 3, t1.p = 0x1528010
5 C: {2 L5 [9 q  B  [$ F1 ot2.i = 2, t2.j = 3, t2.p = 0x1528010
; W) |9 s! \/ X. g7 L$ z*** Error in `./a.out': double free or corruption (fasttop): 0x0000000001528010 ***
! w1 ?+ x2 |8 @Aborted (core dumped)
% z  A6 u2 ?- z7 z, ]3 _5 N" g. C, b3 p/ \2 p  A# k" g

注解:出现了段错误,仔细分析,我们发现这里释放了堆空间两次(因为我们这里没有调用拷贝构造函数,也就是自己去写拷贝构造函数;所以这种情况是浅拷贝,不能释放两次堆空间):

微信图片_20200831234255.png

代码版本二(加上拷贝构造函数):

#include <stdio.h>
  m9 U) U" N1 l& `; ~#include <string>
# h/ K6 l! c& [: l* Z. S3 M/ \# R0 m6 S  ]5 J0 T9 r
class Test{
: y1 U. ~6 S( b8 zprivate:3 K  E; e7 q: z# G  T$ {" Q
   int i;
* |6 j3 p5 u3 F* }/ N   int j;
( ~+ B$ @% F2 H! w   int *p;
/ c4 n+ p; O5 f4 H# V# ypublic:
) m2 M4 ^' h: S( n# T4 z   int getI()
/ p/ `+ A& n$ v# x/ H# S  {0 {. H   {! K2 e  m9 S: m9 z' a2 i: m
      return i;: ^; C: o0 N7 C  F5 k, S5 P
   }
" o+ m/ [# O2 \" [# f   int getJ()
8 ?/ \8 i, C1 b) {0 g/ h# @   {
1 z( t* v5 t. E4 w      return j;  c4 s# [% i3 X1 g- {& s, u
   }5 ?) c( F' N9 D0 o
   int *getP()
0 p  r1 m( |& J  f: ]9 _   {. Z, K4 |, M. m8 A2 ^
      return p;- R$ u9 F% b& h
   }
$ x8 j8 J; g- H) b   Test(int a)2 l( q" g2 ?/ n5 v* m% s' C* o  q# Z
   {' e) K2 h$ E2 [- \6 m2 a0 ^( O( Z
      i = 2;6 G) ]2 l. H0 S; R( m( O% h: x
      j = 3;
' a' H2 ~! v% G, Q      p = new int;
% q) H$ A5 U5 C; t2 v; d      *p = a;
6 n4 G9 E2 ?0 w3 `+ g" M8 u  }1 b) V* A0 A; R/ j
  Test(const Test& t)
( a+ z) K2 E0 j8 Z/ w  {( y0 n5 c/ u: v$ c) \
    i = t.i;
. [: N. C9 y4 y+ Y1 q    j = t.j;
: ~$ l% p& k$ \) V* y, l  p  X    p = new int;6 t, f+ t7 ~( h+ w5 K5 w& V

0 _" L2 E) K$ q( B1 i0 s1 k% W    *p = *t.p;
1 ?$ V. O; S4 t+ o  }% ~, t/ ~& r& Q0 g
6 w  g/ c: m; m& I; O) a5 r6 a% X; T4 x
  void free()
7 r1 [( @! U$ L# ]  {/ V+ O* h5 d$ @0 h7 L- p

4 |+ `9 K& ]1 M3 H) l$ _( y9 K. T    delete p;" i/ u3 c! e; s, o4 H7 Z: m$ l5 g
  }! Q8 P6 S) R  O
};
1 r$ |8 A" U2 N+ J1 jint main()8 x8 d  ]1 D2 q" G" M1 E0 b$ S
{
& M5 E* r) f, y) h% U2 I   Test t1(4);
: E4 h! N6 S5 u* Y; A8 t0 D# u$ |: e   Test t2 = t1;
6 d* T( f& Q) w1 J: G. [' U8 v1 X! o4 N
   printf("t1.i = %d, t1.j = %d, t1.p = %p\n", t1.getI(), t1.getJ(), t1.getP());9 K3 ]( \8 Q9 T- l4 r4 l) ~
   printf("t2.i = %d, t2.j = %d, t2.p = %p\n", t2.getI(), t2.getJ(), t2.getP());
5 t1 a: W. |% |/ X   t1.free();$ y0 k5 q9 I4 P6 N! a$ d  Q
   t2.free();# I2 G' i4 N9 u9 {
. j. K5 Q. g, m  Q: x" @; {
   return 0;
2 N* s) Q& N2 z}
3 e4 ~& N7 i+ z5 W6 z6 M# z3 Q9 x+ o8 K

输出结果:

root@txp-virtual-machine:/home/txp# g++ test.cpp# D1 D3 F( t9 r. F
root@txp-virtual-machine:/home/txp# ./a.out
. m4 C. C; A) B$ B9 y% g0 At1.i = 2, t1.j = 3, t1.p = 0xb0a010
9 p9 c1 [: c7 K# j# }1 {- q4 \t2.i = 2, t2.j = 3, t2.p = 0xb0a030
4 N( ]  B# z2 N0 ^
' |, d2 J% b8 Y; _0 L# ?2 Y

注解:从打印的p地址空间来看,就知释放的两个对象的堆空间不同,不再是指向同一堆空间了;同时我们发现浅拷贝只是简单数值上的进行赋值而已;深拷贝不只是简单的值赋值,而是从内存的角度来看,是操作不同的内存。

5、什么时候需要进行深拷贝?

(1)对象中有成员指代了系统中的资源

  • 成员指向了动态内存空间

  • 成员打开了外存中的文件

  • 成员使用了系统中的网络端口

    * y- Z: Y1 J' N. G6 b8 J# R7 g

注意:一般来说,自定义拷贝构造函数(也就是我们自己手写的),必然需要实现深拷贝!

二、总结:

  • C++编译器会默认提供构造函数

  • 无参构造函数用于定义对象的默认初始化状态

  • 拷贝构造函数在创建对象时拷贝对象的状态

  • 对象的拷贝有浅拷贝和深拷贝两种方式。


    & p" _/ p4 R3 o+ \0 ~* D

好了,今天的分享就到这里,如果文章中有错误或者不理解的地方,可以交流互动,一起进步。


3 w% ^' N* J7 N0 J& ?2 n
赞 收藏 评论0 发布时间:2020-8-31 23:43

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版