|
一、深拷贝和浅拷贝构造函数总结: 1、两个特殊的构造函数: (1)无参构造函数: 没有参数的构造函数 Class Test{1 \% _; n7 ]# l0 c public: Test()6 l4 D8 J9 H2 ~. k2 r1 p { //这是一个无参构造函数 } };% O. R1 g5 @" w( Q7 O 当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空;换句话来说,就是我们在类中,不用我们程序猿自己写,编译就自动提供了无参构造函数(只是我们肉眼看不到!) #include <iostream>6 m) J, ~# H' n( G# z, w) e#include <string> / m) ?( B7 U/ |6 x2 M' c$ ^ class Test{ 0 G0 I+ c0 P6 `4 k //编译器默认给我们提供了一个无参构造函数,只是我们肉眼看不到 }; ( d! j" g f4 x& T& u 7 ?1 {3 q A* j1 }/ R1 R int main() { Test t; return 0; }. q6 N9 [# q' u: @, ~ # ~8 h3 S; y& i/ k. V: I* Z 结果输出(编译时能够通过的): root@txp-virtual-machine:/home/txp# g++ test.cpproot@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{public: Test(const Test& p): K& z1 b; m% v3 t+ c {* Q L6 T' N1 i }+ K6 z$ G, q: u, ], c- | }+ a1 B7 N% \; i7 e; j 当类中没有定义拷贝构造函数时,编译器默认提供了一个拷贝构造函数,简单的进行成员变量的值赋值 #include <iostream>* X* F+ g" y6 U% D) z7 R) ~#include <string> $ H% ]0 o* d4 Z+ a+ Z% p class Test{ private: int i;9 E' w4 ~& ~2 R: A int j; public: /* Test(const Test& p)编译器默认提供这样操作的 {* V; M" X# |) ^# _* Q2 Z/ K3 a+ ^- [ i = p.i; j = p.j; }*/ % `* r; I: h. j1 t 2 ]! @/ Q! W7 ^: K: }4 p }; % \+ Q* u- I3 |0 u/ r8 W int main() { Test t; : W5 s: t% f/ s7 i. Q/ F7 a 4 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.cpproot@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);//对应有参构造函数 3 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 private: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& | { 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; j = p.j; }! 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* { { Test t; return 0;& g% J* ]& V+ c3 G! I$ W }- S" `; L; H: L' C , X, _5 M; `. t8 m4 ]! ~ 输出结果: root@txp-virtual-machine:/home/txp# g++ test.cpptest.cpp: In function ‘int main()’:6 ]) d' F+ K4 G. K test.cpp:25:9: error: no matching function for call to ‘Test::Test()’ Test t; ^ test.cpp:25:9: note: candidates are:# o" s6 A, [2 e9 P, l test.cpp:15:3: note: Test::Test(const Test&) Test(const Test& p) ^$ ^3 S- L; r* w1 Y; Q- {4 g test.cpp:15:3: note: candidate expects 1 argument, 0 provided test.cpp:10:3: note: Test::Test(int) Test(int a) ^ 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 class Test{ A2 _; T [# ?( _$ \# b8 s private: 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: int getI() {8 p, r7 f, k0 ~) o return i;* P$ t1 v) B# c }0 ~" d& O @# q' Y int getJ() {: N, j$ W1 G- r# J4 k# t0 V1 l return j; }/ R! |* E# b* D% K. s int *getP() {4 P- f4 l. @! e4 f1 O, [- A return p;9 ?& s$ {; W% b# r: U } Test(int a) {! L' a4 L; U0 I7 E5 { i = 2; j = 3;% }3 a0 Z" J# R( e! }$ }4 g p = new int; *p = a;; ~# M! C. E+ L: I3 C' Y7 a } void free()- u4 U3 m, A/ J4 R$ { { . n3 ~% }/ d4 U9 ? delete p; }3 t$ z. S0 o; L8 d: |0 t- |2 ?: @ };" I/ s$ t# A+ [" ?! T- I7 U int main() { Test t1(3);//Test t1 3; Test t2 = t1;2 I% q( f9 w0 F9 [4 W0 ?( D printf("t1.i = %d, t1.j = %d, t1.p = %p\n", t1.getI(), t1.getJ(), t1.getP()); 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 t1.free(); t2.free(); return 0;3 |) ?& W: l4 d5 N' {; H } % G3 W: C) x& ? 输出结果: root@txp-virtual-machine:/home/txp# g++ test.cpp$ h M0 C7 R$ Z- Mroot@txp-virtual-machine:/home/txp# ./a.out5 R/ \! |9 L H7 M t1.i = 2, t1.j = 3, t1.p = 0x1528010 t2.i = 2, t2.j = 3, t2.p = 0x1528010 *** Error in `./a.out': double free or corruption (fasttop): 0x0000000001528010 *** Aborted (core dumped) 3 _5 N" g. C, b3 p/ \2 p A# k" g 注解:出现了段错误,仔细分析,我们发现这里释放了堆空间两次(因为我们这里没有调用拷贝构造函数,也就是自己去写拷贝构造函数;所以这种情况是浅拷贝,不能释放两次堆空间):
代码版本二(加上拷贝构造函数): #include <stdio.h>#include <string> 3 M/ \# R0 m6 S ]5 J0 T9 r class Test{ private:3 K E; e7 q: z# G T$ {" Q int i; int j; int *p; public: int getI() {! K2 e m9 S: m9 z' a2 i: m return i;: ^; C: o0 N7 C F5 k, S5 P } int getJ() { return j; c4 s# [% i3 X1 g- {& s, u }5 ?) c( F' N9 D0 o int *getP() {. Z, K4 |, M. m8 A2 ^ return p;- R$ u9 F% b& h } 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; p = new int; *p = a; }1 b) V* A0 A; R/ j Test(const Test& t) {( y0 n5 c/ u: v$ c) \ i = t.i; j = t.j; p = new int;6 t, f+ t7 ~( h+ w5 K5 w& V *p = *t.p; }% ~, t/ ~& r& Q0 g 6 w g/ c: m; m& I; O) a5 r6 a% X; T4 x void free() {/ V+ O* h5 d$ @0 h7 L- p delete p;" i/ u3 c! e; s, o4 H7 Z: m$ l5 g }! Q8 P6 S) R O }; int main()8 x8 d ]1 D2 q" G" M1 E0 b$ S { Test t1(4); Test t2 = t1; ' 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()); 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; } 6 z6 M# z3 Q9 x+ o8 K 输出结果: root@txp-virtual-machine:/home/txp# g++ test.cpp# D1 D3 F( t9 r. Froot@txp-virtual-machine:/home/txp# ./a.out t1.i = 2, t1.j = 3, t1.p = 0xb0a010 t2.i = 2, t2.j = 3, t2.p = 0xb0a030 注解:从打印的p地址空间来看,就知释放的两个对象的堆空间不同,不再是指向同一堆空间了;同时我们发现浅拷贝只是简单数值上的进行赋值而已;深拷贝不只是简单的值赋值,而是从内存的角度来看,是操作不同的内存。 5、什么时候需要进行深拷贝? (1)对象中有成员指代了系统中的资源
注意:一般来说,自定义拷贝构造函数(也就是我们自己手写的),必然需要实现深拷贝! 二、总结:
好了,今天的分享就到这里,如果文章中有错误或者不理解的地方,可以交流互动,一起进步。 |
微信公众号
手机版