面向对象程序设计与c语言答案.docx

上传人:b****5 文档编号:30745526 上传时间:2023-08-20 格式:DOCX 页数:14 大小:26.58KB
下载 相关 举报
面向对象程序设计与c语言答案.docx_第1页
第1页 / 共14页
面向对象程序设计与c语言答案.docx_第2页
第2页 / 共14页
面向对象程序设计与c语言答案.docx_第3页
第3页 / 共14页
面向对象程序设计与c语言答案.docx_第4页
第4页 / 共14页
面向对象程序设计与c语言答案.docx_第5页
第5页 / 共14页
点击查看更多>>
下载资源
资源描述

面向对象程序设计与c语言答案.docx

《面向对象程序设计与c语言答案.docx》由会员分享,可在线阅读,更多相关《面向对象程序设计与c语言答案.docx(14页珍藏版)》请在冰豆网上搜索。

面向对象程序设计与c语言答案.docx

面向对象程序设计与c语言答案

面向对象程序设计与c语言答案

【篇一:

在过程式程序设计(①)、数据抽象程序设计(②)、面向对象程序设计(③】

紧扣教材和考试说明,从考生熟悉的基础知识入手,多角度、多层次地考查了学生的数学理性思维能力及对数学本质的理解能力,立足基础,先易后难,难易适中,强调应用,不偏不怪,达到了“考基础、考能力、考素质”的目标。

试卷所涉及的知识内容都在考试大纲的范围内,几乎覆盖了高中所学知识的全部重要内容,体现了“重点知识重点考查”的原则。

1.回归教材,注重基础

试卷遵循了考查基础知识为主体的原则,尤其是考试说明中的大部分知识点均有涉及,其中应用题与抗战胜利70周年为背景,把爱国主义教育渗透到试题当中,使学生感受到了数学的育才价值,所有这些题目的设计都回归教材和中学教学实际,操作性强。

2.适当设置题目难度与区分度

选择题第12题和填空题第16题以及解答题的第21题,都是综合性问题,难度较大,学生不仅要有较强的分析问题和解决问题的能力,以及扎实深厚的数学基本功,而且还要掌握必须的数学思想与方法,否则在有限的时间内,很难完成。

3.布局合理,考查全面,着重数学方法和数学思想的考察

在选择题,填空题,解答题和三选一问题中,试卷均对高中数学中的重点内容进行了反复考查。

包括函数,三角函数,数列、立体几何、概率统计、解析几何、导数等几大版块问题。

这些问题都是以知识为载体,立意于能力,让数学思想方法和数学思维方式贯穿于整个试题的解答过程之中。

【篇二:

c语言中的面向对象思想】

t>经常听见别人说面向对象的程序设计,以前在学校上课的时候,也有开面向对象程序设计这门课。

可是不幸的是,这些都是以c++,甚至vc++为基础的。

而更加不幸的是,多年以来我一直是一个c的使用者。

在学校的时候,我主要做的是硬件上的驱动层,和底层功能层。

在工作以后,又做的是手机上的软件开发,所有这些都是和c离不开的。

虽然我不得不说,c++是一门很好的语言,但是它的编译速度,代码效率,编译后的代码大小都限制了它在嵌入式上的应用。

(但现在的嵌入式cpu越来越快,内存容量变大。

我觉得用c++也应该没有什么问题。

这使我觉得似乎是嵌入式编译器的限制。

虽然菲利普和ti好像都有c++的编译器,但是似乎没人用这个。

难道是太贵了?

但不管怎么说,嵌入式应用中,c语言的普遍使用是肯定的)

那么在面向过程的时代产生的c语言能否使用面向对象的思想呢?

我认为是肯定可以的,c++不过是在语言级别上加入了对对象的支持,同时提供了丰富的对象库。

而在c语言下,我们只好自力更生了。

一、面向对象思想的目的是框架化,手段是抽象

相信很多人都明白面向对象讲了什么:

类,抽象类,继承,多态。

但是是什么原因促使这些概念的产生呢?

打个比方说:

你去买显示器,然而显示器的品牌样式是多种多样的,你在买的过程中发生的事情也是不可预测的。

对于这样的事情,我们在程序语言中如何去描述呢。

面向对象的思想就是为了解决这样的问题。

编写一个程序(甚至说是一个工程),从无到用是困难的,从有到丰富是更加困难的。

面向对象将程序的各个行为化为对象,而又用抽象的办法将这些对象归类(抽象),从而将错综复杂的事情简化为几个主要的有机组合(框架化)。

其实我们的身边很多东西都是这样组成的:

比如说电脑:

电脑是由主板,cpu加上各种卡组成的。

这就是一个框架化。

而忽略不同的cpu,不同的主板,不同的声卡,网卡,显卡的区别,这就是抽象。

再比如说现在的教育网:

是由主核心节点:

清华,北大,北邮等几个,然后是各个子节点,依次组成了整个教育网网络。

所以我觉得面向对象的编程思想就是:

一个大型工程是分层次结构的,每层又由抽象的结构连接为整体(框架化),各个抽象结构之间是彼此独立的,可以独立进化(继承,多态)。

层次之间,结构之间各有统一的通讯方式(通常是消息,事件机制)。

二、以前c语言编程中常用的“面向对象”方法

其实c语言诞生以来,人们就想了很多办法来体现“面向对象”的思想。

下面就来说说我所知道的方法。

先说一些大家熟悉的东东,慢慢再讲诡异的。

呵呵

1.宏定义:

有的人不禁要问,宏定义怎么扯到这里来了,我们可以先看一个简单的例子:

#definemacrofunctionafunction

然后在程序里面你调用了大量的afunction,但是有一天,你突然发现你要用bfunction了,(不过afunction又不能不要,很有可能你以后还要调用),这个时候,你就可以#definemacrofunctionbfunction来达到这样的目的。

当然,不得不说这样的办法是toosimple,sometimena?

ve的,因为一个很滑稽的问题是如果我一般要改为bfunction,一半不变怎么办?

那就只好查找替换了。

2.静态的入口函数,保证函数名相同,利用标志位调用子函数:

这样的典型应用很多,比如说网卡驱动里面有一个入口函数nilan(intfunctioncode,para*)。

具体的参数是什么记不清楚了。

不过nilan的主体是这样的:

longnilan(intfunctioncode,para*)

{

switch(functioncode){

casesendpacket:

send(….)

casereceivepacket:

receive(…)

…..

}

写到这里大家明白什么意思了吧。

保证相同的函数名就是说:

网卡驱动是和pna+协议栈互连的,那么如何保证pna+协议栈和不同的驱动都兼容呢,一个简单的办法就是仅仅使用一个入口函数。

通过改变如果函数的参数值,来调用内部的各个函数。

这样的做法是可以进化的:

如果以后想调用新的函数,增加相应的函数参数值就好了。

如果我们将网卡驱动和pna+协议栈看作两个层的话,我们可以发现:

层与层之间的互连接口是很小的(这里是一个入口函数),一般是采用名字解析的办法而不是具体的函数调用(利用functioncode调用函数,nilan仅仅实现名字解析的功能)!

接口限制和名字解析:

接口限制:

层与层之间仅仅知道有限的函数

名字解析:

层与层之间建立共同的名字与函数的对应关系,之间利用名字调用功能。

3.callback函数。

我觉得这是c语言的一个创举,虽然它很简单,就象如何把鸡蛋竖起来一样,但是你如果没想到的话,嘿嘿。

如果说静态入口函数实现了一个可管理的宏观的话,callback就是实现了一个可进化的微观:

它使得一个函数可以在不重新编译的情况下实现功能的添加!

但是在最最早期的时候,也有蛮多人持反对态度,因为它用了函数指针。

函数指针虽然灵活,但是由于它要访问内存两次才可以调用到函数,第一次访问函数指针,第二次才是真正的函数调用。

它的效率是不如普通函数的。

但是在一个不太苛刻的环境下,函数调用本身就不怎么耗时,函数指针的性能又不是特别糟糕,使用函数指针其实是一个最好的选择。

但是函数指针除了性能,最麻烦的地方就是会导致程序的“支离破碎”。

试想:

在程序中,你读到一个函数指针的时候,如果你愣是不知道这个函数指针指向的是哪个函数,那个感觉真的很糟糕。

(可以看后面的文章,要使用先进的程序框架,避免这样的情况)

三、event和message

看了上面的描述,相信大家多少有些明白为什么要使用event和message了。

具体的函数调用会带来很多的问题(虽然从效率上讲,这样做是很好的)。

为了提高程序的灵活性,event

和message的办法产生了。

用名字解析的办法代替通常的函数调用,这样,如果双方对这样的解析是一致的话,就可以达到一个统一。

不过event和message的作用还不仅仅是如此。

event和message还有建立进程间通信的功能。

进程将自己的消息发给“控制中心”(简单的就是一个消息队列,和一个while循环不断的取消息队列的内容并执行),控制程序得到消息,分发给相应的进程,这样其他进程就可以得到这个消息并进行响应。

event和message是很灵活的,因为你可以随时添加或者关闭一个进程,(仅仅需要添加分发消息的列表就可以了)event和message从程序实现上将我觉得是一样的,只不过概念不同。

event多用于指一个动作,比如硬件发生了什么事情,需要调用一个什么函数等等。

message多用于指一个指示,比如什么程序发生了什么操作命令等等。

四、小结

其实编程序和写文章一样,都是先有一个提纲,然后慢慢的丰富。

先抽象化得到程序的骨架,然后再考虑各个方面的其他内容:

程序极端的时候会发生什么问题?

程序的这个地方的功能现在还不完善,以后再完善会有什么问题?

程序是不是可以扩展的?

【篇三:

c语言的模块化设计和面向对象编程】

=txt>分类:

c面向对象/c++/java2011-03-0221:

04108人阅读评论(0)收藏举报

来自网易杭州研发技术总监“云风”blog的几篇面向对象设计的文章

c语言对模块化支持的欠缺

继续昨天的话题。

随便列些以后成书可能会写的东西。

既然书的主题是:

怎样构建一个(稍具规模的)软件。

且我选择用c为实现工具来做这件事情。

就不得不谈语言还没有提供给我们的东西。

模块化是最高原则之一(在《unix

编程艺术》一书中,unix哲学第一条即:

模块原则),我们就当考虑如何简洁明快的使用c语言实现模块化。

除开c/c++,在其它现在流行的开发语言中,缺少标准化的模块管理机制是很难想象的。

但这也是c语言本身的设计哲学决定的:

把尽可能多的可能性留给程序员。

根据实际的系统,实际的需要去定制自己需要的东西。

对于巨型的系统(比如windows这样的操作系统),一般会考虑使用一种二进制级的模块化方案。

由模块自己提供元信息,或是使用统一的管理方案(比如注册表)。

稍小一点的系统(我们通常开发接触到的),则会考虑轻量一些的源码级方案。

首先要考虑的往往是模块的依赖关系和初始化过程。

依赖关系可以放由链接器或加载器来解决。

尤其在使用c语言时,简单的静态库或动态库,都不太会引起大的麻烦。

c++则不然,c++的某些特性(比如模板类静态成员的构造)必须对早期只供c语言使用的链接器做一些增强。

即使是精心编写的c++库,也有可能出现一些意外的bug。

这些bug往往需要对编译,链接,加载过程很深刻的理解,才能查出来。

注:

我并不想以此来反对使用c++做开发。

我们需要着重管理的,是模块的初始化过程。

对于打包在一起的一个库(例如glibc,或是msvcrt),会在加载时有初始化入口,以及卸载时有结束代码。

我想说的不是这个,而是我们自己内部拆分的更小的模块的相互依赖关系。

谁先初始化,谁后初始化,这是一个问题。

在c++的语言级解决方案中,使用的是单件模块。

要么由链接器决定以怎样的次序来生成初始化代码,这,通常会因为依赖关系和实际构造次序不同而导致bug(注:

我在某几本c++书中都见过,待核实。

自己好久不写c++也没有实际的错误例子);要么使用惰性初始化方案。

这个惰性初始化也不是万能的,并且有些额外的开销。

(多线程环境中尤其需要注意)

我使用c语言做初期设计的时候,采用的是一种足够简单的方法。

就是,以编码规范来规定,每个模块必须存在一个初始化函数,有规范的名字。

比如foo模块的初始化入口叫intfoo_init()

规定:

凡使用特定模块,必须调用模块初始化函数。

为了避免模块重复初始化,初始化函数并不直接调用,而是间接的。

类似这样:

mod_using(foo_init);

mod_using负责调用初始化函数,并保证不重复调用,也可以检查循环依赖。

在这里,我们还约定了初始化成功于否的返回值。

(在我们的系统中,返回0表示正确,1表示失败)然后定义了一个宏来做这个使用。

#defineusing(m)if(mod_using(m##_init,#m)){return1;}

注:

我个人反对滥用宏。

也尽可能的避免它。

这里使用宏,经过了慎重的考虑。

我希望可以有一个代码扫描器去判断我是否漏掉了模块初始化(可能我使用了一个模块,但忘记初始化它)。

宏可以帮助代码扫描分析器更容易实现。

而且,使用宏更像是对语言做的轻微且必要的扩展。

这样,我的系统中模块模块的实现代码最后,都有一个init函数,里面只是简单的调用了using来引用别的模块。

例如:

#includemodule.h

/*

我个人偏爱把module.h的引入放在源文件最后,初始化入口之前。

它里面之定义了using宏,以及相关管理函数。

这样做是为了避免在代码的其它地方去引入别的模块。

*/

int

foo_init()

{

using(memory);//引用内存管理模块

using(log);//引用log模块

return0;

}

至于模块的卸载,大部分需求下是不需要的。

今天在这里就不论证这一点了。

浅谈c语言中模块化设计的范式

今天继续谈模块化的问题。

这个想慢慢写成个系列,但是不一定连续写。

基本是想起来了,就整理点思路出来。

主要还是为以后集中整理做点铺垫。

我们都知道,层次分明的代码最容易维护。

你可以轻易的换掉某个层次上的某个模块,而不用担心对整个系统造成很大的副作用。

层次不清的设计中,最糟糕的一种是模块循环依赖。

即,分不清两个模块谁在上,谁在下。

这个时候,最容易牵扯不清,其结果往往是把两者看做一体去维护算了。

这里面还涉及一些初始化次序等繁杂的细节。

其次,就是越层的模块联系。

当模块a是模块b的上层,而模块b又是模块c的上层,这个时候,让模块c对模块a可见,在模块a中有对c导出接口的直接调用,对于清晰的设计是很忌讳的一件事。

虽然,我们很难完全避免这个问题,去让a对c的调用完全通过b。

但通常应尽力为之。

(注:

以后写书的话,我争取补充一些实际的例子来说明)不过,对语言不原生支持的数据类型,以及基础设施,但却有必要创造出来给系统用的。

可以有些例外。

比如内存管理,log管理,字符串(c语言用原始库函数管理比较麻烦)等等,我们可能以基础模块的形式提供。

但却可能被不同层次的模块直接使用。

但,上到一定层次后,还是需要去隐藏它们的。

下面来一点更实际的分析。

以c语言为例,由于c语言缺乏namespace的原生支持,我们通常给api加上统一前缀来区分。

这倒也不麻烦。

那么模块a看起来就是一堆a_xxxxx为名字的方法。

我个人主张单个模块不宜过大,在实现时适合放在同一个.c文件里即可。

通常,一个模块会围绕一类对象处理。

这些对象可以用整数handle来表示,也可以用一个特定类型的对象指针。

两种方案各有千秋。

先来谈

对象指针的方案。

一个模块a的接口描述文件很可以是这样的(希望以后能补上更现实的代码):

#ifndef_a_h

#define_a_h

structa;

structb;

structa*a_create(void);

voida_release(structa*self);

voida_bind(structa*self,structb*b);

voida_commit(structa*self);

voida_update(void);

inta_init(void);

#endif

这里,我们定义了a这种数据类型。

我个人反对用typedef或宏来减少代码输入。

除非有特别的理由,都写上struct前缀,而不是定义出新类型。

尤其是在较底层的模块设计时更是如此。

在接口描述时,structa的细节是绝对不应该暴露出来的,它的数据结构应该仅存在于实现的文件a.c中。

关于a的接口通常分两类,一类是对structa*做一些处理的,那么就让第一个参数传入self指针。

这相当于c++的this指针。

比如上例中的a_commit;另一类接近于c++类的静态成员函数,通常用于对这一类对象全部做一个处理,如a_update。

注:

我无意用c去模拟c++,但基于一类数据类型做一些处理的方法,对于c,这样的写法也是一个常规的范式而已。

至于面向对象等在构建复杂系统时常用到的方法,以后我会谈谈我自己常用的另一些范式。

或许像c++,也可以不像。

怎么写更好,是个见任见智的问题。

不用过于拘泥。

这里的例子中,我们还提到了另一个数据类型b。

显然,它是放在b模块中的。

我们通常不会在a.h中去includeb.h,而只是声明一下structb。

(对于c语言来说,这并不必要,但写上是个好习惯)。

这是因为,如果b是位于a之下的模块,既在a模块的实现中,会用到b的方法,我们通常不会让用到a模块的人,可以看见b的接口。

包含a.h的同时隐式包含b.h就是不必要的了。

从范例代码中,我们可以猜想,structa是对structb的某种封装,可以通过对a的操作,间接操作到其中的b类型。

在a的模块初始化a_init中一定就会初始化b了。

如果是这样,b的层次就位于a之下。

往往structb中还会保留一个structa类型的引用。

首先,我们应该尽力避免这种情况。

即:

位于下层的b应该对上层的a一无所知是最好的。

如果在b模块中必须出现structa,那么我们应该至少保证,仅仅是structa*,一个引用,而绝对不能出现任何对a模块内接口的调用。

不要认为使用巧妙的方法,绕过循环依赖初始化问题就够了。

这应该是一个设计原则,不要去违反。

btw,草率的接口设计往往是日后系统脆弱的根源。

图一时之快,随意暴露一些接口,或是自以为聪明的用一些“巧妙”的方法,甚至是语法糖来绕过设计原则,都是很危险的。

一个常见的难处理的问题是:

如果structa和structb相互有双向引用。

怎样建立这个引用关系?

这个建立的过程,到底是a的方法,还是b的方法?

我的答案是,谁在上层,就是谁的方法。

但是a和b相互都看不见内部数据布局的细节,让b的内部对a类型做一个引用,比如也需要从b模块中暴露一个接口出来。

这个接口,可能仅供a使用。

在这个例子里,

就是仅供a_bind这个方法去使用。

如果是c++,我们或许会采用friend。

也可能使用其它一些技巧。

反正c++里可以挖掘的语法太多了。

但c怎么办?

下面给个我自己的方案。

原本,我们在b中导出的api是这样的:

voidb_set_a(structb*self,structa*a);

现在写成:

structi_a;

voidb_set_a(structb*self,structi_a*a);

在b.c的实现中,加一个函数用于structi_a*到structa*的转换。

staticinlinestructa*a(structi_a*a){return(structa*)a;}

然后在a.c的实现中,加一个类似函数用于转换structa*到structi_a*。

这样,在a.c之外,其它模块因为不能得到任何structi_a类型,而不会错误的使用b_set_a这个接口了。

我所偏爱的c语言面向对象编程范式

面向对象编程不是银弹。

大部分场合,我对面向对象的使用非常谨慎,能不用则不用。

相关的讨论就不展开了。

但是,某些场合下,采用面向对象的确是比较好的方案。

比如ui框架,又比如3d渲染引擎中的场景管理。

c语言对面向对象编程并没有原生支持,但没有原生支持并不等于不适合用c写面向对象程序。

反而,我们对具体实现方式有更多的选择。

大部分用c写面向对象程序的程序员受c++影响颇深。

企图用宏模拟出一个常见c++编译器已经实现的对象模型。

于我愚见,这并不是一个好的方向。

c++的对象模型,本质上是为了追求实现层的性能,并直接体现出来。

就有如在c++中被滥用的inline,的确有效,却破坏了分离原则。

c++的继承是过紧的耦合。

我所理解的面向对象,是让不同的数据元有共同的操作方式,适合成组的处理。

根据操作方式的不同,我们会对数据元做不同的分组。

一个数据可能出现在这个组里,也可以出现在那个组里。

这取决于你从不同的方面提取的共性。

这些可供统一操作的共性称之为接口(interface),接口在c语言中,表现为一组函数指针的集合。

放在c++中,即为虚表。

我所偏爱的面向对象实现方式(使用c语言)是这样的:

若有一组数据,我们需要让他们看起来都有一种叫作foo的共性。

把符合这样的数据都称为foo_object。

通常,我们会有如下api去操控foo_object。

structfoo_object;

structfoo_object*foo_create();

voidfoo_release(structfoo_object*);

voidfoo_dosomething(structfoo_object*);

在具体实现时,会在一个叫foo.c的实现文件中,定义出foo_object结构,里面有一些foo_dosomething所需的数据成员。

但是,以上还不能满足要求。

因为,我们会有不同的数据,他们只是表现出foo_object某些方面的特性。

对于不同的数据,它们在dosomething时,实际所做的操作也有所区别。

这时,我们需要定义出一个接口,供foo.c内部使用。

那么,以上的头文件就需要做一些修改,把接口i_foo的定义加进去,并修改create函数。

structi_foo{

void(*foobar)(void*);

};

structfoo_object*foo_create(structi_foo*iface,void*data);

这里稍做解释。

i_foo是供foo_dosomething内部使用的一组接口。

构造foo_object时,我们把一个外部数据data和为foo_object相关特性定义出的i_foo接口捆绑在一起,传入构造函数foo_create。

一般,我还会会每个符合foo_object特性的对象实现一个方法来得到对应的i_foo,如:

structfoobar;

structi_foo*foobar_foo(void);

structfoobar*foobar_create(void);

voidfoobar_release(structfoobar*);

创建一个foo_object对象的代码看起来是这样:

structfoobar*foobar=foobar_create();

structfoo_object*fobj=foo_create(foobar_foo(),foobar);

structfoo_object的定义中,必然要记录i_foo的接口指针和data数据指针。

从c++的观点看,foo_object是基类,它也会有一些基类成员和非虚的

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 农学

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

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