回答

收藏

第二层:对象的初始化和清理(什么是第二层)

知识点 知识点 34 人阅读 | 0 人回复 | 2023-01-19

帖子摘要:文章目录 前情回顾对象特性——对象的初始化和清理构造函数和析构函数什么是构造函数和析构函数构造函数构造函数的语法构造函数的分类按照参数分类按照类型分类拷贝构造函数拷贝函数的调用时机深拷贝和浅拷贝   ......% J5 k& _, }5 ]1 k' c- O- u0 m
, K1 U$ i; r# W, G
大家好,欢迎来到Java吧(www.java8.com),交流、学习Java技术、获取Java资源无任何套路,今天说一说:“第二层:对象的初始化和清理”" T2 A0 B6 H: x; L' s* V
  k) p! W# P, Y' v( S
/ x' E4 N" c) F" f) h; L2 E" J) l+ f
        
# V7 _# _$ _7 W7 {9 t0 y# Y2 e                # y0 t8 _0 c* j
                    , i0 P/ u2 @# X  I( A8 z" O9 p6 e& n
                        
: }8 N+ U* v/ |7 J5 i  B7 T& T                    , A: R" m/ s9 q/ D
                    
. W5 t4 _# C) {0 A# z 文章目录! _  _, V$ l) w
  • 前情回顾
  • 对象特性——对象的初始化和清理
  • 构造函数和析构函数
  • 什么是构造函数和析构函数
  • 构造函数
  • 构造函数的语法
  • 构造函数的分类
  • 按照参数分类
  • 按照类型分类
  • 拷贝构造函数
  • 拷贝函数的调用时机
  • 深拷贝和浅拷贝. T# J6 z1 F% K/ o
         
    / h. w9 Y. l; B
         
  • 构造函数的调用方法
  • 括号法
  • 显示法
  • 隐式转换法
    8 t" r( ]8 n7 }  z$ S     
  • 构造函数规则
      H& u4 z2 c- u  k1 m   
  • 析构函数
  • 析构函数的语法
  • 析构函数的作用
    ' v, L! ^5 c# }* h1 ~' O   
    & n& F9 x/ L  `6 E9 C   
  • 初始化列表
  • 作用
  • 语法. \: i4 P' L& \- L4 V
       
  • 类对象作为类成员
  • 静态成员
  • 静态成员变量
  • 静态成员变量特点$ r3 j) n$ e! n
       
  • 静态成员函数
  • 访问方式
  • 注意事项
    # X, s4 _: P9 j& j) y4 R   
    % L8 S2 C) q- G1 H# h
      7 I7 p7 @2 a0 M/ n8 ]' w; @
      
  • 掌握对象初始化和清理步入第三层
  • 本章知识点图片形式
  • 出现的陌生名词解释
    ' ~: w3 ?$ z6 Y" ]) p- e7 z( Y
    # ~0 v- C" F+ `9 D! \
    7 Y7 [( k4 D& ]6 I welcome! Q; o7 ]7 c- ]1 X; h  O6 y) f
    ??博主介绍一名大一的智能制造专业学生在学习C/C++的路上会越走越远后面不定期更新有关C/C++语法数据结构算法Linuxue5使用制作游戏的心得和大家一起共同成长。
    6 K% {0 T. P1 D+ }) E* }, K ??C++专栏C++爬塔日记7 i. d: @4 m0 h
    博客制作不易点赞+?收藏+?关注
    ) |* p1 ?8 f2 s4 D7 S" c  L% F , s; H( a8 r4 q- N% m

    " G" k. n5 k  S- o5 y  W前情回顾
    : [/ _( D: \/ m; a: ]* t& j8 J' N上回说到我踏入C++古塔在第一层中了解到了面向对象的第一大特性——封装通过努力我成功获得封装的力量并且上到了第二层…8 Y0 p3 E+ x) ]3 T
    ' Q! z0 h3 ~! q5 Y7 X! F/ A
    对象特性——对象的初始化和清理 , y' F# g, q# N1 I2 S
    踏入第二层当中那道声音再次飘来“挑战者这层的任务需要你掌握对象特性中的初始化和清理祝你好运…”一块石板再次出现在我的眼前! h" m7 r# p. b$ |# w/ [2 Y! T
    # P: l4 h! }8 \
    构造函数和析构函数
    3 l6 M6 }3 e" m4 ^  T8 K- ]1 b什么是构造函数和析构函数 - ^7 R: e% u6 A2 `) {5 D: c' E
    在C++中对象在被调用和销毁的时候会默认调用两个函数在创建对象的时候会调用构造函数在销毁的时候会调用析构函数它们在一个对象中只会被调用一次所以构造函数和析构函数是必须存在的当程序员本身不提供任何的构造和析构函数的时候编译器会提供默认构造函数0 w) n+ t0 y' X. \: e
    ! j4 j- g; X% [$ s, ]
    构造函数 ( b# B5 C- y) c+ ]! ]/ E+ o+ [7 }: p
    构造函数的语法
    * o. Q# ?) V3 _$ d 2 R' z1 u# @6 T0 O  d
  • 构造函数没有返回值也不用写void
  • 构造函数的函数名与类名相同
  • 构造函数可以有参数可以发生函数重载1
    # ~9 m; o' V5 \, ~4 Q2 y% L
    + M% S. E1 F5 V. Z
    基本语法了解那构造函数就可以尝试的去写一下。1 v. E% u1 J, g7 F
    # L2 ~0 `0 c3 L
    1. class A
    2. {
    3. public: 4 F. L8 u; Q! O( k
    4.         A() 6 D* b0 H) q* F, p( |& C' \
    5.         {
    6.                 cout , ~+ ~; J/ p1 y* P
    7.         A c;
    8. } , H. q$ g& |0 {0 C' d
    复制代码

    2 [, K6 T/ o$ Y. G% \
    cb34ceced2334c15a9e1267d602692ee.png (144.66 KB, 下载次数: 0, 售价: 3 金钱)
  • 类名const 类名 引用2
    ; O3 v0 ]- o# n# I4 [4 J6 K# s
    & f" c5 B8 w( z0 p
    可以看到它的参数是我们的对象那它的作用大致就可以猜到了就是将对象的数据拷贝到调用拷贝构造函数的对象上面去可以用代码来试验一下。
    , K/ [6 c, T' Q- @4 C$ W
    ) R" \- E: o( A* D  C3 c8 n
    1. #include 3 S4 [1 Q- N6 t, ]" p( v# M' w# e; @
    2. #include[i]
    3. using namespace std;   p5 [- `$ {( Q9 }
    4. class A
    5. {
    6. public:
    7.         A()
    8.         {
    9.                 cout
    10.                 b = c.b; 2 s2 V5 y' Y0 S1 V7 p
    11.         }
    12.         int b; 9 g$ M# k) k. h
    13. }; 0 b7 j% F* h% P8 b  j" D7 r1 ]
    14. int main() ; d: L& A1 J3 [
    15. { : o% i! K4 |% S9 W9 L5 |  ?
    16.         A c; 0 M0 T0 E1 a5 P* B0 k
    17.         cin >> c.b;
    18.         A b(c);
    19.         cout
    20. 7559452d9d234c63960819738f28f66f.png (132.66 KB, 下载次数: 0, 售价: 2 金钱)
    21. 可以看到c里面的内容拷贝到了b里面。
    22. 拷贝函数的调用时机 : E* G& d, x) q) P/ z- X$ F. V
    23. 通常在三种情况下调用我们的拷贝函数 - L9 e7 U, q2 [
    24. ' x- h4 \) l  u9 ~9 ?! {
    25. [b]
    26. [ol][*]使用一个已经创建完毕的对象来初始化一个新对象可以参考上面[*]值传递的方式给函数参数传值[*]以值的方式返回局部变量[/ol]
    27. [/b]
    28. 对于第二点可以看下方这张图来理解 9 ^; f6 `" a4 p" P+ ]9 ?
    29. f17f4a66f5c9460cb93d536c5874f053.png (188.04 KB, 下载次数: 0, 售价: 1 金钱)
    30. int main函数内部调用test1函数函数参数为类在调用函数时候会调用拷贝构造函数然后在回去调用test1函数内部。 % l" @  E) {" @- c1 h% v" m
    31. 而第三点是因为函数中当返回的值是我们的对象时因为出了函数体局部变量销毁所以函数内对象会进行销毁这个时候就会发生拷贝将函数内的对象数据重新拷贝到一块新的空间内进行返回。 / x( e3 _! T. D0 p4 [" S
    32. 深拷贝和浅拷贝 ) E* q7 C( u' c/ |! D
    33. [b] ' g7 F2 V( n! b; p- f( a
    34. [u][*]浅拷贝是进行简单的拷贝操作只会进行值拷贝[*]深拷贝是在堆区重新申请空间进行拷贝[/u]
    35. [/b] 8 \' |- ]; Y- l0 h' a: x
    36. 那为什么会出现深拷贝和浅拷贝呢当类内的属性有指针的时候这个时候发生拷贝会拷贝的是地址。
    37. 9 Y* {% t. ]2 n+ [
    38. b8cfddb01ec942379293d2d655fceed6.png (11.53 KB, 下载次数: 0, 售价: 3 金钱)
    39. 因为指针内存放数据之前我们要使用new3函数进行开辟空间这个时候我们就需要在析构函数内使用delete4释放空间但是因为两个对象都同时指向了同一块空间就会释放两个同一块空间的地址对堆区进行了重复释放那这个时候想要解决就需要程序员自己设计出拷贝构造函数进行深拷贝对指针的拷贝用new操作符重新开辟一块空间。
    40. 深拷贝 + c0 d" ]' Q9 k, S, \5 }
    41. [code]class A
    42. {
    43. public:
    44.         A()
    45.         { ( o  S& \, X( X4 p8 Z
    46.                 cout
    47.                 b=c.b; ! E5 Q5 C0 I' O" W
    48.                 d=c.d;
    49.         }
    50.         //深拷贝
    51.         A(A &c) . C$ c& Q- V; n' z% Q# _: {+ I( J, `" X
    52.         {
    53.                 cout   , C5 p  u. S9 i
    54. 构造函数的调用方法
    55. 括号法 0 Z, c0 F7 c4 j0 ~1 m% ?
    56. 括号法在上面我们进行拷贝构造函数调用时已经用过就是在对象后面加括号括号内是参数但是这个时候要注意对于无参构造函数而言是在后面加括号的这样编译器会认为这一个函数声明
    57. c25169d2dd4249f98b37febcffff51c9.png (21.68 KB, 下载次数: 0, 售价: 2 金钱)
    58. 在编译器眼中加括号的就会认为是这样。
    59. $ X+ k5 \2 b8 m% G$ m1 a
    60. 显示法 9 g9 i0 Z" a2 k0 }$ b$ D
    61. 显示法是将我们的构造函数当赋值一样书写方式如下
    62. ! t6 }% U/ w: ~6 U
    63. [b]
    64. 类名 对象名 = 类名(参数); # [0 H- U+ @! d2 W7 U4 ^5 E: r3 N
    65. 2 m4 H* B) D, |2 D, [# C# p+ f/ H
    66. [/b] / {$ @3 K2 }, f0 X2 P# b
    67. 函数名(参数)叫做匿名对象特点是当执行结束后系统会立即回收匿名对象。 , P2 j7 D3 \+ U& D4 Q' u
    68. 注意不要利用拷贝构造函数去初始化匿名对象这个时候编译器会认为这是一个函数声明。
    69. 9 V1 F3 w, u3 D' Q5 k
    70. 隐式转换法 ; l9 n( ^! h# P# h! z8 ?
    71. 隐式转换法是将我们的显示法中等号右侧的类名省略书写方式如下 * u' E! U- y% u
    72. $ T' G- H& O8 a$ z( j0 M5 }3 K, k
    73. [b]
    74. 类名 对象名 =参数; 5 \3 W9 v) K' n+ i/ }7 N) `. c
    75. 7 @8 C' n0 B9 [0 Y1 s- D
    76. [/b] - _, F5 [) i) N: F2 h2 S! f
    77. 构造函数规则 5 }) s6 n$ L' v$ W/ m7 g
    78. 在默认情况下C++编译器至少会给一个类添加三个函数
    79. . \! U1 `5 }: N1 O1 {+ L$ O
    80. [b] ) x, P2 M4 n  I
    81. [ol][*]默认构造函数无参内部空实现[*]默认析构函数内部空实现[*]默认拷贝构造函数对属性进行浅拷贝[/ol]
    82. [/b]
    83. 构造函数规则如下
    84. ) E" c3 s, d; [
    85. [b]
    86. 如果程序员定义有参构造函数C++不在提供默认构造函数无参但是会提供默认拷贝构造函数如果程序员定义拷贝构造函数C++则不会提供任何构造函数。 ( W+ ^% [4 J- h* |3 u
    87. [/b] / J# Y6 v9 n) I1 p# O% ^( F
    88. 析构函数
    89. 析构函数的语法 * w5 X  l6 ~- @1 f/ f7 J$ s
    90. [b]
    91. [u][*]没有返回值也不需要写void[*]函数名和类名相同但是需要在名称前加~[*]析构函数不可以有参数不能发生重载[/u]
    92. [/b]
    93. [code]class A
    94. {
    95. public: ( o0 h0 _6 P  Y/ X
    96.         A() + V5 V: M. P3 `0 m
    97.         {
    98.                 cout 4 r" J- t$ o$ F' _) _0 E
    99.                
    100.         }
    101.         string a;
    102. }; ( H# t. }1 z) T+ x% j: t4 j& z# k4 H
    103. int main() ) O1 w" Y) @3 E7 _
    104. {
    105.         A c; / X1 m% C4 \' k% B+ }4 a
    106. }
    复制代码
    4 [4 G4 H  A2 [1 g$ @
    析构函数的作用 0 C5 t0 q; C# ^( v' X8 D
    当类内调用new操作符开辟空间时需要在析构函数内部进行释放空间的操作用delete操作符最好在将指向new操作符开辟的空间的指针置空防止野指针。
    % O7 O* H: S+ P ) w2 o5 e! W# U. |5 D# _
    初始化列表 + _! f% T* L6 E/ D5 F: ?
    作用 # I* {  M4 x- T; l
    , T- {6 Z5 X5 f2 J# {
    对类内的属性进行初始化操作
    5 B4 ^2 V6 l! a" |& K5 T2 U 8 D2 i8 \4 Q+ }
    & p. C7 k2 S* C6 H8 T
    语法
    / B- W0 D3 K$ w# N! X, R: x8 n2 g
    6 G1 {! y; g& M, ~5 E8 R1 [ 构造函数():属性1(初始值)属性2(初始值)…
    / C( r. v5 A' g # }( v( _. s4 M8 c3 M  L! e

    2 u- m4 D& b' c7 E) a7 x4 ~7 \
    1. class man 0 w$ q6 Z* x, N
    2. {
    3. public:
    4.         //构造函数初始化列表 4 M: o9 G- w0 W: E& V3 y
    5.         man() :_name("狗蛋"), age(18), car("鬼火")
    6.         {
    7.         } 7 ~1 ~" ?( A% B/ @5 A; d$ l  R$ j) w
    8.        
    9.         //属性 # ?7 U$ h0 J/ e
    10.         string _name;//名字
    11.         int age;//年龄
    12.         string car;//汽车
    13. }; ! o9 b( m9 g- g0 i9 m
    复制代码
    $ k7 i6 z' B2 S! ~* G
    这是最基础的用法还可以和有参构造函数进行结合1 m! Q/ U) h! R/ t# B" t' I3 d
    , ?1 Q  u# r8 k) e* M3 |5 C' q) s4 |7 {
    1. #include 4 e  s! s2 T/ C0 p
    2. #include[i] 4 k" S/ X4 j( K* s' A
    3. using namespace std; 9 \( _  M, b7 `* p' z' J% G
    4. class man
    5. {
    6. public:
    7.         //构造函数初始化列表
    8.         man(string a, int b, string c) :_name(a), age(b), car(c)
    9.         {
    10.         }
    11.        
    12.         //属性 7 h6 s: J- a: c: x$ C+ g. _8 d
    13.         string _name;//名字
    14.         int age;//年龄
    15.         string car;//汽车 4 {9 `9 ^1 p  W8 Q5 M9 P# W  o9 {
    16. };
    17. int main()
    18. { , z, Q! ?* H) d) f# \/ O3 W
    19.         man m1("狗蛋", 18, "鬼火"); 9 R( L1 t6 |# W4 I+ V* F
    20.         return 0; 9 i: b# C" J' D( C; j& ]5 n
    21. }
    复制代码
    * S5 X/ t- J$ l- @
    也可以这样去进行初始化。
    6 e0 Z5 R$ S6 G9 `& ?' Y# |' t6 I
    0 T) t& `# j) e% K类对象作为类成员
    - I* U  B! }+ e/ d6 P1 y  J在C++中类中的成员可以是另一个类的对象这个时候称这个成员为对象成员
    6 s9 Y3 a& A8 q' V- x( b1 i5 f
    " l! |; m8 C$ s3 ]; ~7 N
    1. class A
    2. { . ~: U+ t- I8 j$ _# Y1 C0 U
    3. }; ; l  `* r* K1 p. z- J, w) W$ c0 r
    4. class B 3 V' E& C$ h( M: {. ?+ A, P1 D% k
    5. {
    6.         A a; 9 Y4 m+ C' t/ |: U% D/ M
    7. };
    复制代码

    : h# `6 V, w$ _* u- p上述代码中A a就是对象成员在我们创建B类的对象时A a为对象成员要先有成员才能有这个类所以会先调用A的构造函数在调用B的构造函数但是在解析的时候时相反的先析构B在析构A。
    2 ^% k" s9 D* S! l% V* f
    : r6 _* r4 F: I+ X' `6 ^$ c静态成员 " S$ G2 p9 Q6 G* u4 r
    静态成员变量 & o3 ^: }+ P  Q7 k3 ?5 Y4 u
    静态成员变量是将我们的成员变量放入到静态区需要在成员的类型前面加static关键字。
    ' {. Y; n' P% D2 q( r2 [ " H% s) ]0 t4 E% ?9 Y
    静态成员变量特点
    / y, |' f- g' @
    & Y9 V) B. q! n( x [ol]
  • 因为放入到了静态区所以所有对象是共享同一份数据的不属于任何的一个对象了
  • 会在编译阶段就分配内存相当于在运行之前就已经分配好内存了分配在全局区
  • 类内声明类外初始化比如有一个初始值才可以用或者就无法访问到那片内存空间。[/ol]
      X* o$ |) A9 O* m3 P0 `: [

    ' j) J1 E) g. l5 i& Q/ l& c对于静态成员变量的初始化我们可以这样
    5 X# }) @7 `0 I
    ) C- ?: X4 H! [! K( `1 e1 _
    1. class A
    2. { % V; A& a( v, e* `7 H
    3.         static int a; ( }1 I2 R4 x, z5 n3 O: i1 S# G
    4. }
    5. int A::a=//初始值; 1 q, j3 [+ c- N7 f
    复制代码

    ; s1 }& w% B2 s3 |0 w. y也是因为上述特点静态成员不属于任何对象所有有了两种访问方式  I3 f4 N+ J2 U3 X- W- Y

    7 K# ~: E( u0 \! W0 f) d- Z, G5 z1 L & _8 ~/ A* k' @$ @! h3 l
    [ol]
  • 通过对象进行访问访问方式对象名.成员名
  • 通过类名进行访问访问方式类名:: 成员名[/ol] $ M8 o2 f( v% ]2 W

    6 I/ l/ P/ a& {1 N3 l3 f静态成员变量也是遵循访问权限的。
    ) T) _  y" _3 `6 q
    : @% a+ x/ J, E% s静态成员函数
    & m. n  D. o' [% q访问方式 3 `/ ?4 M# p( X# U
    与静态成员变量的访问方式是一样的。
    0 U& [8 }# c# p. H$ e2 m& e
    3 c5 N* g' M$ y  C  W注意事项
      m) w/ K& L7 M" I6 j
    4 J) Q. ?, O2 o7 E  V0 f& Q [ol]
  • 静态成员函数可以访问静态成员变量
  • 静态成员函数不可以访问非静态的成员变量
  • 放访问非静态的时候这个时候静态成员函数是不知道修改的是哪个对象的如果修改它会把所有对象的这个属性进行修改[/ol] 1 X, o$ K  P) {8 R
    - m3 n" \: z* W
    静态成员函数同样遵循访问权限。
    ' b% j* y0 v( y% g) Z
    ! [) I" W2 J3 Y' a" U* V7 `掌握对象初始化和清理步入第三层 6 I& a* l$ T& U% M- t: k
    随着面前石板的倒下第三层的楼梯显现在了我的眼前…) U0 l8 g' s' L' V! w
    - t; D, o, O# |+ Z
    本章知识点图片形式 ( G; m& \+ F  u) s! K+ n
    d69f039ef3af418a961e45ae0f2977ca.png (105.09 KB, 下载次数: 0, 售价: 4 金钱)
  • 分享到:
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则