tag 标签: 面向对象

相关博文
  • 热度 8
    2014-10-9 07:26
    3434 次阅读|
    5 个评论
    一、前言 对于编程而言,重要的是解决问题的方式,而不是语言本身。面向对象与面向过程是解决问题和思考问题的方式。C语言常说是面向过程开发的语言,因为其缺少很多对于面向对象特性的支持。但,这并不影响我们采用面向对象的方式,使用C语言编程。   二、基本介绍 在C语言中,采用面向对象开发,有两个地方是要明白的: 1、结构体, 2、函数指针变量。   结构体:结构体是对于内存的一种组织方式,结构体成员可以是多个不同类型的变量(这是与数组最大的区别,数组中可以包含多个的变量,但只能是同一种类型)。这样使用结构体做到将多个相关的数据集合(封装)到一起。便于记忆与管理。 函数指针变量:函数指针变量,是一个可以存放函数地址的指针变量。因为它是变量,所以可以被重新赋值;因为它是指针变量,所以可以间接寻址;因为它是函数指针变量,可以将函数地址赋值给它,并通过对它的访问,实现调用函数的目的。   由于结构体本身不能定义函数,那么没有办法将函数直接集合(封装)到结构体中。但是,函数指针变量给我们提供了一个间接将函数集合(封装)到一起的方式。我们可以通过调用函数指针变量,实现调用函数的目的。 struct struct_test {      uint8_t type;      uint16_t counter;      uint32_t size;      void (*fun)(void *input); };   struct struct_test test; 测试内存(Ubuntu gcc编译)分布如下: test size:12 type:0xbff9fb64  /* 这里进行了字节对齐,多加了一个字节 */ counter:0xbff9fb66 size:0xbff9fb68 fun:0xbff9fb6c   三、简单实现 面向对象的本质是对数据和方法(函数)的隐藏,避免对细节的了解。而这一点,正是结构体结合函数指针变量可以做到的。 在这里,着重于对于数据和方法(函数)的隐藏来谈论C语言面面向对象的简单实现。暂时不去谈论,关于面向对象的其他特性例如:继承,组合,多态等。 在C的模块化编程中,将一个.c和.h文件看作一个模块,在实现的时候,尽量使用局部静态变量,减少对外的函数接口,同时尽可能少的调用其它模块中的函数,避免受到其他模块的影响(也即,低耦合,高内聚)。C的面向对象编程与模块化编程,都是为了解决降低编程复杂度,减轻同一时间需要记忆的工作量,所以他们可以也应该联合使用。 简单的例子: 有一个.c文件oo_test.c #include "oo_test.h"   static struct oo_test  oo; /* struct oo_test  oo; */ /* void oo_test_init(void) {      oo.type = 0;      oo.counter = 0;      oo.fun = test; } */   struct oo_test *oo_test_init(void) {      oo.type = 0;      oo.counter = 0;      oo.fun = test;      return oo; }   static void test(void *input) {      …; }   有一个.h文件oo_test.h #ifndef  OO_TEST_H #define OO_TEST_H #include "stdint.h"   struct oo_test {      uint8_t type;      uint16_t counter;      void (*fun)(void *input); };   struct oo_test *oo_test_init(void); /* #define OO               oo  */ /* extern struct oo_test  oo; */ #endif   其他文件使用: xx.c …… struct oo_test *oo = oo_test_init(); oo.fun(); /* oo_test_init(); */ /* OO.fun();  */ …… (注释中,是另一个种实现方式,这两种方式各有所取。根据使用的情况下,适当选择)   在上边提到的简单的例子中,首先避免了C编程中全局变量的问题,通过局部静态变量实现信息传递,做到数据的隐藏; 其次减少C编程中对外的函数接口(这一点是模块化编程,与面向对象编程都强调的一点:减少对外的接口,隐藏内部函数)。   四、参考 Contiki操作系统; 《Make Embedded System》。
  • 热度 17
    2014-2-17 16:01
    1959 次阅读|
    1 个评论
    多态 (Polymorphism) 1.基类的指针 (Pointers to base class)      继承的好处之一是一个指向子类(derived class)的指针与一个指向基类(base class) 的指针是 type-compatible 的。本节就是重点介绍如何利用 C++的这一重要特性。例如, 我们将结合 C++的这个功能,重写前面小节中关于长方形 rectangle 和三角形 triangle 的程序: // pointers to base class #include class CPolygon      //定义一个多边形的类 { protected: int width, height; public: void set_values (int a, int b)  { width=a; height=b; } };   class CRectangle: public CPolygon //定义一个继承自CPolygon 的 class CRectangle { public: int area (void) { return (width * height); } }; class CTriangle: public CPolygon //定义一个继承自CPolygon 的 class CTriangle   { public: int area (void) { return (width * height / 2); } }; int main () { CRectangle rect; CTriangle trgl; CPolygon * ppoly1 = ▭   //定义CPolygon的指针分别指向rect 和 trg1 CPolygon * ppoly2 = trgl; ppoly1-set_values (4,5); ppoly2-set_values (4,5); cout rect.area() endl; cout trgl.area() endl; return 0; } 结果: 20 10     在主函数 main 中定义了两个指向 class CPolygon 的对象的指针,即 *ppoly1 和 *ppoly2。 它们被赋值为 rect 和 trgl 的地址,因为 rect 和 trgl 是 CPolygon 的子 类的对象,因此这种赋值是有效的。      使用*ppoly1 和 *ppoly2 取代 rect 和 trgl 的唯一限制是*ppoly1 和 *ppoly2 是 CPolygon* 类型的,因此我们只能够引用 CRectangle 和 CTriangle 从基类 CPolygon 中继承的成员。正是由于这个原因,我们不能够使用*ppoly1 和 *ppoly2 来调用成员函 数 area(),而只能使用 rect 和 trgl 来调用这个函数。 要想使 CPolygon 的指针承认 area()为合法成员函数,必须在基类中声明它,而不能只 在子类进行声明。 2.虚拟成员(Virtual members)       如果想在基类中定义一个成员留待子类中进行细化,我们必须在它前面加关键字 virtual ,以便可以使用指针对指向相应的对象进行操作。 请看一下例子: // virtual members #include class CPolygon { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b; } virtual int area (void) {  return(0); } }; class CRectangle: public CPolygon { public: int area (void) { return (width *height); } };   class CTriangle: public CPolygon { public: int area (void) { return (width * height / 2); } }; int main () { CRectangle rect; CTriangle trgl; CPolygon poly; CPolygon * ppoly1 = ▭ CPolygon * ppoly2 = trgl; CPolygon * ppoly3 = poly; ppoly1-set_values (4,5); ppoly2-set_values (4,5); ppoly3-set_values (4,5); cout ppoly1-area() endl; cout ppoly2-area() endl; cout ppoly3-area() endl; return 0; } 结果: 20 10 0   现在这三个类(CPolygon, CRectangle 和 CTriangle) 都有同样的成员:width, height, set_values() 和 area()。 area() 被定义为 virtual 是因为它后来在子类中被细化了。你可 以做一个试验,如果 在代码种去掉这个关键字(virtual),然后再执行这个程序,三个多边形的面积计算结果 都将是 0 而不是 20,10,0。这是因为没有了关键字 virtual ,程序执行不再根据实际对 象的不用而调用相应 area() 函数(即分别为 CRectangle::area(), CTriangle::area() 和 CPolygon::area()),取而代之,程序将全部调用 CPolygon::area(),因为这些调用 是通过 CPolygon 类型的指针进行的。 因此,关键字 virtual 的作用就是在当使用基类的指针的时候,使子类中与基类同名的 成员在适当的时候被调用,如前面例子中所示。 注意,虽然本身被定义为虚拟类型,我们还是可以声明一个 CPolygon 类型的对象并调 用它的 area() 函数,它将返回 0 ,如前面例子结果所示。   3.抽象基类 (Abstract base classes)     基本的抽象类与我们前面例子中的类 CPolygon 非常相似,唯一的区别是在我们前面的 例子中,我们已经为类 CPolygon 的对象(例如对象 poly)定义了一个有效地 area()函 数,而在一个抽象类(abstract base class)中,我们可以对它不定义,而简单得在函 数声明后面写 =0 (等于 0)。 类 CPolygon 可以写成这样: // abstract class CPolygon class CPolygon { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b; } virtual int area (void) =0; }; 注意我们是如何在 virtual int area (void)加=0 来代替函数的具体实现的。这种函 数被称为纯虚拟函数(pure virtual function),而所有包含纯虚拟函数的类被称为抽 象基类(abstract base classes)。 抽象基类的最大不同是它不能够有实例(对象),但我们可以定义指向它的指针 。因此, 像这样的声明: CPolygon poly; 对于前面定义的抽象基类是不合法的。 然而,指针: CPolygon * ppoly1; CPolygon * ppoly2 是完全合法的。这是因为该类包含的纯虚拟函数(pure virtual function) 是没有被实 现的,而又不可能生成一个不包含它的所有成员定义的对象。然而,因为这个函数在其 子类中被完整的定义了,所以生成一个指向其子类的对象的指针是完全合法的。 下面是完整的例子: // virtual members #include class CPolygon     //定义抽象基类 { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b; } virtual int area (void) =0; }; class CRectangle: public CPolygon { public: int area (void) { return (width * height); } }; class CTriangle: public CPolygon { public: int area (void) { return (width * height / 2); } }; int main () { CRectangle rect; CTriangle trgl; CPolygon * ppoly1 = ▭ CPolygon * ppoly2 = trgl; ppoly1-set_values (4,5); ppoly2-set_values (4,5); cout ppoly1-area() endl; cout ppoly2-area() endl; return 0; } 结果: 20 10 再看一遍这段程序,你会发现我们可以用同一种类型的指针(CPolygon*)指向不同类的对 象,至一点非常有用。 想象一下,现在我们可以写一个 CPolygon 的成员函数,使得它 可以将函数 area()的结果打印到屏幕上,而不必考虑具体是为哪一个子类。 // virtual members #include class CPolygon { protected: int width, height; public: void set_values (int a, int b) { width=a; height=b; } virtual int area (void) =0; void printarea (void)     //在抽象基类中声明printarea函数  便于后面的调用 { cout this-area() endl; } }; class CRectangle: public CPolygon { public: int area (void) { return (width *height); } }; class CTriangle: public CPolygon { public: int area (void) { return (width * height / 2); } }; int main () { CRectangle rect; CTriangle trgl; CPolygon * ppoly1 = ▭ CPolygon * ppoly2 = trgl; ppoly1-set_values (4,5); ppoly2-set_values (4,5); ppoly1-printarea(); ppoly2-printarea(); return 0; } 结果: 20 10   记住,this 代表代码正在被执行的这一个对象的指针。 抽象类和虚拟成员赋予了 C++ 多态(polymorphic)的特征,使得面向对象的编程 object-oriented programming 成为一个有用的工具。这里只是展示了这些功能最简单 的用途。想象一下如果在对象数组或动态分配的对象上使用这些功能,将会节省多少麻 烦。
  • 热度 11
    2014-2-14 10:00
    2547 次阅读|
    1 个评论
    五.函数 1.数值传递与地址传递 参数按数值传递和按地址传递(Arguments passed by value and byreference)到目前为止,我们看到的所有函数中,传递到函数中的参数全部是按数值传递的(byvalue)。也就是说,当我们调用一个带有参数的函数时,我们传递到函数中的是变量的数值而不是变量本身。例如,假设我们用下面的代码调用我们的第一个函数 addition : int x=5, y=3, z; z = addition ( x , y ); 在这个例子里我们调用函数 addition 同时将 x 和 y 的值传给它,即分别为 5 和 3,而不是两个变量:这样,当函数addition被调用时,它的变量a和b的 值分别变为5和3,但在函数addition内对变量 a 或 b 所做的任何修改不会影响变量他外面的变量 x 和 y 的值,因为变量 x和 y 并没有把它们自己传递给函数,而只是传递了他们的数值。   但在某些情况下你可能需要在一个函数内控制一个函数以外的变量。要实现这种操作, 我们必须使用按地址传递的参数(arguments passed by reference),就象下面例子中 的函数 duplicate: // passing parameters by reference #include void duplicate (int a, int b, int c)//采用按地址传递的方式传入参数 { a*=2; b*=2; c*=2; } int main () { int x=1, y=3, z=7; duplicate (x, y, z); x=2, y=6, z=14 cout "x=" x ", y=" y ", z=" z; return 0; } 第一个应该注意的事项是在函数 duplicate 的声明(declaration)中,每一个变量的类型后面跟了一个地址符 ampersand sign (),它的作用是指明变量是按地址传递的(byreference),而不是像通常一样按数值传递的(by value) 按地址传递(Passing by reference)是一个使函数返回多个值的有效方法。例如,下面是一个函数,它可以返回第一个输入参数的前一个和后一个数值。     参数的默认值(Default values n n n in arguments) 当声明一个函数的时候我们可以给每一个参数指定一个默认值。如果当函数被调用时没有给出该参数的值,那么这个默认值将被使用。指定参数默认值只需要在函数声明时把一个数值赋给参数。如果函数被调用时没有数值传递给该参数,那么默认值将被使用。但如果有指定的数值传递给参数,那么默认值将被指定的数值取代。例如: // default values in functions #include int divide (int a, int b=2) { int r; r=a/b; return (r); } int main () { cout divide (12); cout endl; cout divide (20,4); return 0; } 6 5 我们可以看到在程序中有两次调用函数 divide。第一次调用: divide (12) 只有一个参数被指明,但函数 divide 允许有两个参数。因此函数 divide 假设第二个参数的值为2,因为我们已经定义了它为该参数缺省的默认值(注意函数声明中的int b=2)。因此这次函数调用的结果是 6 (12/2)。 在第二次调用中:divide (20,4)这里有两个参数,所以默认值 (int b=2) 被传入的参数值 4 所取代,使得最后结果为 5(20/4).   函数重载(Overloaded functions) 两个不同的函数可以用同样的名字,只要它们的参量(arguments)的原型(prototype)不同,也就是说你可以把同一个名字给多个函数,如果它们用不同数量的参数,或不同类型的参数。例如: // overloaded function即当存在两个相同的函数时,编译器会自动选择匹配的函数。 #include int divide (int a, int b) { return (a/b); } float divide (float a, float b) { return (a/b); } int main () { int x=5,y=2; float n=5.0,m=2.0; cout divide (x,y); cout "\n"; cout divide (n,m); cout "\n"; return 0; } 2 2.5 在这个例子里,我们用同一个名字定义了两个不同函数,当它们其中一个接受两个整型(int)参数,另一个则接受两个浮点型(float)参数。编译器 (compiler)通过检查传入的参数的类型来确定是哪一个函数被调用。如果调用传入的是两个整数参数,那么是原型定义中有两个整型(int)参量的函数被调用,如果传入的是两个浮点数,那么是原型定义中有两个浮点型(float)参量的函数被调用。为了简单起见,这里我们用的两个函数的代码相同,但这并不是必须的。你可以让两个函数用同一个名字同时完成完全不同的操作。   Inline 函数(inline functions) inline 指令可以被放在函数声明之前,要求该函数必须在被调用的地方以代码形式被编译。这相当于一个宏定义(macro)。它的好处只对短小的函数有效,这种情况下因为避免了调用函数的一些常规操作的时间(overhead),如参数堆栈操作的时间,所以编译结果的运行代码会更快一些。 它的声明形式是: inline type name ( arguments ... ) { instructions ... } 它的调用和其他的函数调用一样。调用函数的时候并不需要写关键字 inline ,只有在函数声明前需要写   六.数组 注意:同时给数组赋多个值只有在数组初始化时,也就是在声明数组时,才是合法的。 象下面代码现实的表达式都是错误的: mystring = "Hello"; mystring = "Hello"; mystring = { 'H', 'e', 'l', 'l', 'o', '\0' }   给字符数组的赋值 因为赋值运算的 lvalue 只能是数组的一个元素,而不能使整个数组,所以,用以下方 式将一个字符串赋给一个字符数组是合法的: mystring = 'H'; mystring = 'e'; mystring = 'l'; mystring = 'l'; mystring = 'o'; mystring = '\0'; 但正如你可能想到的,这并不是一个实用的方法。通常给数组赋值,或更具体些,给字符序列赋值的方法是使用一些函数,例如 strcpy。strcpy (string copy) 在函数库 cstring (string.h) 中被定义,可以用以下方式被调用: strcpy (string1, string2); 这个函数将 string2 中的内容拷贝给 string1。string2 可以是一个数组,一个指针, 或一个字符串常量 constant string。因此用下面的代码可以将字符串常量"Hello"赋给mystring: strcpy (mystring, "Hello"); 例如: // setting value to string #include #include int main () { char szMyName ; strcpy (szMyName,"J. Soulie");//给数组元素赋值 cout szMyName; return 0; } J. Soulie 注意:我们需要包括头文件才能够使用函数 strcpy。 虽然我们通常可以写一个像下面 setstring 一样的简单程序来完成与 cstring 中 strcpy 同样的操作: // setting value to string #include void setstring (char szOut , char szIn )//作用等同于strcpy函数 { int n=0; do { szOut = szIn ; } while (szIn != '\0'); } int main () { char szMyName ; setstring (szMyName,"J. Soulie"); cout szMyName; return 0; } 另一个给数组赋值的常用方法是直接使用输入流(cin)。在这种情况下,字符序列的值是在程序运行时由用户输入的。 当 cin 被用来输入字符序列值时,它通常与函数 getline 一起使用,方法如下: cin.getline ( char buffer ; cout "What's your name? "; cin.getline (mybuffer,100); cout "Hello " mybuffer ".\n"; cout "Which is your favourite team? "; cin.getline (mybuffer,100);//给数组元素赋值 What's your name? Juan Hello Juan. Which is your favourite team? Inter Milan I like Inter Milan too. cout "I like " mybuffer " too.\n"; return 0; } 注意上面例子中两次调用 cin.getline 时我们都使用了同一个字符串标识 (mybuffer)。程序在第二次调用时将新输入的内容直接覆盖到第一次输入到 buffer 中 的内容。 你可能还记得,在以前与控制台(console)交互的程序中,我们使用 extraction operator() 来直接从标准输入设备接收数据。这个方法也同样可以被用来输入字符串,例如, 在上面的例子中我们也可以用以下代码来读取用户输入: cin mybuffer; 这种方法也可以工作,但它有以下局限性是 cin.getline 所没有的: • 它只能接收单独的词(而不能是完整的句子),因为这种方法以任何空白符为分隔 符,包括空格 spaces,跳跃符 tabulators,换行符 newlines 和回车符 arriage returns。 • 它不能给 buffer 指定容量,这使得程序不稳定,如果用户输入超出数组长度,输入信息会被丢失。 因此,建议在需要用 cin 来输入字符串时,使用 cin.getline 来代替 cin 。   字符串和其它数据类型的转换(Converting strings to other types) 鉴于字符串可能包含其他数据类型的内容,例如数字,将字符串内容转换成数字型变量的功能会有用处。例如一个字符串的内容可能是"1977",但这一个 5 个字符组成序列,并不容易转换为一个单独的整数。因此,函数库 cstdlib (stdlib.h) 提供了 3 个有用 的函数: • atoi: 将字符串 string 转换为整型 int • atol: 将字符串 string 转换为长整型 long • atof: 将字符串 string 转换为浮点型 float 所有这些函数接受一个参数,返回一个指定类型的数据(int, long 或 float)。这三个函数与 cin.getline 一起使用来获得用户输入的数值,比传统的 cin 方法更可靠: // cin and ato* functions Enter price: 2.75 #include #include int main () { char mybuffer ; float price; int quantity; cout "Enter price: "; cin.getline (mybuffer,100); price = atof (mybuffer); cout "Enter quantity: "; cin.getline (mybuffer,100); quantity = atoi (mybuffer); cout "Total price: " price*quantity; return 0; }   字符串操作函数(Functions to manipulate strings) 函数库 cstring (string.h) 定义了许多可以像 C 语言类似的处理字符串的函数 (如前 面已经解释过的函数 strcpy)。这里再简单列举一些最常用的: • strcat: char* strcat (char* dest, const char* src); //将字符串 src 附加到字符串 dest 的末尾,返回 dest。 • strcmp: int strcmp (const char* string1, const char* string2); //比较两个字符串 string1 和 string2。如果两个字符串相等,返回 0。 • strcpy: char* strcpy (char* dest, const char* src); //将字符串 src 的内 容拷贝给 dest,返回 dest 。 • strlen: size_t strlen (const char* string); //返回字符串的长度。 注意:char* 与 char[] 相同。
  • 热度 13
    2014-2-14 09:59
    1679 次阅读|
    0 个评论
    类的指针(Pointers to classes) 类也是可以有指针的,要定义类的指针,我们只需要认识到,类一旦被定义就成为一种 有效的数据类型,因此只需要用类的名字作为指针的名字就可以了。例如:CRectangle * prect; 是一个指向 class CRectangle 类型的对象的指针。 就像数据机构中的情况一样,要想直接引用一个由指针指向的对象(object)中的成员, 需要使用操作符 -。这里是一个例子,显示了几种可能出现的情况: // pointer to classes example #include class CRectangle { int width, height; public: void set_values (int, int); int area (void) {return (width *height);} }; void CRectangle::set_values (int a, intb) { width = a; height = b; } int main () { CRectangle a, *b, *c; CRectangle * d = new CRectangle ; //定义类的指针D指向一个类数组 b= new CRectangle; c= a; a.set_values (1,2); b-set_values (3,4); d-set_values (5,6); d .set_values (7,8); cout "a area: " a.area() endl; cout "*b area: " b-area() ; cout "*c area: " c-area() ; cout "d area: " d .area() endl; cout "d area: " d .area() endl; return 0; } 结果: a area: 2 *b area: 12 *c area: 2 d area: 30 d area: 56   以下是怎样读前面例子中出现的一些指针和类操作符 (*, , ., -, ): •    *x 读作: pointed by x (由 x 指向的) •    x 读作: address of x(x 的地址) •    x.y 读作: member y of object x (对象 x 的成员 y) •    (*x).y 读作: member y of object pointed by x(由 x 指向的对象的成员 y) •    x-y 读作: member y of object pointed by x (同上一个等价) •    x 读作: first object pointed by x(由 x 指向的第一个对象) •    x 读作: second object pointed by x(由 x 指向的第二个对象) •    x 读作: (n+1)th object pointed by x(由 x 指向的第 n+1 个对象 ) 在继续向下阅读之前,一定要确定你明白所有这些的逻辑含义。 由关键字 struct 和 union 定义的类 类不仅可以用关键字 class 来定义,也可以用 struct 或 union 来定义。   因为在 C++中类和结构体的概念太相似了,所以这两个关键字 struct 和 class 的作用 几乎是一样的(也就是说在 C++中 struct 定义的类也可以有成员函数,而不仅仅有数据 成员)。两者定义的类的唯一区别在于由 class 定义的类所有成员的默认访问权限为 private,而 struct 定义的类所有成员默认访问权限为 public。除此之外,两个关键字 的作用是相同的。 union 的概念与 struct 和 class 定义的类不同, 因为 union 在同一时间只能存储一个数 据成员。但是由 union 定义的类也是可以有成员函数的。union 定义的类访问权限默 认为 public。   关键字 this 关键字 this 通常被用在一个 class 内部,指正在被执行的该 class 的对象(object)在 内存中的地址。它是一个指针,其值永远是自身 object 的地址   静态成员(Static members) 一个 class 可以包含静态成员(static members),可以是数据,也可以是函数。 一个 class 的静态数据成员也被称作类变量"class variables" ,因为它们的内容不 依赖 于某个对象,对同一个 class 的所有 object 具有相同的值。   例如,它可以被用作计算一个 class 声明的 objects 的个数,见以下代码程序: // static members in classes #include class CDummy { public: static int n; CDummy () { n++; }; ~CDummy () { n--; }; }; int CDummy::n=0; // 类的静态成员  类似于全局变量 只是享有类的范围 int main () { CDummy a; CDummy b ; CDummy * c = new CDummy; cout a.n endl; delete c; cout CDummy::n endl; return 0; } 结果: 7 6       静态成员与全域变量(global variable)具有相同的属性,但它享有类(class) 的范围。因此,根据 ANSI-C++ 标准,为了避免它们被多次重复声明,在 class 的声明 中只能够包括 static member 的原型(声明),而不能够包括其定义(初始化操作)。为了 初始化一个静态数据成员,我们必须在 class 之外(在全域范围内),包括一个正式的定 义,就像上面例子中做法一样。 因为它对同一个 class 的所有 object 是同一个值,所以它可以被作为该 class 的任何 object 的成员所引用,或者直接被作为 class 的成员引用(当然这只适用于 static 成 员): cout a.n; cout CDummy::n; 以上两个调用都指同一个变量:class CDummy 里的 static 变量 n 。 在提醒一次,它其实是一个全域变量。唯一的不同是它的名字跟在 class 的后面。   静态函数      我们会在 class 中包含 static 数据一样,我们也可以使它包含 static 函数。它们 表示相同的含义:static 函数是全域函数(global functions) ,但是像一个指定 class 的对象成员一样被调用。它们只能够引用 static 数据,永远不能引用 class 的非静态 (nonstatic) 成员 。它们也不能够使用关键字 this ,因为 this 实际引用了一个对象指针, 但这些 static 函数却不是任何 object 的成员,而是 class 的直接成员。
  • 热度 18
    2013-6-20 10:08
    1038 次阅读|
    0 个评论
    UML 与面向对象系统分析与设计培训                   课程简介    本课程主要向系统开发人员讲授如何使用UML(统一建模语言)建立系统模型,描述系统架构。包括了静态建模、动态建模、物理架构以及UML的扩展。学员 可以有效地通过用例表述的系统需求来开发稳定的分析模型、设计模型、实现模型、测试模型、部署模型。学员将了解如何把“统一建模语言 (UML)”应用到基本的面向对象分析和设计概念中,其中包括构架、对象、类、构件、构造型、关系和各个类型的图。 在本课程中,学员可以通过学习使用年度最佳UML工具Plato/Kant可视化建模工具来提高系统分析和设计能力。从理解团队开发基础到创建图并运行报表,再到观看正向和逆向工程演示,学员将练习Plato/Kant的所有基本内容,从而理解 Plato/Kant的功能和可视化建模的价值。 培训对象 该课程适用于希望在实际项目应用中系统性的应用UML的技术人员,以及那些希望获得在用例驱动、以构架为中心和迭代化开发过程中,应用该技术的高级项目开发人员。包括需求调研人员、构架设计师、分析人员、设计人员和软件开发人员、产品市场技术人员。 本培训课程适宜于想从事软件系统分析与设计的系统程序员,最好应精通一种OOP的语言(如:C++、C#或者Java等)和开发工具(PB、Delphi、VC++等)。   中国电子标准协会 http://www.ways.org.cn    培训时间:3天 必备条件   参加此课程之前,学员应当具有面向对象语言程序设计经验。为确保授课效果,学员应当具备基本的软件工程背景知识和能够阅读简单的计算机英文的能力。 培训目标 完成本课程之后,学员将具备以下能力: 能独立地进行系统分析与设计,并利用UML设计自己的业务模块。 如何把“统一建模语言 (UML)”应用到基本的面向对象分析和设计乃至整个软件过程中,其中包括使用UML建立业务模型、需求模型、分析模型、设计模型、实现模型、测试模型、部署模型。 培训内容 一.UML语言规范: UML语言基础:基本概念,模型元素,体系结构 用UML对系统结构建模技术:类,关系,高级关系,图,类图,对象图,公共机制,接口,包,实例等基本及高级技术 用UML对系统行为建模技术:用例图,序列图,协作图,活动图,状态机,状态图,事件,信号,进程,线程等基本及高级技术 对系统体系结构建模的技术:组件,协作,实施,模式,框架,组件图,实施图,系统体系结构等基本技术及高级技术 二.UML面向对象分析与设计 概述 面向对象基本概念 运用UML、设计模式和面向对象的分析与设计技术 开发过程导论 业务需求建模阶段: 业务角色的查找及建立 业务用例查找与分析 用例模型的建立 业务规则及其建模 用活动图表示用例结构 活动图泳道的划分及对象流的标识 业务实体的分析及提取 业务对象模型的建立 系统需求建模阶段: 需求的捕获与理解 系统功能的理解 系统角色的建立 系统用例的建立 用例对过程的描述 基本用例及其分类 用例的扩展、包含及泛化关系 用例规约及文档标准 SRS现代软件需求包的理解   分析阶段及分析模型的建立: 从用例建立概念模型 分析及分析机制 用例分析 用例的关联 概念的识别及提取 概念模型的细化 概念模型的扩展及润饰 系统行为的分析-系统顺序图及契约 用状态图对对象建模 分析类及实体、控制、边界类的理解及分析 建立分析类图 包的组织 系统分层及架构分析 设计阶段及设计模型的建立: 从分析模型到设计阶段的映射 描述真实用例 设计用例 对设计类的理解 设计阶段的协作图 对类的职责分配 类设计的原理及原则 设计模型中的关联、泛化及聚合关系 设计模型中的系统行为的表达 接口的设计 包及子系统的设计 系统构架设计的要点 设计类图的建立 软件架构文档及模板 三. Plato/Kant 工具及应用: 针对问题需求描述建立Use Cases视图,序列图及协作图 建立包和类图,建立关联和聚合关系以及建立关系,建立类的属性和操作 建立状态转换图及活动 建立构件图和布署图模型 Plato/Kant与开发工具代码的正向及逆向工程 四.应用案例分析:穿插到各个阶段讲解 可根据客户提供实际案例进行讲解  
相关资源