UML(统一建模语言),它是一种图形化语言,允许系统构件人员(软件结构设计师,系统工程师,程序员等)采用一种标准的记号法,对他们的面向对象设计进行呈现。
//
开发流程:
1.分析和设计系统
2.用例图
对于分析阶段的输出来说,它的意图是清楚地指出系统应如何构建才能完成需要它做的工作。UML提供了用例图,以便简化进行需求分析的过程。用例图的目标是展现用户同系统之间的各种交互,但不必提供交互的细节!
3.标识系统中的类。
4.类图
UML允许我们通过类图对电梯系统中的“类”及其“关系”进行建模。
在一个类图中,各类都用矩形来表示。然后,该矩形被分成3部分。顶部包含类名。
中部包含类的属性;底部包含类的操作。
通过关联,便将不同的类联系在一起。所谓关联,就是类之间的一种关系。
5.对象图
UML还定义了对象图,它同类图相似,只是它建模的是对象和链接(链接是对象之间的关系)。和类图相同,对象图也对系统的结构进行建模。对象图展示了系统运行时的一个结构“快照”,即提供了在某个特定的时刻,参与系统运作的各个对象的信息。
第三章:
rand()生成0到RAND_MAX(是<cstdlib>头文件中定义的一个符号常量)的一个无符号整数。RAND_MAX的值至少应为32767—一个两字节整数所能表达的最大值。
P191
面向对象程序设计的第一阶段,即确定(实现电梯模拟程序)所需要的类。然后会确定每个类的属性,再确定其操作,最后确定不同对象之间的交互。
开发和维护大型程序的最佳方式是将其分成更小的程序模块,与原始程序相比,每个小模块将更易于管理。在C++中,模块被称为类和函数。
第六章:
面向对象的设计方法:分析典型问题语句,要求建立一个系统,确定系统中需要实现的类(标准系统所牵涉的类),确定这些类对象的属性,确定这些类对象的行为,并指定对象之间如何通过交互来完成系统的总体目标。
绝不要让类的public成员函数返回对该类private数据成员的非常量引用或指针。返回这种数据会破坏类的封装。(意思就是说,客户程序员可以不通过类本身的设置其数据成员的函数而在外部直接改变那个成员。这会导致意外发生!)
如果在类定义中定义成员函数,该成员函数将自动成为内联函数,但编译器有权决定其是否作为内联函数。
良好的软件工程的基本原则之一是将接口与实现分离。
赋值操作符(=)可以将一个对象赋给另一个类型的对象,这种赋值方式一般通过默认的按位成员复制来完成。按位成员复制不适用于所有的类!
变量用const修饰,其值不得被改变。任何改变此变量的代码都会产生编译错误。Const加在数据类型前后均可。
Const跟指针一起使用时有两种方法:1. const可用来限制指针不可变。也就是说指针指向的内存地址不可变,但可以随意改变该地址指向的内存的内容。2. const也可用来限制指针指向的内存不可变,但指针指向的内存地址可变。
类的每个对象都有其所有数据成员的副本。某些情况下只有一个变量副本供类的所有对象共享。静态变量就是为此以及其他用途而设计的。静态变量表示整个类范围中(所有类对象而非指定的类对象)共享的信息。
(对了,在类里定义一个静态变量,可以用它来记录这个类一共实例化了多少个对象~经典的作用,感觉上有点象COM)
最常见的类型包括容器类(也称集合类),即将类设计为保存一组对象集合。容器类通常提供插入,删除,查找,排序和测试类成员项目等操作确定该容器类是否是集合的成员。数组,堆栈,队列,树和链表都是容器类。
容器类经常与迭代对象(简称迭代器)关联。迭代器是返回集合中下一个项目的对象(或对集合中下一个项目的某种操作)。
代理类,就是为了隐藏一个类A的接口而定义另一个类B,让B有一个私有的A对象!
向客户代码提供代理类,代理类只能访问类的public接口,这样就可以让客户代码使用类的服务而不必访问类的实现细节。代理类唯一的private成员是隐藏该类对象的private数据的指针。P476
(来自网络:
友元函数要在一个类体内说明,形式为:
friend 类型名 友元函数名(形参表);
然后在类体外对友元函数进行定义,定义的格式和普通函数相同,但可以通过对象作为参数直接访问对象的私有成员
友元函数说明如下:
1)必须在类的说明中说明友元函数,说明时以关键字friend开头,后跟友元函数的函数原型,友元函数的说明可以出现在类的任何地方,包括在private和public部分;
2)注意友元函数不是类的成员函数,所以友元函数的实现和普通函数一样,在实现时不用"::"指示属于哪个类,只有成员函数才使用"::"作用域符号;
3)友元函数不能直接访问类的成员,只能访问对象成员,
4)友元函数可以访问对象的私有成员,但普通函数不行;
5)调用友元函数时,在实际参数中需要指出要访问的对象,)
继承是软件可重用性的一种形式,新类通过继承这一方式,从现有的类中吸收其属性和行为,并对其进行覆盖或改写,产生新类所需要的功能。软件的可重用性可节省程序开发的时间。
多态性可以使我们以常规方式写程序以操作多种现有的,且已专门化了的相关类。继承和多态性是管理软件复杂性的有效技术!
友元函数不能被继承!
如果派生类可以访问基类的 private成员,就会破坏基类的封装。隐藏private成员对于测试,差错,和正确地修改系统有着重要的意义。如果派生类可以访问基类的private成员,那么从该派生类派生而来的类也能访问这些数据,这种对private数据的访问能力传递下去,会削弱类的层次结构封装的优势!
基类的初始程序可以在派生类中显示调用基类的构造 函数来实现,否则派生类的构造函数会调用基类的默认构造函数。
在public继承方式下,派生类对象也可视为基类的对象,这之所以有意义是因为派生类中有相应的成员和基类的成员一一对应,记住,派生类的成员可能比基类更多,其他方向的赋值是不允许的,因为将基类对象赋值给派生类会导致派生类独有的成员不被定义。
用基类对象与派生类对象混合和匹配基类与派生类指针时,可采用以下4种方法之一:
1. 直接用基类指针指向基类对象
2. 直接用派生类指针指向派生类对象
3. 用基类指针指向派生类指针,这不会有危险,因为派生类对象同时也是它基类的对象。这样的代码只能指向基类成员。如果用基类的指针指向派生类才有的成员,会出现编译错误
4. 用派生类指针指向基类对象,会 语法错误。派生类指针必须先强制转换为基类指针。
基类指明了共性---基类的所有派生类都继承了基类的功能。在面向对象的设计中,设计者按照求同排异的方式设计基类。在继承基类功能的基础上,定制派生类。
在面向对象的系统中,类与类常联系紧密。求同排异是将共有的属性和行为放在基类中,然后用继承来生成派生类。
第十章:
对不同类型的对象进行处理,一种方法是使用switch 语句。不同类型的对象,采用的操作有所不同。
使用虚拟函数和多态性进行程序设计后,将不再需要switch逻辑。程序员可以用虚拟函数机制自动执行等价的逻辑,从而避免了switch逻辑带来的各种错误。
使用虚拟函数和多态性以后,一个有趣的结果是,程序看上去变得很简单。程序中很少有分支逻辑,而是一些简单的顺序代码。这大大简化了程序的测试,调试和维护,避免了错误!
如果用名称和圆点成员选择操作符引用一特定对象以调用虚拟函数,被调用的虚拟函数是在编译时确定的(即静态绑定),调用的虚拟函数就是为该特定对象的类(或继承该对象类)定义的函数。
把一个类视为一个数据类型时,我们假定该类型的对象要被实例化。然而,很多情况下,定义程序元不想将其实例化为任何对象的类仍然大有好处。这样的类称为“抽象类abstract class”.因为这些抽象类要被用作基类,所以通常也称为“抽象基类abstract base class”.抽象基类不能实例化为对象。
抽象类的唯一用途是为其他类提供合适的基类,以便它们可以从它那继承和/或实现接口。可实例化为对象的类称为“具体类concrete class”.
如果类从带有纯虚拟函数的类派生迩来,且该派生类中没有提供该纯虚拟函数的定义,那么这个纯虚拟函数在该类中仍然是纯虚的,这个派生类仍然是抽象类。
一个类层次结构中可以不包括任何抽象类,但许多面向对象的好系统中,类层次结构的顶部便是抽象基类。某些情况下,层次结构好的顶部好几层都是抽象类!
多态性是通过虚拟函数实现的 。通过基类指针(或引用)来请求使用虚拟函数时,C++会在与对象关联的派生类中选择正确的改写过的函数。
尽管不能实例化抽象类,但是可以声明抽象基类的指针和引用。实例化具体类的对象时,这些指针和引用就可用于实现派生类对象的多态性操作。
多态性特别适合分层的软件系统。
(内联函数不能是虚函数,因为内联函数是不能在运行中动态确定其位置的。即使虚函数在类的内部定义,编译时仍将其看作是非内联的!说明虚拟析构函数的目的在于:使用delete运算符删除一个对象时,能确保析构函数被正确地执行。这是因为设置虚拟析构函数后,可以利用动态联编方式选择析构函数!构造函数不能说明为虚拟函数,因为构造对象时还是一片未定型的空间。只有在构造函数完成后,对象才能作为一个类的名副其实的实例!)
多态性,虚函数和动态绑定的本质
在编译一个或多个虚拟函数的类时,C++会为该类建立一个虚拟函数表(vtable)。每次执行该类的虚拟函数时,执行中的程序都会用vtable来选择恰当的实现方法。
任何类在其vtable中都有一个或多个函数指针是0,它就是抽象类。类的vtable没有任何为0的指针,该类就可称为具体类!
P596