|
[导读] C++语言有时候也拿来写写应用代码,可是居然发现连构造、析构都还没弄明白,把这糟心的概念整理分享一下。 在谈类的构造前,先聊聊面向对象编程与面向过程的个人体会。 面向过程策略要谈这个问题,先来对比一下传统面向过程的编程策略:
比较典型的–C, Pascal, Basic, Fortran语言,传统的做法是整了很多函数,整合时主要是整合各函数的调用,main函数协调对过程的调用,并将适当的数据作为参数在调用间进行传递流转而实现业务需求。信息流转主要靠:
如上图,传统面向过程编程语言,比如C语言其编程的主要元为函数,这种语言有这些弊端:
当然如C语言开发,现在的编程策略已经大量引入了面向对象编程策略了,但是要实现这些对象编程策略,则开发人员本身需要去实现这些面向对象本身的策略,需要自己手撕实现这些基础的对象思想。所以这里仅仅就语言本身特征做为说明,不必纠结。而这些策略如果由编程语言本身去实现,则显然是一个更优异的解决方案。但是比如嵌入式领域为嘛不直接用C++呢,而往往更多采用C的方式采用对象策略来撸代码呢? 嵌入式领域更多需要与底层硬件打交道,而C语言本身抽象层级相对更适合这种场景,结合对象策略编程则可以兼顾重用封装等考量。 回到技术发展历史来看,1970年代初期,美国国防部(DoD)成立了一个工作队,调查其IT预算为何总是失控的原因,其调查结果是:
而面向对象编程语言则很好的解决了这些弊端:
而现代面向对象编程语言(OOP: Object-Oriented Programming) ,从语言本身角度,其编程的场景则变成下面一种截然不一样的画风:
程序的运行态:是不同的实例化对象一起协作干活的场景 应用程序通过对象间消息传递,对象执行自身的行为进而实现业务需求。编写代码,设计类,撰写类的代码,然而应用程序运行时,却是以相应的类实例化的对象协作完成逻辑,这就是所谓面向对象编程的含义。那么对于对象而言,具有哪些属性呢?
从上面的描述,应用程序本质是很多对象相互协作一起在干活,就好比一个车间,有机器、工人、工具等一起相互在一起产生相互作用,从而完成某项产品的制造。那么,这些对象从哪里来呢? 对象来自于类的实例化,谁负责实例化对象呢?这就是类中构造函数干的活,那么析构函数就是销毁对象的。所以构造函数管生,析构函数管埋。 构造管 “生”构造函数按照类的样式生成对象,也称为实例化对象,那么C++中有哪几种构造函数呢?
构造函数的相同点:
那为嘛又要整这么几个不同的构造函数呢?举个生活中你或许遇到过的栗子:
那么,到底不同的构造函数有些什么不同呢?为嘛C++语言设计这么多种不同的构造函数呢?
ClassName(const ClassName &old_object); 析构管“埋” 析构函数通常用于释放内存,并在销毁对象时对类对象及其类成员进行其他清理操作。当类对象超出生命周期范围或被显式删除时,将为该类对象调用析构函数。
既然析构函数是构造函数的反向操作,对于对象管"埋",那么什么时候“埋”呢?
前面说如果程序猿没有显式定义析构函数,编译器会自动生成一个默认的析构函数。言下之意是有的时候需要显式定义析构函数,那么什么时候需要呢? “生”与“埋”举例 前面说构造管“生”,析构管“埋”,那么到底怎么“生”的呢?怎么“埋”呢?,看看栗子: #include <iostream>* s6 s6 ?1 W! z5 Z/ ^: V7 m' e! Zusing namespace std;% t2 V7 F5 V. y; o+ u: v class Rectangle {: d, g$ S5 b* `6 D( }+ ]4 Y public: : d( a2 F' d4 j3 }2 @ Rectangle(); : f* q4 B) O5 b# P3 L Rectangle(int w, int l);) V3 l( R0 W0 G) }* B0 @5 S Rectangle(const Rectangle &rct) {width = rct.width; length = rct.length; }6 s' R: o k& S0 n0 i P ) |) q! N2 N, R; }; z* J ~Rectangle(); public: int width, length; };6 J* f5 v7 o( Y; R Rectangle::Rectangle(). Y& c, W) w: B { cout << "默认矩形诞生了!" << endl; } Rectangle::Rectangle(int w, int l)+ Y+ T/ Z# ?4 Z {' ~/ I! K; H8 n C8 v y/ `7 J width = w;4 l. M6 w, A& n' K7 c length = l;6 q! O2 R* V7 M: W4 l) c cout << "指定矩形诞生了!" << endl; } Rectangle::~Rectangle() { cout << "矩形埋掉了!" << endl;# M i+ q1 U" w }; X+ S6 @ f4 `1 o6 c6 k9 [ int main()3 Q) A5 t3 H o9 i { ' B+ l1 ~; g( _" G4 q& [; Z1 ?& `6 w# F Rectangle rct1;9 [; k0 k1 K5 ~; z; c1 ^ Rectangle *pRct = new Rectangle(2,3);5 U0 }6 L7 h( C. k8 L/ ^) e. _ Rectangle rct2 = rct1; / N0 a3 r6 ?6 z9 T' y# s. [ return 0; }, l) i/ ^1 X' Y& M& \3 W( ~ L 这个简单的代码,实际运行的输出结果: 默认矩形诞生了!指定矩形诞生了!6 x" ]" K3 q. ]1 b. g 矩形埋掉了! 矩形埋掉了! 技术人总是喜欢眼见为实:因为看见,所以相信!,看看其对应的汇编代码(VC++ 2010汇编结果,这里仅贴出main函数,仅为理解原理,对于汇编指令不做描述,其中#为对汇编注释): 31: int main()4 @$ u8 U/ T/ `/ m4 `32: { 012C1660 55 push ebp $ i- x' T! V0 Q* n4 m+ y t! l 012C1661 8B EC mov ebp,esp 0 q3 Y6 Y }) A; Z3 b0 W# @6 d 012C1663 6A FF push 0FFFFFFFFh 012C1665 68 76 53 2C 01 push offset __ehhandler$_main (12C5376h) 9 P* U# {) k) E: Z 012C166A 64 A1 00 00 00 00 mov eax,dword ptr fs:[00000000h] 012C1670 50 push eax ) r/ ?2 H& |7 t 012C1671 81 EC 14 01 00 00 sub esp,114h ( D0 g2 h& H0 k! }: _7 v) t 012C1677 53 push ebx 012C1678 56 push esi 012C1679 57 push edi $ E) ]+ k0 R1 v! N. s6 w 012C167A 8D BD E0 FE FF FF lea edi,[ebp-120h] ! S- g+ I2 a' n: D/ N- U. T1 J 012C1680 B9 45 00 00 00 mov ecx,45h 1 d- {$ @8 s, R3 Q. B W0 i 012C1685 B8 CC CC CC CC mov eax,0CCCCCCCCh ( K' a1 s; {4 D 012C168A F3 AB rep stos dword ptr es:[edi] 012C168C A1 00 90 2C 01 mov eax,dword ptr [___security_cookie (12C9000h)] / p2 k; l) {# |1 z9 U 012C1691 33 C5 xor eax,ebp 012C1693 50 push eax , E. w4 \2 M- U9 o X+ ` 012C1694 8D 45 F4 lea eax,[ebp-0Ch] : p+ l I- q5 N( N 012C1697 64 A3 00 00 00 00 mov dword ptr fs:[00000000h],eax 33: Rectangle rct1; 012C169D 8D 4D E8 lea ecx,[ebp-18h] #调用默认构造函数管“生”. ^, E& @- s+ r3 j* a) I# E% F; Z 012C16A0 E8 32 FA FF FF call Rectangle::Rectangle (12C10D7h) 012C16A5 C7 45 FC 00 00 00 00 mov dword ptr [ebp-4],0 34: Rectangle *pRct = new Rectangle(2,3);# M, A2 P8 H( i# L 012C16AC 6A 08 push 8 012C16AE E8 41 FB FF FF call operator new (12C11F4h) 012C16B3 83 C4 04 add esp,4 1 ?/ y f' q1 L5 Q! D d/ k3 a. O 012C16B6 89 85 F4 FE FF FF mov dword ptr [ebp-10Ch],eax & }. F, K7 N+ t1 I" u- v" o- L1 s8 | 012C16BC C6 45 FC 01 mov byte ptr [ebp-4],1 012C16C0 83 BD F4 FE FF FF 00 cmp dword ptr [ebp-10Ch],0 . U7 n0 n7 ?4 O5 o& H 012C16C7 74 17 je main+80h (12C16E0h) 012C16C9 6A 03 push 3 #传参 012C16CB 6A 02 push 2 #传参 + }: Q& ^/ l$ C2 F% S6 {3 E 012C16CD 8B 8D F4 FE FF FF mov ecx,dword ptr [ebp-10Ch] #调用参数化构造函数3 F* z- X0 a. t2 Z$ E 012C16D3 E8 B8 FA FF FF call Rectangle::Rectangle (12C1190h) 1 j# Z3 U* P2 n) k+ X! {, h7 H0 t 012C16D8 89 85 E0 FE FF FF mov dword ptr [ebp-120h],eax 012C16DE EB 0A jmp main+8Ah (12C16EAh) 012C16E0 C7 85 E0 FE FF FF 00 00 00 00 mov dword ptr [ebp-120h],0 012C16EA 8B 85 E0 FE FF FF mov eax,dword ptr [ebp-120h] 2 D! b2 C# m5 `- @& ~- f9 |7 M 012C16F0 89 85 E8 FE FF FF mov dword ptr [ebp-118h],eax 012C16F6 C6 45 FC 00 mov byte ptr [ebp-4],0 ( }2 E+ |" K/ O5 P 012C16FA 8B 8D E8 FE FF FF mov ecx,dword ptr [ebp-118h] ' n( T2 Q* I F5 _! a1 t3 _/ d) r 012C1700 89 4D DC mov dword ptr [ebp-24h],ecx ) F" G3 \& a( S7 N 35: Rectangle rct2 = rct1; 012C1703 8D 45 E8 lea eax,[ebp-18h] 012C1706 50 push eax 012C1707 8D 4D CC lea ecx,[ebp-34h] 7 ?% `7 u0 w) X, D; A #调用拷贝构造函数( Z" w- J) k8 t( j5 D0 k 012C170A E8 3C F9 FF FF call Rectangle::Rectangle (12C104Bh) 36: ( U! j0 u+ V" B4 y1 D3 M5 p* U 37: return 0; 012C170F C7 85 00 FF FF FF 00 00 00 00 mov dword ptr [ebp-100h],0 - W/ B/ j- t4 i- W4 @ 012C1719 8D 4D CC lea ecx,[ebp-34h] #调用析构函数,销毁rct23 j6 H3 z& p8 j4 U6 R% k 012C171C E8 15 FA FF FF call Rectangle::~Rectangle (12C1136h) 012C1721 C7 45 FC FF FF FF FF mov dword ptr [ebp-4],0FFFFFFFFh ( ^$ r* J2 r8 z* |+ r: Z 012C1728 8D 4D E8 lea ecx,[ebp-18h] #调用析构函数,销毁rct1: c; I, v t+ p5 b. ?) M' c9 r 012C172B E8 06 FA FF FF call Rectangle::~Rectangle (12C1136h) 012C1730 8B 85 00 FF FF FF mov eax,dword ptr [ebp-100h] 38: }& i: G/ `, z" g/ x5 ]2 u 这里引发几个问题: 问题1:为什么先析构rct2,后析构rct1呢?
你如不信,将上述代码修改一下,测测: Rectangle::~Rectangle(){. [% E$ P& S" w" x; m9 x% m cout <<"当前宽为:" << width << "矩形埋掉了!" << endl; } int main()- N5 t4 H6 p% y) y, { { 2 U9 O* \8 ]& w9 Q, s: J" s Rectangle rct1;* A- B5 B7 O" N! ~' K7 i rct1.width = 1;7 B" S) }, T+ D1 I1 q. w4 G* w Rectangle *pRct = new Rectangle(2,3);" t0 I0 N- T% s- `1 y Rectangle rct2 = rct1; rct2.width = 2; return 0;4 n8 T! b! U g Q. `; a% p( ^ }* k$ w$ k, X* G. u$ j 其输出结果为: 默认矩形诞生了!指定矩形诞生了! 当前宽为:2矩形埋掉了!; ]- b5 h) p" e' G. [ 当前宽为:1矩形埋掉了! 问题2:请问上述代码,构造函数被调用了几次?析构函数又被调用了几次?这是经常面试会考察的基础知识。显然前面的打印以及给出了答案。 问题3:该代码有啥隐患?
所以应该修正一下: Rectangle::~Rectangle()+ j' E# u& L" H2 w{4 c+ o' P! |* I( A- F( E' f cout <<"当前宽为:" << width << "矩形埋掉了!" << endl; } int main() { 6 R2 @# i+ Q% J5 n3 k Rectangle rct1;) |. @0 x1 `- U) P rct1.width = 1;; w, k! g, S& n; o" \& r Rectangle *pRct = new Rectangle(2,3);) k, o4 A& U. u# Q6 r$ X: ] Rectangle rct2 = rct1; rct2.width = 3;' o7 ?& d: ^& k delete pRct; cout << "手动埋掉!" << endl;! E" \* w4 W* X' [1 V return 0; }- ~1 b+ P g! E- Z8 p 看看输出结果: 默认矩形诞生了!指定矩形诞生了!7 q& P# m8 n, Q L1 | 当前宽为:2矩形埋掉了!2 ~, w% d! E: H+ d y& J* Q0 X 手动埋掉!8 F. a; n/ N5 G$ O$ L. w 当前宽为:3矩形埋掉了! 当前宽为:1矩形埋掉了!9 d! `# h V b3 N4 k) E1 x 总结一下
对于拷贝构造函数,还有一个所谓深拷贝、浅拷贝的要点没有涉及,下次学习总结分享一下,敬请关注期待~,如发现文中有错误,敬请留言指正,不胜感激~ " @2 ^2 S) T3 F% D) e |
微信公众号
手机版