C++面向对象的程序设计


1 一、面向对象程序设计理论一、面向对象程序设计理论 1.1 面向对象的程序设计方法基础 1.2 对象和类 1.3 继承性 1.4 重载 1.5 多态性 2 参考书参考书 z 《C++程序设计语言》pp905 ¥85 (美) Bjarne Stroustrup 推荐指数:☆☆☆☆☆ z 《C++程序设计教程》pp468 ¥39.5 钱能 清华大学出版社 推荐指数:☆☆☆☆ z 《C++面向对象程序设计》 谭浩强谭浩强 z 推荐指数: ☆☆☆☆☆ z 《C++语言基础教程》 吕凤翥编著吕凤翥编著 推荐指数: ☆☆☆☆☆ z 《C++ Primer中文版(第三版)》 zz StandleyStandley B.LippmanB.Lippman著著 潘爱民译潘爱民译 3 4 www.sourceforge.netwww.sourceforge.net 5 www.codeproject.comwww.codeproject.com 6 www.codeguru.comwww.codeguru.com 7 www.vckbase.comwww.vckbase.com 8 CC++语言发展历史++语言发展历史 z C++语言 C语言 B语言 z 高级语言ALGOL 60 :1960年,面向问题 z CPL(Combined Programming Language)语言:1963 年,英国剑桥大学,简化为BCPL语言 z 1970年,贝尔实验室K.Thompson,设计了一 种类似于BCPL的语言,称为B语言 9 CC++语言发展历史++语言发展历史 z 1972年,贝尔实验室Dennis M.Ritchie,为克 服B语言的诸多不足,在B语言的基础上重新 设计了一种语言,BCPL,称为C语言 z 1980年,贝尔实验室Bjarne Stroustrup对C语 言进行了扩充,推出了“带类的C”,多次修 改后起名为C++。以后又经过不断的改进, 发展成为今天的C++ z C++改进了C的不足之处,支持面向对象的 程序设计,在改进的同时保持了C的简洁性 和高效性。 10 1.11.1 OOPOOP基础基础 • 面向对象的程序设计方法(Object Oriented Programming)简称为OOP, 它的基本元素是类 ( Class )和对象 (Object)。 • 使用对象模拟自然的或抽象的实体,对 用户掩盖了实现的复杂性(封装),并 且能使程序员付出尽可能小的代价,获 得尽可能大收益(继承),提高软件的 开发效率。 11 1.21.2 对象和类对象和类 • 类是对事物的特性及操作的抽象描述, 对象是类的实体。 • 从C语言的角度来看,类是结构的自然 演化,类就是包含数据声明和函数声明 的结构。 • 类既包含了数据又包含了代码,对类中 成员的存取是受控制的,这一控制不仅 针对数据,也针对代码,类的这个特性 被称为封装。 12 计算过程的演变: 手工计算 全部手工和脑力完成 计算器(面向过程) 部分借助非人力因素,但仍需 对过程进行控制和管理 计算机(面向对象) 完全借助非人力因素,运算一 旦开始,可以无需人工的介入 封装的说明封装的说明 13 交通工具 类继承类继承 水运工具 公路工具 铁路 空运 汽车二轮车 三轮车 大众奇瑞 福特 现代 速腾捷达 帕萨特 奥迪 14 特性: 脚个数 物理位置 长度 宽度 高度 形状 操作: 搬动 改造 类的说明类的说明 “桌子”类 15 定义类定义类 class Counter { long count; public: void SetValue(long); long GetValue(); }; 关键字class引导类声明 Counter是类标识符 类体 分号 16 使用类使用类 类 是 一 个模板,就象数据类型一 样,使用的时候必须定义类变量。 定义类变量称为类的实例化,类的 实例化变量就是对象。 void main () { Counter people; long value; people.SetValue(0); value = people.GetValue(); } 对象是具有某一 个类所描述的共 性的,但又有各 自特性的个体。 17 类存取控制类存取控制 类的成员的使用者有三类: 1. 类本身 2. 派生类 3. 一般用户 每种使用者权限不同,对应与 不同的使用权限,有不同的关 键字说明。 18 类类私有成员(私有成员(privateprivate)) 类本身(或类的友元)可以存取 类的私有成员。派生类不能存取其父 类的私有成员。 因为类必须在程序中使用,所以 类必须至少有一个非私有的成员。 类成员的存取控制缺省为私有类 型。 19 类私有成员(续) class PrivateClass { long value; void f1(); void f2(); }; void main() { PrivateClass object; long l = object.value; object.f1(); object.f2(); } 提问:上述程序可以达到预期效果吗? 类私有成员(续)类私有成员(续) 20 类类公有成员(公有成员(publicpublic)) 在程序中使用给定类的对象,必 须能存取成员数据、成员函数或两 者。 为使这些成员在程序中可以 被存取,必须在类中声明公有部分。 在公有部分定义的内容允许被其 他对象无限制地存取。通常可以有控 制地使用公有成员存取私有数据,调 用私有成员函数完成工作。 21 类公有成员(续类公有成员(续)) class PublicExample { public: int variable; void function(); }; void main() { PublicExample object; int i = object.variable; object.function(); } 22 类保护成员(类保护成员(protectedprotected)) 类的本身可以存取类的保护成员。 派生类也可以存取其父类的保护成员。 一般用户不能存取类的保护成员。 23 类保护成员(类保护成员(protectedprotected)) class A { protected: int value_a; }; class B:public A { public: void FB(); }; class C:public B{ public: void FC(); } void B::FB() { value_a = 0; } void C::FC() { value_a = 0 } 24 访问权限访问权限 Access in Base Class Base Class Inherited as Access in Derived Class Public Protected Private Public Public Protected No access Public Protected Private Protected Protected Protected No access Public Protected Private Private Private Private No access 25 问题问题 class A { int value_1; protected: int value_2; void FA_1(); public: int value_3; void FA_2(); }; class B:public A{ public: int value_4; void FB(); } 问:在类B的FB函数中可以 直接存取的成员有哪些? 问:通过类B的对象在程序 中可以直接访问的成员有 哪些? 26 特殊类成员特殊类成员特殊类成员 1、构造函数(constructor) 2、析构函数(destructor) 27 构造函数构造函数 构造函数是用来建立某给 定类的对象的。一个类可以不 定义构造函数,也可以定义一 个或多个构造函数。编译系统 保证类的构造函数先于其他函 数被调用。 28 构造函数(续一)构造函数(续一) class Counter { long value; public: Counter(); }; Counter::Counter() { value = 100; } void main() { Counter c1; return; } 构造函数Counter() 它有什么特点? 29 构造函数(续二)构造函数(续二) class Counter { long value; public: Counter(int); }; Counter::Counter(int i) { value = i; } void main() { Counter c1(100); return; } 构造函数Counter() 它有什么特点? 30 构造函数(续三)构造函数(续三) class Counter { long value; public: Counter() Counter(int i); }; Counter::Counter() { value = 100; } Counter::Counter(int i) { value = i; } void main() { Counter c1; Counter c2(100); return; } 31 构造函数(续四)构造函数(续四) class Counter { long value; public: Counter() { value = 100; } Counter(int i = 0) { value = i;} }; void main() { Counter c1; Counter c2(100); } 问:Counter类是否能 包含如上所示的两个 构造函数?为什么? 32 构造函数(续五)构造函数(续五) class Counter { long value; public: Counter(long i){value = i}; Counter(Counter &); }; Counter::Counter(Counter & reference) { value = reference.value; } 33 void main() { Counter object(5); Counter object1 = object; }; 构造函数(续六)构造函数(续六) 这个等号表示复制构造函数 void main() { Counter object(5); Counter Proc_A(object); }; 把对象作为参数传递, 也需要拷贝构造函数。 34 构造函数(小结一)构造函数(小结一) 1、缺省构造函数 2、有特定初始化值的构造函数 3、通过复制另一个对象建立新 对象的构造函数 35 构造函数(小结二构造函数(小结二)) 1、构造函数没有返回类型 2、构造函数与类同名 3、同一个类可以有多个构造函 数 36 构造函数(思考题)构造函数(思考题) class Counter { long value; public: Counter(); }; class Example { int value; public: Counter c; Example(); }; void main() { Example e; } 在本例中,当c对象作 为e对象的成员。 问:c对象和e对象的 构造函数的调用顺序 是怎样的? 37 析构函数析构函数 析构函数在对象不再被使 用时,执行一些必须的操作, 一般是构造函数的逆操作。一 个类只能声明一个析构函数。 析构函数是构造函数的逆 函数。 38 析构函数(续)析构函数(续) class Counter { long value; public: ~Counter(); }; Counter::~Counter() { printf(“…”); } 析构函数在 声明和使用 上有什么特 点? 39 析构函数(小结析构函数(小结)) 1、析构函数没有返回类型 2、析构函数的命名是在类名前 加“~” 3、一个类只能有一个析构函数 4、析构函数没有参数 40 1.31.3 继承性继承性 从编码角度讲,从基类中派生类 以较低代价换来了大量的灵活性。一 旦产生了可靠的基类,只需要调试派 生类中所做的修改即可。 派生类从它的父类中继承性质 时,可使派生类对它们进行扩展、限 制、改变、删除或不做任何修改。所 有这些变化归结成两类基本的面向对 象技术:性质约束和性质扩展。 41 单一继承单一继承 class First { int value_1; protected: int value_2; public: int value_3; }; class Second:First{ int value_4; public: void F2(); }; 问:在程序中可 以访问的Second 类的对象的成员 变量有那些? 42 基类存取限定符基类存取限定符 class First { int value_1; protected: int value_2; public: int value_3; }; class Second:public First{ int value_4; public: void F2(); }; 基类存取限定符 43 基类存取限定符(续)基类存取限定符(续) private:如果没有特别说明,private 是缺省的限定符。基类中所有可继承 的成员在派生类中都变成私有的。 public:基类中所有public成员在派生 类中仍为public成员,…… 44 构造函数的调用构造函数的调用 class A { public: A() { ……;} }; class B : public A { public: B() { ……;} }; 如果现在将类 B实例化成一 个对象,构造 函数的调用情 况会如何? 45 构造函数的调用(续)构造函数的调用(续) 类实例化时,需要调用其构造函 数。如果该类是派生类,必须调用其 父类的构造函数,如果其父类也是派 生出来的,重复该过程直至到达非派 生的基类。 46 析构函数的调用析构函数的调用 析构函数的调用顺序与构造函数 的调用顺序相反。 47 构造函数的参数传递构造函数的参数传递 class A { public: int va; A(int i) { va = i;} }; class B:public A { public: int vb; B(int i, int j) { va = i; vb = j} }; 48 构造函数的参数传递(续)构造函数的参数传递(续) class A { public: int va; A(int i) { va = i;} }; class B :public A { public: int vb; B(inti, intj): A(i){ vb = j;} }; 49 派生类的类型转换派生类的类型转换 void main { First c1; Second c2; First * d1 = new First; Second * d2 = new Second; c1 = c2; d1 = d2; c2 = c1; d2 = d1; }; 如果这样则不行! 50 作用域的分辨作用域的分辨 class A { public: int get () { return 1;} }; class B: public A{ public: int get () { return 2;} }; void main() { A a; B b; int i = a.get(); int j = b.get(); } 51 作用域的分辨(续)作用域的分辨(续) class A { public: int get () { return 1;} }; class B: public A { public: int get () { return 2;} }; void main() { A a; B b; int i = a.get(); int j = b.get(); int k = b.A::get(); } 作用域分辨操作符 52 性质扩展性质扩展 使用派生类的主要原因是基类提 供了派生类需要的部分特征。常常基 类很类似于需要的类,但不完全相 同,其中有的函数需要稍微扩展一 下。如果在派生类中再次书写整个函 数会浪费很多时间。因此,可以充分 利用类的继承特性,将函数的功能进 行扩充。 53 性质扩展(续一性质扩展(续一)) 假设: 有一个类A,它有两个成员函数,其 中一个函数的功能的清屏,另一个函 数的功能是用“*”画一条直线,直线 的长度根据参数决定。 现在需要一个新类B,希望它具有清 屏的功能,同时具有用“*”画三角形 的功能。 54 性质扩展(续二性质扩展(续二)) ******* n=7的直线 * ** *** **** ***** ****** ******* n=7的三角形 55 性质约束性质约束 用户常常找到一些基本合乎要求 的类,但是它们可能会有一些不受欢 迎的行为。这时,可以使用性质约束 的方法来使派生类不具有某种功能。 56 性质约束(续)性质约束(续) 假设: 有一个类A,同上例。 有一个类B,具有用“*”画三角形的功 能,但是它每次画三角形之前都会先 清屏。 现在需要一个新的类C,希望它具有 类B画三角形的功能,但是在画三角 形之前不会自动清屏。同时它依然保 留单独清屏的功能。 57 多重继承多重继承 C++语言中,一个类的父类并不 只局限于一个,实际上可以有多个, 派生类可以从每个父类中继承性质。 当然,这种继承性增加了语言和编译 器的复杂性,但相比之下益处更多。 多重继承大大增加了类继承的灵 活性。 58 多重继承的实现多重继承的实现 假设: 有一个类A,具有单独清屏的功能。 有一个类B,具有用“*”画直线的功 能,但是没有清屏的功能。 现在需要一个新的类C,希望它具有 用“*”画三角形的功能,又具有单独 清屏的功能。 59 多重继承的构造函数多重继承的构造函数 class A { …… }; class B { …… }; class C:public A, public B { public: C(int i, int j, int k) } C::C(int i,int j,int k):B(j),A(i) { …… }; 构造函数的调用次 序是怎样的? 60 虚虚基类基类 class A { public: int value; }; class B :public A{ …… }; class C:public A { …… } class D:public B,public C { public: int get() { return value}; }; 这个value值从何而 来? 61 虚基类(续一)虚基类(续一) class A { public: int value; }; class B:public virtual A{ …… }; class C:public virtual A { …… } class D:public B,public C { public: int get() { return value}; }; 62 虚基类(续二)虚基类(续二) A A A B C B C D D 63 1.41.4 重载重载 在OOP中,简洁性是很重要的。 用户使用系统时对系统了解得越少越 好,调用函数时需要的细节越少越好 (参数的个数和类型)。如果函数能 够接受各种类型的参数,由编译器区 分细节,则会很方便。 这个目的在C++中可以达到。但 是重载并不是一个全新的概念。 64 重载(续)重载(续) 1、函数重载 非成员函数重载 成员函数重载 2、操作符重载 所谓重载就是同一个符号在不 同的上下文中代表不同的含 义。 65 重载非成员函数重载非成员函数 void Display(char * string) {……} void Display(long value) {……} void Display(double value) {……} void main() { Display(“\nPrint this,please!”); Display(123456789); Display(3.14159); } 66 重载非成员函数(续一)重载非成员函数(续一) void Display(char * string) {……} void Display(long value) {……} void Display(double value) {……} void main() { Display(333); } 67 重载非成员函数(续二)重载非成员函数(续二) void Display(char * string) {……} void Display(long value) {……} void Display(double value) {……} void Display(float value) {……} void main() { Display(3.14159); } 68 重载非成员函数(续三)重载非成员函数(续三) void Display(float value) {……} int Display(float value) {……} void main() { Display(3.14159); } 不同的返回值不能用 来区分重载函数。 69 重载成员函数重载成员函数 class Example { int value; public: void Value(int v) { value =v; } int Value() { return value; } }; void main() { Example e; e.Value(3); int i = e.Value(); } 70 类等级中的重载函数类等级中的重载函数 class A { public: int foo (int i) { return i+1;} }; class B:public A { public: int foo (float f) { return f+10;} }; void main() { B b; int i = b.foo(2); } 到底应该调用A的 foo还是B的foo? 71 类等级中的重载函数(续一)类等级中的重载函数(续一) class A { public: int foo (int i) { return i+1;} }; class B:public A { public: int foo (float f) { return f+10;} }; void main() { B b; int i = b.foo(2); } 这种情况不构成重载 72 类等级中的重载函数(续二)类等级中的重载函数(续二) int f(A& a) { return a.foo(3.14159F); } void main() { B b; int i = f(b); } i的结果是多少? 73 作用域分辨符作用域分辨符 class A { public: int foo(int i) {return i;} }; class B:public A { public: int foo(float f) {return f;}} int foo(int i) {return A::foo(i);} }; void main() { B b; int i = b.foo(2); int j = b.A::foo(2); int k = b.foo(3.14f); 74 参数匹配参数匹配 参数匹配问题既重要有复杂。编 译器遇到对重载函数的调用时,必须 确定调用哪个函数。如果能找到参数 完全匹配的函数,自然没问题,找不 到时,则找一替代函数。此时,编译 器将实在参数与所有重载函数的参数 比较,这个过程叫作参数匹配。 75 参数匹配(续)参数匹配(续) 1、类型转换不是非要不可的。 2、有时需要作参数升级。参数可以 沿路径char→int → long → float → double → long → double升级。 3、参数转换是可以的。参数可以根 据标准或用户定义的转换规则进行转 换。 76 重载操作符重载操作符 class Example { int value; public: void Value(int v) { value =v; } int Value() { return value; } }; void main() { Example e(?); …… e.Value(100+ e.Value()); } 如果能这样: e = e+100; 既直观又方便。 77 如何重新理解操作符如何重新理解操作符 一元操作符 @ X X.@(); 二元操作符 X @ Y X.@(Y); @是一个操作符,但 是也可以被理解为一 个成员函数。 78 重载操作符实例重载操作符实例 class Counter { int value; public: Counter(int i) { value =i; } Counter operator !() {return Counter(!value); } Counter operator +(Counter &c) { return Counter(value+c.value); } }; void main() { Counter c1(3),c2(5); c1 = !c1; c1 = c1+c2; } 79 重载操作符实例(续)重载操作符实例(续) class Counter { int value; public: Counter(int i) { value =i; } Counter operator !( int i) {return Counter(!value); } Counter operator +(Counter &c) { return Counter(value+c.value); } }; 放一个不用的参数可 以吗? 返回类型 为什么经 常是对象 类型。 c1+c2+c3+c4+c5 80 名字分裂及其规则名字分裂及其规则 名字分裂的目的:使产生重载的符号 在编译系统内部有唯一的名字,从而 使得它们在目标代码中有不同的入口 地址。 名字分裂原则: <@类名>@函数名$q符号化的参数表 void Counter::Value(int k) @Counter@Value$qi 81 1.51.5 多态性多态性 多态性是指C++的代码可以根据 运行情况的不同执行不同操作。这通 常不能由程序员来直接控制,而必须 依靠面向对象的程序设计的特性,使 对象对自身的运行进行跟踪。 多态性是通过类的体系结构来实 现的,不过,只有类成员函数可以具 有多态性,而不是整个类都具有多态 性。 82 先期联编先期联编 class A { public: void foo (); }; class B:public A { public: void foo (); }; int f(A & a) { return a.foo(); } void main() { B b; f(b); } 这个地方 最终调用 的是哪个 类的foo 函数?为 什么? 83 迟后联编迟后联编 如果在上例中需要f函数根据形 式参数中传递进来的对象的类型自 动调用该类的foo函数,则需要激活 C++的迟后联编的功能,使程序在执 行的过程中自动选择相应的函数。 84 迟后联编(续)迟后联编(续) class A { public: virtual void foo (); }; class B:public A { public: virtual void foo (); }; int f(A & a) { return a.foo(); } void main() { B b; f(b); } 这时将 根据实 际的参 数类型 来选择 相应的 函数。 85 虚函数虚函数 声明为virtual的函数为虚函数。 通过声明函数为virtual,来人为指定 迟后联编。迟后联编只能用于类等 级中的对象。对于不被任何类继承 的类,声明其函数为虚函数,从语 法上来讲是正确的,但是会引起不 必要的运行时刻开销。 86 函数覆盖函数覆盖 1、虚函数的参数个数与类型。 2、虚函数的传递。 使用虚函数后,派生类中的 函数可以覆盖原有基类中函数的 功能,这被称为函数覆盖。构成 函数覆盖的条件是: 87 抽象类抽象类 由虚函数的传递性可以看出,出 现在类等级顶部及其附近的类常常有 多个空的虚函数。如果一个类只有空 虚函数,实例化这样的类有意义吗? 但是,这种实例化是允许的,其结果 是得到一个占有内存却不做任何操作 的无用的对象。为了避免这种情况的 发生,C++中引进了可以被继承但是 不能实例化的抽象类。 88 纯虚函数纯虚函数 至少包含一个纯虚函数的类被称 为抽象类。 class A { public: virtual void foo () = 0; }; 注意纯虚函数的 定义方法。 89 纯虚函数的使用纯虚函数的使用 class A { public: virtual void foo ()=0; }; void main() { A a; a.foo(); } 注意:抽象类不 能实例化。 90 纯虚函数的使用纯虚函数的使用 class A { public: virtual void foo ()=0; }; class B:public A { public: virtual void foo (); }; void main() { B b; b.foo(); } 注意:纯虚函数 一定要被覆盖。 91 作用域分辨符作用域分辨符 class A { public: virtual void foo (); }; class B:public A { public: virtual void foo (); }; int f(A & a) { return a.A::foo(); } void main() { B b; f(b); } 此处调用 的依然是 A::foo() 函数,而 不是 B::foo()函 数
还剩90页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 5 金币 [ 分享pdf获得金币 ] 0 人已下载

下载pdf

pdf贡献者

ljf551

贡献于2013-06-02

下载需要 5 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf