为了正常的体验网站,请在浏览器设置里面开启Javascript功能!
首页 > C语言中的面向对象

C语言中的面向对象

2012-08-15 20页 doc 104KB 21阅读

用户头像

is_101780

暂无简介

举报
C语言中的面向对象  一 、面向对象思想   一、 面向对象思想的目的是框架化,手段是抽象 相信很多人都明白面向对象讲了什么:类,抽象类,继承,多态。但是是什么原因促使这些概念的产生呢? 打个比方说:你去买显示器,然而显示器的品牌样式是多种多样的,你在买的过程中发生的事情也是不可预测的。对于这样的事情,我们在程序语言中如何去描述呢。面向对象的思想就是为了解决这样的问题。编写一个程序(甚至说是一个工程),从无到用是困难的,从有到丰富是更加困难的。面向对象将程序的各个行为化为对象,而又用抽象的办法将这些对象归类(抽象),从而将错综复杂的...
C语言中的面向对象
 一 、面向对象思想   一、 面向对象思想的目的是框架化,手段是抽象 相信很多人都明白面向对象讲了什么:类,抽象类,继承,多态。但是是什么原因促使这些概念的产生呢? 打个比方说:你去买显示器,然而显示器的品牌样式是多种多样的,你在买的过程中发生的事情也是不可预测的。对于这样的事情,我们在程序语言中如何去描述呢。面向对象的思想就是为了解决这样的问题。编写一个程序(甚至说是一个工程),从无到用是困难的,从有到丰富是更加困难的。面向对象将程序的各个行为化为对象,而又用抽象的办法将这些对象归类(抽象),从而将错综复杂的事情简化为几个主要的有机组合(框架化)。 其实我们的身边很多东西都是这样组成的:比如说电脑:电脑是由主板,CPU加上各种卡组成的。这就是一个框架化。而忽略不同的CPU,不同的主板,不同的声卡,网卡,显卡的区别,这就是抽象。再比如说现在的教育网:是由主核心节点:清华,北大,北邮等几个,然后是各个子节点,依次组成了整个教育网网络。 所以我觉得面向对象的编程思想就是:一个大型工程是分层次结构的,每层又由抽象的结构连接为整体(框架化),各个抽象结构之间是彼此独立的,可以独立进化(继承,多态)。层次之间,结构之间各有统一的通讯方式(通常是消息,事件机制)。 二、 以前 C 语言编程中常用的“面向对象”方法 其实C语言诞生以来,人们就想了很多办法来体现“面向对象”的思想。下面就来说说我所知道的方法。先说一些大家熟悉的东东,慢慢再讲诡异的。呵呵 1. 宏定义: 有的人不禁要问,宏定义怎么扯到这里来了,我们可以先看一个简单的例子: #define MacroFunction Afunction 然后在程序里面你调用了大量的AFunction,但是有一天,你突然发现你要用BFunction了,(不过AFunction又不能不要,很有可能你以后还要调用),这个时候,你就可以#define MacroFunction Bfunction来达到这样的目的。 当然,不得不说这样的办法是too simple,sometime naïve的,因为一个很滑稽的问题是如果我一般要改为BFunction,一半不变怎么办? 那就只好查找替换了。 2. 静态的入口函数,保证函数名相同,利用标志位调用子函数: 这样的典型应用很多,比如说网卡驱动里面有一个入口函数Nilan(int FunctionCode,Para*)。具体的参数是什么记不清楚了。不过NiLan的主体是这样的: Long Nilan(int FunctionCode,Para*){ Switch(FunctionCode){ Case SendPacket: send(….) Case ReceivePacket: 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多用于指一个指示,比如什么程序发生了什么操作命令等等。 四、 小结 其实编程序和写文章一样,都是先有一个提纲,然后慢慢的丰富。先抽象化得到程序的骨架,然后再考虑各个方面的其他内容:程序极端的时候会发生什么问题?程序的这个地方的功能现在还不完善,以后再完善会有什么问题?程序是不是可以扩展的? 二、类模拟的性能    类模拟中使用了大量的函数指针,结构体等等,有必须对此进行性能分析,以便观察这样的结构对程序的整体性能有什么程度的影响。   1.函数调用的开销 #define COUNTER XX void testfunc() {     int i,k=0;     for(i=0;i
,大家有什么体会呢?Bridge,Proxy,Factory这些设计模式都是基于抽象类的。使用抽象对象是这里的一个核心。          其实我觉得框架化编程的一个核心问题是抽象,用抽象的对象构建程序的主体框架,这是面向对象编程的普遍思想。用抽象构建骨架,再加上多态就形成了一个完整的程序。由于C++语言本身实现了继承和多态,使用这样的编程理念(理念啥意思?跟个风,嘿嘿)在C++中是十分普遍的现象,可以说Virtual(多态)是VC的灵魂。     但是,使用C语言的我们都快把这个多态忘光光了。我常听见前辈说,类?多态?我们用的是C,把这些忘了吧。很不幸的是,我是一个固执的人。这么好的东西,为啥不用呢。很高兴的,在最近的一些纯C代码中,我看见了C中的多态!下面且听我慢慢道来。     1. VC中的Interface是什么   Interface:中文解释是接口,其实它表示的是一个纯虚类。不过我所要说的是,在VC中的Interface其实就是struct,查找Interface的定义,你可以发现有这样的宏定义:        #Ifndef Interface        #define Interface struct        #endif   而且,实际上在VC中,如果一个类有Virtual的函数,则类里面会有vtable,它实际上是一个虚函数列表。实际上C++是从C发展而来的,它不过是在语言级别上支持了很多新功能,在C语言中,我们也可以使用这样的功能,前提是我们不得不自己实现。     2.C中如何实现纯虚类(我称它为纯虚结构)   比较前面,相信大家已经豁然开朗了。使用struct组合函数指针就可以实现纯虚类。   例子:      typedef struct {         void  (*Foo1)();         char  (*Foo2)();         char*  (*Foo3)(char* st);     }     MyVirtualInterface;          这样假设我们在主体框架中要使用桥模式。(我们的主类是DoMyAct,接口具体实现类是Act1,Act2)下面我将依次介绍这些“类”。(C中的“类”在前面有说明,这里换了一个,是使用早期的数组的办法)     主类DoMyAct: 主类中含有MyVirtualInterface* m_pInterface; 主类有下函数:     DoMyAct_SetInterface(MyVirtualInterface* pInterface)     {         m_pInterface= pInterface;     }     DoMyAct_Do()     {         if(m_pInterface==NULL) return;         m_pInterface->Foo1();         c=m_pInterface->Foo2();     }   子类Act1:实现虚结构,含有MyVirtualInterface  st[MAX]; 有以下函数:     MyVirtualInterface* Act1_CreatInterface()     {         index=FindValid() //对象池或者使用Malloc !应该留在外面申请,实例化         if(index==-1) return NULL;         St[index].Foo1=Act1_Foo1; // Act1_Foo1要在下面具体实现         St[index].Foo2=Act1_Foo2;         St[index].Foo3=Act1_Foo3;         Return &st [index];     }   子类Act2同上。     在main中,假设有一个对象List。List中存贮的是MyVirtualInterface指针,则有:     if( (p= Act1_CreatInterface()) != NULL)     List_AddObject(&List, p); //Add Al     While(p=List_GetObject()){         DoMyAct_SetInterface(p);//使用Interface代替了原来大篇幅的Switch Case         DoMyAct_Do();//不要理会具体的什么样的动作,just do it     }       FREE ALL 四、类模拟和多态,继承   在面向对象的语言里面,出现了类的概念。这是编程思想的一种进化。所谓类:是对特定数据的特定操作的集合体。所以说类包含了两个范畴:数据和操作。而C语言中的struct仅仅是数据的集合。(liyuming1978@163.com)   1.实例:下面先从一个小例子看起 #ifndef C_Class #define C_Class struct #endif C_Class A { C_Class A *A_this; void (*Foo)(C_Class A *A_this); int a; int b; }; C_Class B{ //B继承了A C_Class B *B_this; //顺序很重要 void (*Foo)(C_Class B *Bthis); //虚函数 int a; int b; int c; }; void B_F2(C_Class B *Bthis) { printf("It is B_Fun\n"); } void A_Foo(C_Class A *Athis) { printf("It is A.a=%d\n",Athis->a);//或者这里 // exit(1); // printf("纯虚 不允许执行\n");//或者这里 } void B_Foo(C_Class B *Bthis) { printf("It is B.c=%d\n",Bthis->c); } void A_Creat(struct A* p) { p->Foo=A_Foo; p->a=1; p->b=2; p->A_this=p; } void B_Creat(struct B* p) { p->Foo=B_Foo; p->a=11; p->b=12; p->c=13; p->B_this=p; } int main(int argc, char* argv[]) { C_Class A *ma,a; C_Class B *mb,b; A_Creat(&a);//实例化 B_Creat(&b); mb=&b; ma=&a; ma=(C_Class A*)mb;//引入多态指针 printf("%d\n",ma->a);//可惜的就是 函数变量没有private ma->Foo(ma);//多态 a.Foo(&a);//不是多态了 B_F2(&b);//成员函数,因为效率问题不使用函数指针 return 0; }   输出结果: 11 It is B.c=13 It is A.a=1 It is B_Fun 添加评论 10:21  |  固定链接 | 引用通告 (0) | 记录它 固定链接 关闭 http://spaces.msn.com/weijiahan/blog/cns!E35F713B6640ECD2!108.entry 9月28日 用Gtk+开发Linux上的GUI应用软件   一般一个容器构件只能容纳一个构件,如果要在一个容器构件中安排多个子构件,可先将多个子构件放入一个组合框或组合表构件内,再将这个组合框或组合表构件放入该容器构件。   一、如何建立Gtk+应用   1.基本概念   在Linux上开发GUI应用软件,我们通常利用Gtk+库。为了深入了解Gtk+的应用,我们首先要明确几个概念。   (1) 构件(widget)的概念   在Gtk+库里的窗口、选单和按钮等应用实体,我们称之为构件(widget)。构件具有面向对象的特征,其具体结构由Gtk+库定义,它对运用构件的程序员来说是透明的,除非你想创建自己的构件。在对构件进行安排时,我们只需要关心构件的处理函数,这样做的目的就是要对应用程序员屏蔽构件的细节,使程序员将更多的精力集中在应用功能上。   (2) 容器(container)的概念   构件主要分为两大类,即容器类(container)和杂类(miscellaneous)。大多数构件属容器类,它们可以像容器一样容纳其它的构件。在安排构件时常常要遵循一个规则:除了组合框(box)和组合表(table)这两种构件外,其它的容器类构件都只能容纳一个构件。杂类构件比容器类构件简单,但它不能容纳其它构件。   (3) 消息及回呼函数的概念   程序必须能对用户的操作作出反应,在基于GUI的程序设计中,“消息”或“信号”是必要的。用户点击选单、各种按钮、输入用户数据、查询运行结果和拖放,都会产生消息或信号。信号可能需要由软件来加以处理,这时程序员就需要编写消息回呼函数。消息或信号的概念类似于Windows中的事件。在Gtk+中经常产生各种信号,用户产生的大多数信号被忽略,只有程序关心的信号,程序中才有相应的处理函数被调用。程序员要登记信号与处理函数之间的关系,告诉Gtk+哪些信号或事件是程序所关心的。   2. 构件(widget)的运用   在调用Gtk+的构件时,一般进行如下操作步骤:   (1) 包含所调用构件的头文件   头文件通常存放在/usr/include/gtk/目录下,该文件里有对构件的定义及对构件的操作函数,在我们使用构件时,可以经常在这个目录下查阅构件函数的调用格式或参数。我们不必让所有构件包含头文件,因为构件的头文件已包含在该目录下一个名为gtk.h的文件里,包含这个文件就包含了所有构件的头文件。一般系统将编译的包含目录指定为/usr/include,所以我们只需写明 include 即可。   (2) 声明构件   声明构件的过程是为了符合C语言中先声明后使用的原则。一般在程序开始要声明构件结构的指针,这就为构件分配了地址。   (3) 构件指针的初始化   调用构件的创建函数,对构件的属性进行调整(大小、位置和状态等),接着显示构件,这一点不能忽略,如果只创建了构件却忘了显示(gtk_widget_show),在应用时,用户就不能看到构件,也就无法对构件进行操作了。   (4) 安排构件的位置与层次关系   构件之间往往是容纳与被容纳的关系,也就是说容器内还有容器,层层嵌套。要记住一般一个容器构件只能容纳一个构件,如果要在一个容器构件中安排多个子构件,可先将多个子构件放入一个组合框或组合表构件内,再将这个组合框或组合表构件放入该容器构件。注意父容器构件要先于子容器构件创建,父容器构件的显示要在子容器构件的显示之后。   (5) 编写事件处理函数   构件的头文件中定义了构件能响应的事件(消息),但并非所有的消息在应用中都是程序所关心的,对于程序应当响应的事件,程序员要写出事件处理函数。   二、应用举例   为了对Gtk+库的应用有一个具体的认识,让我们来看一个简单的应用实例。   1. 初始化Gtk+   写Gtk+程序需要调用gtk_init函数对Gtk+库进行初始化。我们首先将应用程序的变量argc和argv传递给gtk_init函数,并检查主要用于调试的Gtk+选项。如果在变量列表中出现任何这样的Gtk+参数,就将它移去。在运行gtk_init函数后,它们不应该出现在应用程序中。初始化Gtk+的代码如下:   gtk_init(&&argc,&&argv);   2. 建立构件   首先要建立的构件是窗口,它是应用程序的顶层构件,其它构件一般安排在顶层窗口中。窗口是GUI程序的基本框架,在窗口里我们可以为用户安排各种方便的应用。一个应用可以有若干个窗口,这些窗口之间既相互独立,又有层次的关系。窗口有相对固定的风格,前景色、背景色、字体和字号等,我们应当保持定制的风格,确保不使应用软件杂乱。   按照上面提出的构件运用步骤,我们创建一个程序主窗口:   /?声明构件指针?/   GtkWidget ?window;   /?创建窗口,初始化窗口指针,建立一个顶层窗口?/   window=gtk_window_new(GTK_WINDOW_TOPLEVEL);   /?显示构件?/   gtk_widget_show(window);   /?登记消息与消息处理函数的关系?/   gtk_signal_connect(GTK_OBJECT(window),"delete_event",GTK_SIGNAL_FUNC(close_window),NULL);   3. 构件类型的转换   由于建立的构件是通用构件,需要将它转换为具体的类型以适合调用更为专用的函数。比如建立按钮构件函数会返回GtkWidget 指针,但是专用的按钮子程序要求返回GtkButton指针,所以在调用专用的按钮函数以前,需要使用GTK_BUTTON宏将通用的GtkWidget指针转换为GtkButton指针。通常,构件是由其它构件派生的,窗口构件(GtkWindow)是由容器构件GtkContainer派生的,而容器构件是由通用构件派生的。在Gtk+中可以将构件指针转换为其父类、祖先类构件的任意类型,然后再调用父类或祖先类构件的函数。   4. Gtk+的事件循环   对Gtk+进行初始化并将窗口等构件安排在屏幕上之后,应用软件需要使用Gtk+开始执行事件的循环函数gtk_main(),没有这个函数,应用程序运行时就会一闪即逝。但是在调用gtk_main_quit()函数之前对gtk_main()函数的调用并不返回,也就是说只有gtk_main_quit()函数才能停止Gtk+的执行,从而最终退出应用程序。我们把gtk_main_quit()函数放在消息处理函数close_window()之中,这样,当用户点击了窗口的关闭按钮,Gtk+收到“delelte_event”消息,然后调用close_window(),执行gtk_main_quit()函数,整个程序即可退出。   5. 实例源代码   现在创建一个显示“hello”字样的简单窗口程序。整个程序实现代码如下:   //hello.c   include   /?关闭主窗口中的消息处理函数?/   close_window(GtkWidget ?window,gpointer data)   {   /?中止gtk事件循环?/   gtk_main_quit();   }   /?主函数?/   main(int argc,char ?argv)   {/?声明窗口和标签两个构件?/   GtkWidget ?window;   GtkWidget ?label;   /?初始化Gtk+库?/   gtk_init(&&argc,&&argv);   /?初始化window构件?/   window=gtk_window_new(GTK_WINDOW_TOPLEVEL);   /?初始化label构件?/   label=gtk_label_new("hello!");   /?将标签放入窗口内,函数内用GTK_CONTAINER宏将窗口构?/   /?件的指针类型转换为其父类容器构件的指针类型?/   gtk_container_add(GTK_CONTAINER(window),label);   /?显示label构件?/   gtk_widget_show(label);   /?显示window构件?/   gtk_widget_show(window);   /?当窗口获得'delete_event'消息时调用close_windw函数?/   gtk_signal_connect(GTK_OBJECT(window),"delete_event",   GTK_SIGNAL_FUNC(close_window),NULL);   /?gtk事件循环?/   gtk_main();   }   6. 编译源代码及运行程序   最后谈一下Gtk+在程序编译和运行时有哪些特别之处。在Linux系统下的C编译器,如gcc,要求编译时在编译命令后加上各种参数,如果参数太复杂,最好把命令写入makefile文件。   前面我们讲过在应用程序文件中用到Gtk+函数或定义的每一部分必须包含gtk/gtk.h文件,它是Gtk+的主要包含文件。此外,还必须连接若干库。Gtk+的开发人员为我们提供了方便。使用gtk-config程序可以简化这些工作。编译hello.c源文件成为可执行文件hello的命令如下:   gcc hello.c -o hello `gtk-config -cflags``gtk-config -libs`   注意,程序中一定是反引号(在键盘上位于字符1的左边)。`gtk-config`实际运行了gtk-config程序;参数-cflags输出编译标志,并将它们插入命令行;参数-libs输出连接标志并插入命令行。在Gtk+ 1.2以上版本中包含gtk-config程序。在Linux的命令行提示符后敲入`gtk-config -cflags`和`gtk-config -libs`表示给gcc 传递参数的正确方法。   用shell命令“chmod -777 hello”将文件hello设定为所有用户都可执行的文件,再用shell命令“./hello”执行这个文件。 添加评论 10:37  |  固定链接 | 引用通告 (0) | 记录它 固定链接 关闭 http://spaces.msn.com/weijiahan/blog/cns!E35F713B6640ECD2!107.entry 将VC程序移植到LINUX的几点心得 有时我需要制作LINUX与WINDOWS下都可以运行的程序。在一般情况下,我会选择在WINDOWS平台下完成初始的开发。因为VC提供的图形化的编辑与调试界面的确较GCC要高产得多。在完成了测试之后,就开始把它向LINUX移植,移植的过程会有一些需要注意的地方。下面就是我的一些心得。 文件名 由于ext2文件系统对文件名是大小写敏感的,当你在这种文件系统上进行编译的时候,源文件中出现的#include 语句必须小心了。因为在VC环境下,由IDE自动生成的#include 语句,其中的文件名全部是小写的。所以,你需要在一开始就注意这个问题,严格的使用大小写敏感的文件名格式,避免在LINUX下编译时出现找不到头文件的错误。 数据类型 千万不要使用VC独有的数据类型,象__int16, __int32 和__int64 等等,你无法保证其它的编译器能否支持它们。特别是__int64,它确实简化了编程工作,但是当你的逻辑里充满了这样的数据类型的时候,改动就变得无比困难了。还有一个问题就是,我们经常在VC中使用WORD,DWORD,INT,UINT这样的扩展数据类型,不直接使用编译器的数据类型有助于提高在不同平台之间的可移植性。但是LINUX下没有定义这样的类型啊?其实只需要将windows.h和basetypes.h中对这些数据进行定义的语句复制到一个头文件中,再在linux下包括进来就行了。   关键字 关键字是比较好处理的东西,凡是VC中带两个下划线的关键字,比方__asm都是VC独有的。尽量不使用它们,如果实在无法避免,就用#ifdef 和#endif为LINUX和WINDOWS编写两个版本。 MAKEFILE的编写 你可以先用VC导出一个makefile,然后对其进行修改,但我倾向于从中拷贝出一段来生成GCC的makefile,比起手工编写要快许多。 程序设计结构 这绝对是移植过程中问题最大的一个部分。应用程序难免要用到操作系统的服务,如果完全使用的C/C++编写,这将不是一个问题,但是当我们使用到多进/线程,管道,或者对WINDOWS图形界面的程序进行移植的时候,这个问题就变得突出了。我们应当从设计上就为程序的移植打好基础。 解决这个问题首先必须搞清楚应用程序的逻辑模块。对于这个模块必须使用标准的C/C++进行编写。同时将应用程序使用的线程数最小化,线程越多越难移植。将输入输出模块独立出来。最后划分出控制模块,这个模块与用户进行交互。 上面所讲是我在处理一般的,面向桌面用户的一些程序的大的原则。   下面是寻找应用程序进线程单元具体的方法,见图1: 图一:基本程序模块 这种图似乎随处可见,但是真正严格遵循了的人却是很少的。 同常情况下,核心模块只需要一个进线程,如果你的核心模块需要两个以上的进线程,则说明还需要更细的划分,直至一个模块对应一个进线程。 >这个图中的箭头意义重大。在画完模块图以后,就是分清这些箭头是同步的还是异步的。比方核心模块与输入模块之间,是否需要核心模块等待输入模块将数据读取完毕然后才能运行?如果答案为“是”,那么你可以将这两个模块合并到同一个线程中来。(如图2)如果被划分到同一个进线程块中来的两个模块又与第三个模块是同步的关系,那么就可以把第三个模块也组合到这个进线程单位中来。直到每一个模块都被包含在了一个进线程单位中。 图二:划分进线程单位后 值得注意的是,如果其中一个模块被包含在了两个以上的进线程单位中或者没有被包含在任何进线程单位中,则你的划分一定是有错的。 在通常的情况下,应用程序的进线程单位的划分最后会变成图2的形式。因为控制模块往往需要与用户同步,而不是核心模块,所以它需要一个单独的进/线程(通常是线程)。为了提高性能,输出模块也往往与逻辑模块异步工作,必须用不同的进线程来完成。完成了上面的图,数一数虚线框,就是最合适的进线程数目了。 进线程的实现是操作系统相关的。我们可以将进线程这个载体与它搭载的逻辑分离开来。我的是:视上图中划分的每一个进线程单位为一个同步实体。这个实体可以封装在这样的一个几乎所有多任务操作系统都支持的抽象类里: class CSyn { virtual Init()=0;//初始化 virtual Create(BOOL Suspended)=0;//创建 virtual Run()=0;//运行 virtual Suspend()=0;//挂起 virtual Kill()=0;//杀死 virtual Resume()=0;//取消挂起状态 } 所有的同步实体都必须由这个类派生。同步实体可以创建新的同步实体,并应该负责管理它们,打个比方,同步实体必须在自己退出之前调用所有它创建的同步实体的Kill函数。 你不一定非要使用上面的类,但一定要遵循上面的原则,它可以保证你在移植多进线程程序的时候逻辑清晰。 进线程之间的通讯是第二个棘手的问题。同步实体之间的通讯也应当用一个类来进行抽象。这样在移植的时候,我们只需要对这个类进行修改。同时,通讯的数据最好使用流的方式,这样我们可以将通讯类设计成象cout与cin这样的类。同步实体所有的输入数据都应该来自通讯类,所有的输出数据也应该到通讯类那里去。 输出模块的设计原则与上面的并无二致,尽量多的使用函数过程和类来进行抽象能够进一步的降低移植难度。 最后需要说明的是,上面的设计可能还需要添加其它的模块才能完成真正的应用程序。比如在WINDOWS平台下,我们可能需要在输出模块和控制模块的外延添加上窗口控制模块来完成一个完整的图形化WINDOWS程序,如果把窗口控制模块改成一个批处理文件的解析模块,我们可以做成LINUX下的一个批处理执行的应用程序。良好的模块设计能够让这种替换成为可能。可以说,一个移植性良好的程序,其可扩展性也一定是非常好的。 上面的例子非常简单,在实际的设计过程中,模块的划分会复杂得多。动手移植一个WINDOWS应用程序会让你深有体会。
/
本文档为【C语言中的面向对象】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索