ImageVerifierCode 换一换
格式:DOCX , 页数:16 ,大小:58.03KB ,
资源ID:20692417      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/20692417.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(笨鸟先飞学编程系列浅析C++的封装性文档格式.docx)为本站会员(b****6)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

笨鸟先飞学编程系列浅析C++的封装性文档格式.docx

1、 m_nSecNum = 0; /构造函数 CExample() / 空析构;当然,上面这个类的定义是不是很像定义一个结构体?只不过多了个private和public还有一些函数。是的,C+里面,将结构体升级了,结构体里面可以有函数成员了,为了兼容,换了个关键字,当然,上面的这个class完全可以改成struct,一点问题都没有。好奇的朋友会问:如果函数体的语句太多,逻辑复杂了,函数很大,那这个类岂不是很难看,太臃肿了吧。是的,为了方便类的组织,也为了协调项目工程中文件的组织。上面的类还可以写成如下的形式:/ .h文件中写如下的声明部分 / 权限控制,防止外面直接操作这些变量,相关内容在下面的

2、小节中详细讲述 int GetSum() const; / 成员函数 bool SetNum(int nFirst, int nSec); CExample(); /构造函数 CExample(); / 空析构/ .cpp 文件中写如下的定义及实现部分CExample:CExample()CExample()int CExample:GetFirstNum() const return m_nFirstNum;GetSecNum() const return m_nSecNum;bool CExample:SetNum(int nFirst, int nSec)GetSum() const r

3、eturn m_nFirstNum+m_nSecNum;上面两种写法也是有区别的,第一种方法写的函数具有Inline函数的特性。后一种则没有。2、 属性和方法的使用 C+中定义一个对象跟定义一个函数没有什么区别。#include #include Example.hint main(int argc, char* argv) CExample obj_Exp; / 定义一个对象 obj_Exp.SetNum(10, 20); / 调用一个方法 printf(%d+%d = %drn, obj_Exp.GetFirstNum(), obj_Exp.GetSecNum(), obj_Exp.Get

4、Sum(); return 0; 由此,我们就可以通过一个函数间接的来操作我们的变量,用户在给我们的变量赋值时,我们可以通过Set函数来对输入的内容作检测,在获取一个变量的内容时,我们可以通过一个函数来取得,这样都提高了程序安全性。也许,有的朋友会说,如果我绕过你提供的函数,直接对_nFirstNum;和 m_nSecNum;进行操作不是一样不安全么,是的,这就是为什么我在程序中加上了private 的原因。下面我们详细说明下这些关键字的含义。private、public、protected三个关键字,是C+提供给并实现类封装的关键,它们用来说明类外的代码可以直接访问类内的什么哪些成员,哪些成

5、员不可能被外面访问。private:说明,它后面所有的变量和函数,都不可能被类外访问,只能在类内被使用。public:说明,它后面的所有变量和函数可以被类外的代码所访问,没有任何限制。protected:说明,它后面的所有变量和函数,只能被自己或自己派生的类所使用。不能被类外的代码使用。 这个我们将在C+继承性中详细讨论。 从程序设计的角度来讲,如果我们以类为单位编码的话,每个模块都是独立的我们只要关注与本类相关操作,比如人这个类,它一般情况下有两个眼睛、一个嘴巴等之类的属性,人可以使用工具,可以行走,可以跳跃等方法。我们编写的所有的函数都是针对这些展开的。而不用去关心谁要使用这个类。因此,类

6、/对象概念的加入,不单单是给编码方式做了改变,主要是设计思路的改变,程序模块化的改变等等。3、 关于常成员函数相信我们讲过的const与inline相关的知识,大家一定都没有忘记,是的,你猜对了,现在我们要说的就是const的一个扩展用法。当然,不用担心,这只是一个小小的扩展,不用担心混淆杂乱:当我们的成员函数不允许修改我们类中的成员内容时,可以在函数的参数列表后加上一个const关键字。以免以后不小心更改了我们的类中成员属性。二、 解析对象的内存结构 现在,我相信,如果习惯了我这种学习方式的朋友一定会很好奇,类定义对象的内存格式是怎样的,它是不是像一个普通变量那样,或者是不是像一个结构体变量

7、那样在内存里连续的将各个成员组织到一起,它又是怎样将各个成员变量还有函数绑定到一起的?变量和函数在一起它是怎么组织的?本小节让我们来解决这些问题。 为节省篇幅,我们仍旧使用上面的代码。我们用VC的调试器,调试这个代码: 注意看我们的变量监视区,我们定义的对象的内容跟结构体成员的内容格式差不多,(是按照定义的顺序连续存放的,这点跟普通的局部变量不一样,普通的局部变量在内存中的顺序与定义顺序相反)内存中只存放了成员变量,它并没有标出SetNum的位置,那它是怎么找到SetNum这个函数的呢? 根据我们先前调试C函数的经验,我们知道,函数的代码是被放在可执行文件的代码区段中的。在这个代码中,也有调用

8、SetNum的代码,我们详细的跟一下它的汇编代码:10:004011ED lea ecx,ebp-14h004011F0 call ILT+15(CExample:CExample) (00401014)004011F5 mov dword ptr ebp-4,011:004011FC push 14h004011FE push 0Ah00401200 lea ecx,ebp-14h00401203 call ILT+0(CExample:SetNum) (00401005) 这段代码又给我们带来了新的问题,我们只用类定义了一个对象(变量),它自动的调用了一个函数,根据注释我们知道它调用的是构

9、造函数。我们跟进去看下: CExample:12: 00401050 push ebp00401051 mov ebp,esp00401053 sub esp,44h00401056 push ebx00401057 push esi00401058 push edi00401059 push ecx ; 保存寄存器环境0040105A lea edi,ebp-44h0040105D mov ecx,11h00401062 mov eax,0CCCCCCCCh 00401067 rep stos dword ptr edi ; 将栈空间清为CC(Release编译就没有这部分代码了。)0040

10、1069 pop ecx 0040106A mov dword ptr ebp-4,ecx ; 将 ECX中的内容给局部变量13: 0040106D mov eax,dword ptr ebp-4 ; 将ECX的内容返回00401070 pop edi00401071 pop esi00401072 pop ebx00401073 mov esp,ebp00401075 pop ebp00401076 ret 这段代码,首次看还真看不出个所以然来,源码的构造函数中,我们什么都没写,是个空函数,而这里做的是返回ECX的值,可是这个函数也没有对ECX做什么特别的操作,而是直接使用进函数时ECX的值

11、。那只能说明在调用这个函数前,ECX发生了变化。我们再回头看下调用构造函数的代码: 哈哈,它是把我们obj_Exp对象的地址给了ECX,然后调用构造返回的,也就是说,构造的返回值是我们对象的首地址。哎,迷糊了,真搞不懂这是在干什么。先不管他,我们继续看怎么调用的SetNum这个函数吧:004011FC push 14h ; 传递参数00401200 lea ecx,ebp-14h ; 也有这句,还是把我们的对象首地址给ECX29: bool CExample:30:00401130 push ebp00401131 mov ebp,esp00401133 sub esp,44h00401136

12、 push ebx00401137 push esi00401138 push edi00401139 push ecx0040113A lea edi,ebp-44h0040113D mov ecx,11h00401142 mov eax,0CCCCCCCCh00401147 rep stos dword ptr edi00401149 pop ecx0040114A mov dword ptr ebp-4,ecx ; 备份一下我们的对象首地址31: m_nFirstNum = nFirst;0040114D mov eax,dword ptr ebp-4 ; 取出对象首地址00401150

13、 mov ecx,dword ptr ebp+8 ; 取出nFirst参数00401153 mov dword ptr eax,ecx ; 给对象首地址指向的内容赋值为nFirst的内容32: m_nSecNum = nSec ;00401155 mov eax,dword ptr ebp-4 ;00401158 mov ecx,dword ptr ebp+0Ch; 取出nSec参数0040115B mov dword ptr eax+4,ecx ; 给对象首地址+4指向的你内容赋值 return true;0040115E mov al,1 ; 返回134:00401160 pop edi0

14、0401161 pop esi00401162 pop ebx00401163 mov esp,ebp00401165 pop ebp00401166 ret 8 我简要的注释下来一下上面的代码。通过分析上面的代码,我们可以得出这样的结论:A、 函数通过ecx传递了我们对象的首地址。B、 函数通过ecx传递的对象首地址定位对象的每个成员变量。 这样,很明显,ECX起到了传递参数的作用,这时ecx中的地址有个专业术语,叫做this指针。 OK,这就是一个新的知识点,我们成员函数的调用方式。1、 成员函数的调用方式: _thiscall 记得在前面章节介绍函数时,讲过一些调用方式,但是没有提到过这

15、种调用方式。下面我做一个简要的总结:A、 参数也通过栈传递。B、 它用一个寄存器来传递this指针。C、 本条特性摘自加密与解密(第三版)非原文:a) 对于VC+中传参规则:i. 最左边两个不大于4字节的参数分别用ECX和EDX传参数.ii. 对于浮点数、远指针、_int64类型总是通过栈来传递的。b) 对于BC+|DELPHI中的传递规则:i. 最左边三个不大于DWORD的参数,依次使用EAX,ECX,EDX传递,其它多的参数依次通过PASCAL方式传递。 这样,函数的地址还是在代码区域,对象的内存中只存放数据成员,当我们要调用成员函数时,就通过一个寄存器将函数操作的对象的首地址(也就是th

16、is指针)传递过去就可以了,传递不同的对象指针,就操作不同的数据。哈哈,太巧妙了。2、 浅谈构造与析构函数 OK,继续调试代码:, obj_Exp.GetFirstNum(),14: obj_Exp.GetSecNum(),15:00401208 lea ecx,ebp-14h0040120B call ILT+30(CExample:GetSum) (00401023) ; 调用GetSum函数00401210 push eax00401211 lea ecx,ebp-14h00401214 call ILT+5(CExample:GetSecNum) (0040100a)00401219

17、push eax0040121A lea ecx,ebp-14h0040121D call ILT+10(CExample:GetFirstNum) (0040100f)00401222 push eax00401223 push offset string (0042501c)00401228 call printf (00401290)0040122D add esp,10h16:17:00401230 mov dword ptr ebp-18h,000401237 mov dword ptr ebp-4,0FFFFFFFFh0040123E lea ecx,ebp-14h00401241

18、 call ILT+20(CExample:CExample) (00401019) ; 调用析构函数00401246 mov eax,dword ptr ebp-18h 我们至始至终都没有调用过构造和析构函数。但是,通过这次调我们知道,在创建一个对象(变量)的时候,我们的程序会自动的调用我们的构造函数,在要出对象作用域的时候,会自动的调用析构函数。 这样,我们很容易就能想象出,构造和析构的用途:构造就做初始化对象的各个成员,申请空间等初始化工作。析构就做一些释放申请的空间啊之类的清理工作。 就这样,C+将数据跟函数封装到了一起,这样我们每个类产生的对象都是一个独立的个体,它有一个自己的运作方

19、式,几乎完全独立。在我们使用它的时候,根本不需要它是怎么实现了,只要知道怎么使用即可。三、 浅谈类的静态成员 通过前面几节的学习,我们大概的能理解类的封装性及其运作过程,但是,如果我们继续深入的学习C+,我们很快就能发现一个问题:我们上面说的所有的成员都是属于对象的,也就是说,我们必须先通过类来定义一个对象才可以操作。但是有的时候,我们需要一些属于类的成员,比如:人都有一个脑袋,这一个脑袋属于人类共有的特性。不需要具体到哪一个人,我们都可以确定人只有一个脑袋。 放到类中也一样,比如我们需要知道当前这个类创建了几个对象的时候,我们不必在创建一个新的对象只需要使用类的相关函数或者直接访问类的某些属

20、性就可以了,而这些函数或者变量,它肯定不可能属于某个对象,它应该属于这个类本身。 OK,下面就来体验一下静态带给我们的一些好处。同样,我们将前面的代码添加点儿东西(见Exp02): / .h文件中 static int m_nCount; / 统计产生对象的 static int print(const char *szFormat, .); / 让我们的类有自己的输出函数 / .cpp文件中m_nCount = 0; / 初始化静态成员变量 m_nCount+; / 当创建一个对象的时候,这个变量加1 if (m_nCount 0) m_nCount-; / 当对象销毁时,这个变量减1/*/

21、*让我们的CExample可以打印自己的信息/*支持多参,同printf用法相同print(const char *szFormat, .) if (!szFormat) va_list pArgs; char szBuffer256 * 15 = 0; va_start(pArgs, szFormat); vsprintf(szBuffer, szFormat, pArgs); va_end(pArgs); printf(szBuffer); return strlen(szFormat); 好,有了这些,我们可以编写如下的测试代码:stdafx.h CExample obj_Exp1;pr

22、int(当前对象的数量为:%drn, CExample:m_nCount); if (1) CExample obj_Exp2; / 该对象属于if作用域,出了if,对象自动销毁 我想大家应该能想象出来运行的结果: 好,我们调试一下这段程序:004012EC mov eax,CExample:m_nCount (0042ae6c) ;这明显告诉我们,静态就是全局004012F1 push eax004012F2 push offset string %drn 004012F7 call ILT+30(CExample:print) (00401023) ; 调用该静态函数没有传递this指针0

23、04012FC add esp,8 多了不用看了,通过这段代码,我们很明显就可以清楚,静态变量,不属于类对象,它存放于全局数据区,同全局变量在一个地方(更多关于静态变量的相关说明见我发的static学习笔记一文)。 静态函数,跟全局函数一样,它虽然在源码中书写与类内,但是它其实就是一个全局函数,不传递this指针,因此,在使用静态函数时需要知道,静态函数中不能调用其它普通的成员函数也不能引用普通的成员变量。但是反过来,在其它的成员函数中可以调用静态函数也可以使用静态变量。四、 说一下初始化列表现在让我们考虑一个问题:倘若我们的类中有其它类成员、有引用成员、有常量成员时,应该怎么初始化它们呢?是

24、啊,如果我们在头文件中声明它们的时候,直接给它们初始化很明显是要报错的。这时,我们的主角,初始化列表就上场了。(见Exp03):假如,我们有如下一个类:class CBaseExp CBaseExp(char *pszStr) int nLength = strlen(pszStr)+1; if (1 = nLength) return ; m_pszBuff = new charnLength; strcpy(m_pszBuff, pszStr); CBaseExp(); char *m_pszBuff; / 需要临时分配堆空间现在,我们在我们的CExample中增加如下几个成员: / 为了描述方便,我直接使用public方式。 CBaseExp m_ObjBE; / 这个对象需要初始化(用到初始化列表) int& m_nFirst; / 需要初始化(用到初始化列表)

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1