刘胥影 liuxy@seu.edu.cn 东南大学计算机学院 面向对象程序设计1 2010~2011第3学期 刘胥影 liuxy@seu.edu.cn 东南大学计算机学院
Classes: A Deeper Look (II) 教学要求与安排 chap10.1~10.7 chap10.8~10.11不要求掌握 教学安排 chap10.6放在chap11中讲 东南大学计算机学院 11/30/2018
Classes: A Deeper Look (II) 10.1 const对象和const成员 10.2 组成:对象作为类成员 10.3 友元 10.4 this指针 10.5 static成员 10.6 数据抽象和信息隐藏 (chap10.2) (chap10.3) (chap10.4) (chap10.5) (chap10.6) (chap 10.7)
const对象和const成员 const对象 const函数成员 const数据成员
10.1 const对象和const成员(1) const关键字指定元素为常量,不可修改 const对象:对象的数据成员在生命期内不被改变 若为public数据成员 How? const Time noon(12,0,0) class A{ public: int x; int f(); private: int y; }; const A a; a.x=6;//语法错:表达式必须是可 //修改的左值 //a.f()是否会修改数据成员呢? const成员函数 哪些成员函数会改变数据? 哪些成员函数是对const对象“安全”的? 东南大学计算机学院 11/30/2018
10.1 const对象和const成员(2) const成员函数:不能修改本对象数据成员 告诉编译器它对const对象是“安全的” non-const成员函数被编译器视为将要修改数据成员 const对象只能调用const成员函数 const成员函数不能调用其它non-const成员函数 对象 成员函数 Access const √ non-const X 东南大学计算机学院 11/30/2018
10.1 const对象和const成员(3) 声明及定义 要求: 不能修改本对象的数据成员 不能调用其它non-const成员函数 class A{ public: int getX() const; private: int x; }; int getX() const{ return x; } const在参数列表后 const int getX(); 2. 声明和定义中均要指定const //返回值类型 要求: 不能修改本对象的数据成员 不能调用其它non-const成员函数 东南大学计算机学院 11/30/2018
which statements are incorrect? 10.1 const对象和const成员(4) class Time{ public: Time(); void setHour(int); void printStandard(); void set(Time &) const; private: int minute; }; void Time::set(Time &another) { minute = 20; // 不能修改object printStandard(); // 不能调用non-const成员函数 another.minute = 20; // OK,非同一对象 another.setHour(6); // OK } which statements are incorrect? const 东南大学计算机学院 11/30/2018
10.1 const对象和const成员(5) 适用情况 重载:const成员函数可以进行non-const版本的重载 需要调用其他non-const成员函数的不能为const 构造函数和析构函数不能为const 不修改数据成员的任何成员函数都应该声明为const (最小权限原则) 重载:const成员函数可以进行non-const版本的重载 const对象调用const成员函数 non-const对象调用non-const成员函数 const对象生命期 构造 “常量性质”保持 析构 东南大学计算机学院 11/30/2018
10.1 const对象和const成员(6) 一个例外:可变数据成员(mutable) mutable数据成员永远都不能成为const,既使它是const 对象的成员 const成员函数可以改变mutable数据成员 An example (Fig. 10.1~10.3) 东南大学计算机学院 11/30/2018
10.1 const对象和const成员(7) const数据成员:一旦初始化,值不可修改 初始化:? 成员初始化表(只能进行初始化,不能赋值) class A{ public: A(); private: const int a; }; 如何将a初始化为1? 回顾 哪些数据类型只能在成员初始化表中进行初始化? 哪些数据类型不能在成员初始化表中初始化? A():a(1){} 东南大学计算机学院 11/30/2018
组成:对象作为数据成员 为什么需要组成 has-a关系 程序文件组织 成员对象初始化:成员初始化表 成员对象初始化:默认构造函数 构造和析构的顺序 东南大学计算机学院 11/30/2018
10.2组成(1) 代码重复 不利于程序维护 Employer Employee //BirthDay //hireDay Company -Name:string //registerDate -year:int -month:int -day:int Employer //BirthDay Employee //hireDay -year1:int -month1:int -day1:int 代码重复 不利于程序维护 东南大学计算机学院 11/30/2018
10.2组成(2) 组成(Composition):一个类可以将其他类的对象作为 自己的成员 提高软件复用性的一个普遍形式 Company -Name:string -registerDate:Date Employer -BirthDay:Date Employee -hireDay:Date Date -year:int -month:int -day:int 提高软件复用性的一个普遍形式 东南大学计算机学院 11/30/2018
10.2组成(3) has-a关系 程序文件组织(B has-a A) has-a 宿主对象 (Host Object) Employee -Name:string -BirthDay:Date Date -year:int -month:int -day:int has-a 宿主对象 (Host Object) 成员对象 (Member Object) A类声明 A.h A类定义 A.cpp #include “A.h” B类声明 B.h B类定义 B.cpp #include “B.h” 驱动程序 main.cpp 11/30/2018
10.2组成(4) 成员对象初始化:成员初始化表 不能通过构造函数在函数体内初始化! Date类没有定义Date &类型参数的构造函数 成员对象初始化:成员初始化表 不能通过构造函数在函数体内初始化! Date类没有定义Date &类型参数的构造函数 初始化使用了默认的拷贝构造函数 class E{ public: E(Date & x):d(x){}; E(Date & x){d=x};//error private: Date d; }; Date date(7,24,1985); E e(date); 东南大学计算机学院 11/30/2018
10.2组成(5) 成员对象初始化:默认构造函数 当没有显式通过成员初始化表进行初始化时,编译器自 动调用成员对象的默认构造函数进行初始化 若无默认构造函数,则必须使用成员初始化表 东南大学计算机学院 11/30/2018
10.2组成(6) 没有默认的构造函数 正确 Which one is correct? #include <iostream> using namespace std; class Test{ public: Test(int a){ num = a; } private: int num; }; class Test2{ Test2(int a, int b) { num = b; }; Test t; int main() { Test2 test(10, 20); return 0; } #include <iostream> using namespace std; class Test{ public: Test(int a){ num = a; } private: int num; }; class Test2{ Test2(int a, int b): t(a) { num = b; }; Test t; int main() { Test2 test(10, 20); return 0; } (1) 没有默认的构造函数 (2) 正确
10.2组成(7) 正确,调用了默认的构造函数 正确,调用了默认的构造函数 Which one is correct? #include <iostream> using namespace std; class Test{ public: Test(int a=0){ num = a; } private: int num; }; class Test2{ Test2(int a, int b) { num = b; }; Test t; int main() { Test2 test(10, 20); return 0; } #include <iostream> using namespace std; class Test{ public: Test(){ num = 0; } private: int num; }; class Test2{ Test2(int a, int b) { num = b; }; Test t; int main() { Test2 test(10, 20); return 0; } (3) 正确,调用了默认的构造函数 (4) 正确,调用了默认的构造函数
10.2组成(8) 构造和析构的顺序 class Employee{ public: Fig. 10.12: Employee.h class Employee{ public: Employee( const char * const, const char * const, const Date &, const Date & ); void print() const; ~Employee(); // provided to confirm destruction order private: char firstName[ 25 ]; char lastName[ 25 ]; const Date birthDate; // composition: member object const Date hireDate; // composition: member object }; 东南大学计算机学院 11/30/2018
10.2组成(9) 构造和析构的顺序 成员对象先于宿主对象构造 成员对象按在类中声明的顺序构造 成员对象后于宿主对象析构 birthDate和hireDate manager birthDate hireDate manager hireDate birthDate 东南大学计算机学院 11/30/2018
10.2组成(10) An example (Fig. 10.10~10.13) lastDayOff manager hire birth birth构造: Date object constructor for date 7/24/1949 Hire构造: Date object constructor for date 3/12/1988 birthDate缺省拷贝构造: 无输出 hireDate缺省拷贝构造: 无输出 manager构造: Employee object constructor: Bob Blue lastDayOff构造: Date object constructor for date 1/1/1994 lastDayOff析构: Date object destructor for date 1/1/1994 manager析构: Employee object destructor: Blue, Bob hireDate析构: Date object destructor for date 3/12/1988 birthDate析构: Date object destructor for date 7/24/1949 hire析构: Date object destructor for date 3/12/1988 birth析构: Date object destructor for date 7/24/1949 lastDayOff manager hireDate birthDate hire birth main栈区 东南大学计算机学院 11/30/2018
内 容 回 顾 9.5 破坏类的封装性:返回私有数据成员的引用 9.6 默认的逐个成员赋值 默认的赋值运算符 默认的拷贝构造函数 成员包含指针时的问题:不经意改变对象,内存泄 漏 东南大学计算机学院 11/30/2018
内 容 回 顾 10.1 const对象和const成员 const对象:对象的数据成员在生命期内不被改变 const成员函数不能调用其它non-const成员函数 只能通过成员初始化表进行初始化 东南大学计算机学院 11/30/2018
内 容 回 顾 10.2 组成 数据成员为其他类的对象 has-a关系 初始化 构造和析构的顺序 显式:成员初始化表 隐式:默认构造函数 构造:成员对象>宿主对象 析构:宿主对象>成员对象 东南大学计算机学院 11/30/2018
友元 友元 声明和定义 友元关系 示例 东南大学计算机学院 11/30/2018
10.3友元(1) 友元(friend)在类外定义,能访问类的所有数据成员 通常在运算符重载中使用 声明和定义 友元函数(friend function) 友元类(friend class):所有成员函数都是友元函数 通常在运算符重载中使用 声明和定义 类内声明,类外定义 声明时位置无关,访问属性无关 友元函数不是类成员 class A{ friend void f(A &); friend class B; public: Test():x(0){}; private: int x; }; void f(A & a){ a.x=1; } class B{……}; 良好的编程习惯 东南大学计算机学院 11/30/2018
10.3友元(2) 友元关系 x 是授予关系(A grant B),而不是索取关系,是A类的主动 行为 友元关系既不对称,又不传递 A B friend x C class A{ friend class B; public: Test():x(0){}; private: int x; }; B是A的友元 东南大学计算机学院 11/30/2018
10.3友元(3) 友元函数示例(Fig. 10.15): class Count int main() { { friend void setX( Count &, int ); public: Count( ) : x( 0 ) // initialize x to 0 // empty body } void print() const { cout << x << endl; } private: int x; }; void setX( Count &c, int val ) { c.x = val; } int main() { Count counter; counter.print(); setX( counter, 8 ); return 0; } 8 东南大学计算机学院 11/30/2018
10.3友元(4) 友元类示例 class ClassOne{ friend class ClassTwo; // friend declaration int x, int y; }; class ClassTwo{ public: void setX(ClassOne &one, int x){ one.x = x; } void setY(ClassOne &one, int y){ one.y = y; } void printClassOne(ClassOne &one){ cout << "ClassOne.x = " << one.x << ", ClassOne.y = " << one.y << endl; } int main() { ClassOne one; ClassTwo two; two.setX(one, 10); two.setY(one, 20); two.printClassOne(one); return 0; } 东南大学计算机学院 11/30/2018
this指针 this指针 特点 this指针的类型 this指针的使用 东南大学计算机学院 11/30/2018
10.4 this指针(1) class Test{ public: void func(); private: int num; }; item1.num item2.num Stack: Test::func() 形参列表 this指针 Test::func() ClassName item1; ClassName item2; item1.func(); 代码区 void func(Test *this) this指针是成员函数的隐含形参,用来指向该对象的指针 (const指针类型) 定义方式:由编译器隐含定义,不能由成员函数定义 东南大学计算机学院 11/30/2018
10.4 this指针(2) 特点 this是C++保留字 this指针是const指针 this指针和对象是一一对应的 指向的对象可变 保存的地址不可变 this指针和对象是一一对应的 每个对象都有唯一的this指针 每个this指针都唯一对应一个对象 this指针不是对象的一部分 sizeof()不计算this指针的大小 由编译器隐含定义 东南大学计算机学院 11/30/2018
10.4 this指针(3) this指针的类型取决于对象的类类型,以及是否为 const成员函数 普通函数 const成员函数 A * const 可以改变this指向的对象 不能改变this保存的地址 const成员函数 返回const类类型的const指针 const A * const 不能改变this指向的对象 不能改变this保存的地址 class A{ public: A & f1(){ return *this; } const A & f2() const{ return *this; } private: int x; }; 东南大学计算机学院
10.4 this指针(4) this指针的使用 可以由成员函数显式使用 访问类成员 通常用于返回对调用函数的对象的引用 x:隐式调用 11/30/2018
10.4 this指针(5) 成员函数的级联调用(cascade) (.):左结合性 class Screen{ public: Screen& move(int); Screen& set(char); … … }; Screen& Screen::move(int a){ return *this } Screen& Screen::set(char c){ myScreen.move(4).set(‘x’); myScreen.move(4); myScreen.set(‘x’); (.):左结合性 myScreen.move(4).set(‘x’); myScreen.set(‘x’).move(4); //which one is wrong? An example (Fig. 10.18~10.20) 11/30/2018
static类成员 为什么需要static类成员 static数据成员 static成员函数 作用域、访问控制、声明与定义、初始化、访问方式 调用方式、成员函数:static or non-static、特点 东南大学计算机学院 11/30/2018
10.5 static类成员(1) static数据成员 类 int a; int b; 对象1 … … 对象2 对象n 数据成员全部成为对象的组成是否合理? 东南大学计算机学院 11/30/2018
10.5 static类成员(2) A motivation example 浪费存储空间 难以维护 学生 学生1 … … 学生2 学生n int name; int count; 学生 学生1 … … 学生2 学生n 学生总人数:count int name; int count; 新学生 Update count of all objects! 浪费存储空间 难以维护 东南大学计算机学院 11/30/2018
10.5 static类成员(3) A motivation example static数据成员描述类的属性,而非特定对象的属性 int name; int count; 学生 学生1 … … 学生2 学生n 学生总人数:count int name; static int count; int name; int count; 新学生 static数据成员描述类的属性,而非特定对象的属性 它独立于对象而存在 对象不包含static数据成员 东南大学计算机学院 11/30/2018
10.5 static类成员(4) 作用域:类作用域 访问控制 类作用域内,类成员可以被其他成员访问 类作用域外,public成员通过对象的句柄访问 访问控制 public protected private 东南大学计算机学院 11/30/2018
10.5 static类成员(5) 声明与定义 类内声明,类外定义、初始化 若类的实现和接口分离,则应在实现部分定义和初始化 否则“error LNK1169: 找到一个或多个多重定义的符号” //className.h class className{ public: void f(); static int total; }; //className.cpp int className::total; // 不用static关键字修饰 void className::f(){……} 不能缺少定义 否则:error LNK2001: 无法解析的外部符号 东南大学计算机学院 11/30/2018
10.5 static类成员(6) 当static成员需要被多个文件的代码访问时,只能在.h文 件中声明为static,不能在.cpp文件中声明为static //main.cpp class className{ public: static int total; }; int className::total; int main(){ … … } //className.cpp class className{ public: void f(); static int total; }; int className::total; //main.cpp int main(){ … … } No error include “className.cpp” error No error static关键字用于文件作用域的元素时,它只在该文件内可见 东南大学计算机学院 11/30/2018
10.5 static类成员(7) 初始化 至多只能被初始化一次 类外定义处初始化,不能在任何函数体中 (文件作用域) static const整型(int/char/bool)或枚举类型可在类内声明 时初始化 可以通过成员初始化表进行初始化吗?为什么? class className{ private: static const int a = 0; static const char b = ‘h’; static const bool c = false; static int total; static const double d; }; int className::total = 0; // 不用static关键字修饰 double className::d = 0.0; 东南大学计算机学院 11/30/2018
10.5 static类成员(8) 默认的初始化 访问方式 基本类型:0 成员对象:默认构造函数 public: 通过类名访问:A::x 通过对象访问:a.x private, protected:public成员函数/友元 class A{ public: static int x; }; A::x=1; A a; a.x=1; 11/30/2018
class A{ public: A(){ cout<<“x="<<x<<endl; } void print(){ cout<<“y="<<y<<endl; private: static int x; const static int y = 10; }; class B{ B(){}; static A m; A B::m; int main(){ B b; b.m.print(); return 0; } x=20 y=10 x=0 y=10 int A::x=20; int A::x; error LNK2001: unresolved external symbol "private: static int A::x"
class A{ public: A(int n){ cout<<“x="<<x<<endl; } void print(){ cout<<“y="<<y<<endl; private: static int x; const static int y = 10; }; class B{ B(){}; static A m; int main(){ B b; b.m.print(); return 0; } int A::x=20; A B::m; A B::m= A(30);
10.5 static类成员(11) static成员函数 作用 调用方式 为类提供服务,而非为对象提供服务 独立于对象而存在并操作 访问非public的static数据成员 显示类的信息 调用方式 通过类名:A::f() 通过对象:a.f() 东南大学计算机学院 11/30/2018
10.5 static类成员(12) 成员函数:static or non-static? 如果成员函数不访问non-static成员,则应声明为static static成员函数不能访问non-static成员(数据/函数) why? class A{ public: void f(){}; static void print(){ cout<<num<<endl; //error }; static void test(){ f(); //error private: int num; 东南大学计算机学院 11/30/2018
10.5 static类成员(13) 成员函数:static or non-static? 对象 成员函数 Access const √ 调用者 被调用者 static成员函数 static成员 √ non-static成员 X non-static成员函数 对象 成员函数 Access const √ non-const X 回顾 东南大学计算机学院 11/30/2018
10.5 static类成员(14) 特点 An example (Fig.10.21 ~ 10.23) 不具有this指针 不能声明为const An example (Fig.10.21 ~ 10.23) this指针指向对象,this指针和对象一一对应 在static成员函数中使用this指针是编译错误! const限定成员函数不能修改操作对象的内容 将static成员函数声明为const是编译错误! 东南大学计算机学院 11/30/2018
数据抽象和信息隐藏 信息隐藏 数据抽象 抽象数据类型 东南大学计算机学院 11/30/2018
10.6数据抽象和信息隐藏(1) 信息隐藏(Information Hiding) 对类客户隐藏类的实现细节 数据抽象(Data Abstraction) 类客户只关心类提供的功能,不关心其具体实现 只要public服务不变,系统的其他部分不受影响 例子:stack 数组实现 链表实现 东南大学计算机学院 11/30/2018
10.6数据抽象和信息隐藏(2) 其他语言: C++ 强调动作 数据支持动作 数据类型为内置的 强调数据 动作为特定类型的数据服务 数据类型可自定义 利用抽象数据类型可以建立强调数据的语言 东南大学计算机学院 11/30/2018
10.6数据抽象和信息隐藏(3) 抽象数据类型(Abstract Data Type, ADT)包含 类是抽象数据类型的实现 数据表达 可在数据上执行的操作 类是抽象数据类型的实现 数据表达:数据成员 可在数据上执行的操作:成员函数 例子:队列(queue) 入队: enqueue 出队: dequeue 东南大学计算机学院 11/30/2018
Classes: A Deeper Look (II) 教学要求与安排 chap10.1~10.7 chap10.8~10.11不要求掌握 教学安排 chap10.6放在chap11中讲 东南大学计算机学院 11/30/2018
作业 deadline: 4.10 24:00 H3: Ex10.9 deadline: 4.19 24:00 东南大学计算机学院 11/30/2018