Chapter 10 Classes: A Deeper Look, Part 2 http://jssec.seu.edu.cn 杨明 yangming2002@seu.edu.cn
OBJECTIVES To specify const (constant) objects and const member functions. To create objects composed of other objects. To use friend functions and friend classes. To use the this pointer. To create and destroy objects dynamically with operators new and delete, respectively. To use static data members and member functions. 2
10.1 Introduction Topics 10.2 const (Constant) Objects and const Member Functions 10.3 Composition: Objects as Members of Classes 10.4 friend Functions and friend Classes 10.5 Using the this Pointer 10.6 Dynamic Memory Management with Operators new and delete 10.7 static Class Members 3
10.1 Introduction const 对象, 常成员函数, 常数据成员 类的组合 (composition): 一个类以其他类的对象作为成员 友元 (friend) 函数 : 非成员函数如何通过对象句柄访问类的私有成员? this 指针 : 非 static 成员函数的隐含参数 动态内存管理 : new 和 delete 运算符 静态 static 的类成员 4
10.1 Introduction Topics 10.2 const (Constant) Objects and const Member Functions 10.3 Composition: Objects as Members of Classes 10.4 friend Functions and friend Classes 10.5 Using the this Pointer 10.6 Dynamic Memory Management with Operators new and delete 10.7 static Class Members 5
10.2 const (Constant) Objects and const Member Functions const Time noon( 12, 0, 0 ); 构造后不能更改数据成员 措施 : 仅能调用 noon 对象的 const member function ( 常成员函数 ) OBJECT Member Function Access const const const non-const X non-const const non-const non-const 6
10.2 const (Constant) Objects and const Member Functions (1) 常成员函数 Prototype void printuniversal() const; Definition void Time::printUniversal() const { } 实现代码要求 : 不能修改本 (this)object ( 数据成员 ) 直接 不能调用其它 non-const 成员函数 间接 7
10.2 const (Constant) Objects and const Member Functions 1. void Time::test(Time &another) const 2. { 3. minute = 20; 4. // 不能修改 object, this->minute=20 5. printstandard(); 6. // 不能调用 non-const 成员函数 7. another.minute = 20; 8. // OK, 非 this object 9. another.sethour(6); 10. // OK 11. } + noon.test( wakeup ); 8
10.2 const (Constant) Objects and const Member Functions 注意点 : 成员函数是否为常成员函数, 不仅取决于它不修改本对象 不调用 non-const 成员函数, 而且必须显式地声明为 const! 构造函数 / 析构函数不能 也不需要声明为 const! 对象的常量特性体现在初始化 ( 构造 ) 后 析构之前. 建议 : 所有不更改 object 的成员函数均声明为 const 成员函数 ( 让 const 对象能正常访问 ) 9
10.2 const (Constant) Objects and const Member Functions (2) 常数据成员 constructor initializer list ( 构造函数初始化列表 ) Increment::Increment(int i) : increment( i ){..} 使用括号中的值构造 / 初始化该成员! 所有类数据成员都可以用构造函数初始化列表进行初始化, 而以下情况必须如此 : const data member ( 例外?) reference data member 引用类型的数据成员 member objects, 成员对象 派生类的基类 (base class) 10
1. class Increment 2. { 3. public: 4. Increment( int c = 0, int i = 1 ); // default constructor 5. 6. // function addincrement definition 7. void addincrement() 8. { 9. count += increment; 10. } // end function addincrement 11. 12. void print() const; // prints count and increment 13. private: 14. int count; 15. const int increment; // const data member 16. int &refcount; 17. }; // end class Increment 11
1. // constructor 2. Increment::Increment( int c, int i ) 3. : count( c ), // initializer for non-const member 4. increment( i ), // required initializer for const member 5. refcount ( count ) // required initializer for reference member 6. { 7. cout << "Now count = " << count << ", increment = " << increment 8. << " and refcount= " << refcount<< endl; 9. refcount = 99; 10. } 11. 12. // print count and increment values 13. void Increment::print() const 14. { 15. cout << "count = " << count << ", increment = " << increment << endl; 16. } Increment value( 10, 5 ); value.print(); Now count = 10, increment = 5 and refcount = 10 count = 99, increment = 5 12
10.1 Introduction Topics 10.2 const (Constant) Objects and const Member Functions 10.3 Composition: Objects as Members of Classes 10.4 friend Functions and friend Classes 10.5 Using the this Pointer 10.6 Dynamic Memory Management with Operators new and delete 10.7 static Class Members 13
10.3 Composition: Objects as Members of Classes Composition ( 组合, has-a): A class has objects of other class as members. (vs is-a) Host Object ( 宿主对象 ) vs Contained Object( 被包含对象 ) Employees - LastName: String - FirstName: String - birthdate: Date - hiredate: Date 14
10.3 Composition: Objects as Members of Classes // P53 fig03_05.cpp 15. class GradeBook 16. { 19. void setcoursename ( string name ) 20. { 21. coursename = name; 22. } 38. private: 39. string coursename; 40. }; 15
#include <iostream> #include <iostream> using namespace std; using namespace std; class Test{ class Test{ public: public: Test( int a ){ num = a; } Test( int a ){ num = a; } private: private: int num; int num; }; }; class Test2{ class Test2{ public: public: Test2( int a, int b ) { num = b; } Test2( int a, int b ): t(a) { num = b; } private: private: Test t; Test t; int num; int num; }; }; 使用构造函数初始化列表, int main() int main() 对类成员 t 进行初始化! { { Test2 test( 10, 20 ); Test2 test( 10, 20 ); return 0; error C2512: 'Test' : no appropriate return 0; } default constructor available } 16
10.3 Composition: Objects as Members of Classes 结论 : 成员对象类若有缺省构造函数, 则可不使用初始化列表进行初始化 若无缺省构造函数, 则必须使用初始化列表 如果成员对象没有显式地在成员初始化列表中初始化, 则自动隐含调用其缺省构造函数 (default constructor). 17
1. #include <iostream> 2. using namespace std; 3. class Test{ 4. public: 5. Test( int a ){ num = a; } 6. private: 7. int num; 8. }; 9. class Test2{ 10. public: 11. Test2( const Test &ref, int b ): t(ref) { num = b; } 12. private: 13. Test t; 14. int num; 15. }; 16. int main() 17. { 18. Test ref( 10 ); Test2 test( ref, 20 ); 19. return 0; 20. } 无接收此类型参数的构造函数, 如何正确执行? 缺省拷贝构造函数 18
10.3 Composition: Objects as Members of Classes 成员对象的构造和析构顺序 (Employee 类 manager 对象包含 Date 类的 birthdate 和 hiredate) 成员对象的构造先于宿主对象构造 : birthdate 和 hiredate manager 多个成员对象之间按照类定义中的声明顺序构造 (not in the order they are listed in the constructor s member initializer list) 构造 : birthdate hiredate 成员对象的析构后于宿主对象析构 : manager hiredate birthdate 19
10.3 Composition: Objects as Members of Classes manager hiredate birthdate 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 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 main 栈区 20
10.3 Composition: Objects as Members of Classes 三点修改 : 去掉初始化列表中 birthdate(dateofbirth) 和 hiredate(dateofhire) 什么效果? 自动隐性调用缺省构造函数 去掉 Employee 构造函数中的 Date 引用符号什么效果? 多了两次拷贝构造函数调用 去掉 Date 类 print() 函数的 const 声明? Fig10.11 Employee::print() 函数 26/28 行报错 21
10.1 Introduction Topics 10.2 const (Constant) Objects and const Member Functions 10.3 Composition: Objects as Members of Classes 10.4 friend Functions and friend Classes 10.5 Using the this Pointer 10.6 Dynamic Memory Management with Operators new and delete 10.7 static Class Members 22
10.4 friend Functions and friend Classes 类外部的函数和类, 如何通过对象句柄访问该类的非公有成员? Standalone functions or entire classes may be declared to be friends of another class, and hence has the right to access the nonpublic (and public) members of the class. Friend Function( 友元函数 ) & Friend Class( 友元类 ) 意义 : 提高性能 典型应用 : operator overloading( 运算符重载 ) 23
10.4 friend Functions and friend Classes --- 友元函数 To declare a function as a friend of a class, precede the function prototype in the class definition with keyword friend. class Count { friend void setx( Count &, int ); public: Count() : x(0) // initialize x to 0 { // empty body } Count 授权 setx 为其友元 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 ); counter.print(); return 0; 0 8 24
10.4 friend Functions and friend Classes --- 友元类 To declare 1 all member functions of class ClassTwo as friends of class ClassOne 2 place a declaration of the form friend class ClassTwo; in the definition of class ClassOne. 25
10.4 friend Functions and friend Classes --- 友元类 class ClassOne int main() { { friend class ClassTwo; // friend declaration ClassOne one; int x, y; ClassTwo two; }; two.setx( one, 10 ); ClassOne 授权 ClassTwo 为其友元 two.sety( one, 20 ); class ClassTwo{ two.printclassone( one ); public: void setx(classone &one, int x){ one.x = x; } return 0; void sety(classone &one, int y){ one.y = y; } } void printclassone(classone &one){ cout << "ClassOne.x = " << one.x << ", ClassOne.y = " << one.y << endl; } }; ClassOne.x = 10, ClassOne.y = 20 error C2248: cannot access private member declared in class 'ClassOne' 26
10.1 Introduction Topics 10.2 const (Constant) Objects and const Member Functions 10.3 Composition: Objects as Members of Classes 10.4 friend Functions and friend Classes 10.5 Using the this Pointer 10.6 Dynamic Memory Management with Operators new and delete 10.7 static Class Members 27
10.5 Using the this Pointer Test 类成员函数 void Test::printX( ) { cout<<x; } void Test::printY( ) const test1.printx() test1 int x, y printx 函数如何知道应访问哪个对象的内存空间的 x? test2 int x, y 28
Test 成员函数 void Test::printX( ) 10.5 Using the this Pointer void Test::printY( ) const printx( Test * const this ){ cout << this->x; } printy( const Test * const this ) test1.printx() test1.printx( &test1 ) 特例 : static 成员函数无 this 指针 指针常量, 始终指向当前对象 29
在成员函数中 : 10.5 Using the this Pointer Implicitly 隐性 this 指针调用 x = 10; Explicitly 显式调用 this->x = 10; (*this).x = 10; 30
为何常成员函数 printy 不能调用非常成员函数 printx? void Test::printX( ); 10.5 Using the this Pointer void Test::printY( ) const { printx(); } void Test::printX( Test * const this ); void Test::printY( const Test * const this ) const { printx( this ); } 31
Cascaded Function Calls( 级联函数调用 ) 30. Time& Time::setHour( int h ) // note Time& return 31. { 10.5 Using the this Pointer 32. hour = ( h >= 0 && h < 24 )? h : 0; // validate hour 33. return *this; // enables cascading 34. } // end function sethour 指向 t 的指针常量! 1. Time t; // P335 Fig10.17 2. t.sethour(18).setminute(30).setsecond(22); 3. t.setminute(30).setsecond(22); 4. t.setsecond(22); 32
10.1 Introduction Topics 10.2 const (Constant) Objects and const Member Functions 10.3 Composition: Objects as Members of Classes 10.4 friend Functions and friend Classes 10.5 Using the this Pointer 10.6 Dynamic Memory Management with Operators new and delete 10.7 static Class Members 33
10.6 Dynamic Memory Management with Operators new and delete Motivation char words[1000]; Fixed-size array. 1000? Dynamic memory management 根据需求分配 (allocate)/ 释放 (deallocate) 内存 new / delete operator 34
10.6 Dynamic Memory Management with Operators new and delete use the new operator to dynamically allocate the exact amount of memory required at execution time in the free store (sometimes called the heap 堆 ) return the memory to the free store by using the delete operator to deallocate (i.e., release) the memory, which can then be reused by future new operations memory leak ( 内存泄露 ) 35
10.6 Dynamic Memory Management with Operators new and delete (1) 基本数据类型 1. double *ptr = new double; ( 3.14159 ); 2. cout << *ptr << endl; 3. delete ptr; 初始值 -6.27744e+066 ptr 3.14159 36
10.6 Dynamic Memory Management with Operators new and delete (1) 基本数据类型 1. double *ptr = new double(3.14159); 2. cout << ptr << endl; 3. cout << *ptr << endl; 4. delete ptr; 5. cout << ptr << endl; 6. ptr = 0; 00031090 3.14159 00031090 ptr 00031090 3.14159 00031090 37
10.6 Dynamic Memory Management with Operators new and delete (2) 抽象数据类型 ( 类 ) new 对象 : allocates storage of the proper size for an object calls the constructor to initialize the object returns a pointer of the type specified to the right of the new operator delete 对象 : calls the destructor for the object to which pointer points deallocates the memory associated with the object 38
(2) 抽象数据类型 ( 类 ) 1. class Time{ 2. public: 3. Time(){ cout << "Time constructor called.\n"; } 4. ~Time(){ cout << "Time destructor called.\n"; } 5. }; 6. int main() 7. { 8. Time *timeptr = new Time; 9. delete timeptr; 10. return 0; 11. } 10.6 Dynamic Memory Management with Operators new and delete Time constructor called. Time destructor called. 39
10.6 Dynamic Memory Management with Operators new and delete (2) 抽象数据类型 ( 类 ) 1. class Time2{ 2. public: 3. Time2( int, int, int); 4. ~Time2(); 5. }; 6. int main() 7. { 8. Time2 *timeptr = new Time2( 12, 45, 0 ); 9. delete timeptr; 10. return 0; 11. } 构造函数参数列表 40
10.6 Dynamic Memory Management with Operators new and delete (3) 数组 基本数据类型 1. int size = 10; 2. int *gradesarray = new int[ size ]; 3. delete [ ] gradesarray; 注意与 Fixed size 数组的区别 : Constant integral expression vs Any integral expression 41
10.6 Dynamic Memory Management with Operators new and delete (3) 数组 类 ( 有缺省构造函数 ) 1. Time *timeptr = new Time[5]; 2. delete [ ] timeptr; Time constructor called. Time constructor called. Time constructor called. Time constructor called. Time constructor called. Time destructor called. Time destructor called. Time destructor called. Time destructor called. Time destructor called. 42
10.6 Dynamic Memory Management with Operators new and delete (3) 数组 类对象 ( 有缺省构造函数 ) 1. Time *timeptr = new Time[5]; 2. delete timeptr; Time constructor called. Time constructor called. Time constructor called. Time constructor called. Time constructor called. Time destructor called. 43
10.6 Dynamic Memory Management with Operators new and delete (3) 数组 类对象 ( 无缺省构造函数 ) when allocating an array of objects dynamically, the programmer cannot pass arguments to each object's constructor. 无法调用带参数的构造函数! Instead, each object in the array is initialized by its default constructor. 44
10.1 Introduction Topics 10.2 const (Constant) Objects and const Member Functions 10.3 Composition: Objects as Members of Classes 10.4 friend Functions and friend Classes 10.5 Using the this Pointer 10.6 Dynamic Memory Management with Operators new and delete 10.7 static Class Members 45
10.7 static Class Members 需求 : 活期存款账户 (SavingsAccount 类 ), 统计利息 / 余额 账户名应账户 ( 对象 ) 而异 存款额 年利率 annualinterestrate 特点 : 所有存款账户都应是相同的年利率 静态数据成员 annualinterestrate static 数据成员 static 成员函数 46
10.7 static Class Members 在类定义中, 用 static 关键词修饰数据成员和成员函数的声明 : Static data member, 静态数据成员 又称类变量, 由所有对象共享 和全局量一样是静态存储类别 Static member function, 静态成员函数 可通过类名直接调用, 无 this 指针 47
10.7 static Class Members --- 静态数据成员 在类定义中声明, 在类定义外定义和初始化 ( 特例?) class Employee{ static int count; }; int Employee::count = 0; 只初始化一次, 若没有显式初始化, 则 基本数据类型 : 缺省初始化为 0 抽象数据类型 ( 类对象 ): 默认调用缺省构造函数 48
#include <iostream> using namespace std; class Class1{ public: Class1() { cout << "num1 = " << num1 << endl; } void print() { cout << "num2 = " << num2 << endl; } private: static int num1; const static int num2 = 10; }; int Class1::num1; = 20; class Class2{ public: Class2(){ } static Class1 obj1; }; Class1 Class2::obj1; int main() { cout << "AAA\n"; Class2 obj2; obj2.obj1.print(); return 0; } num1 = 20 AAA num2 = 10 num1 = 0 AAA num2 = 10 error LNK2001: unresolved external symbol "private: static int Class1::num1" 49
#include <iostream> using namespace std; class Class1{ public: Class1(int n) { cout << "num1 = " << num1 << endl; } void print() { cout << "num2 = " << num2 << endl; } private: static int num1; const static int num2 = 10; }; int main() { Class2 obj2; obj2.obj1.print(); return 0; } int Class1::num1 = 20; class Class2{ public: Class2(){ } static Class1 obj1; }; Class1 Class2::obj1; = Class1(30); Class1 Class2::obj1(30); 50
10.7 static Class Members --- 静态数据成员 7.7 Case Study: Class GradeBook Using an Array to Store Grades 数据成员 : 普通, const, static, static const 1 普通数据成员 在类定义中只能声明, 不能初始化 可在构造函数中赋值 2 const 数据成员 在对象整个生存期中都不能改变, 在类定义中只能声明, 不能初始化 Conflict: const 数据必须初始化 必须在构造函数初始化列表中初始化 65 51
10.7 static Class Members --- 静态数据成员 数据成员 : 普通, const, static, static const 3 7.7 Case Study: Class GradeBook Using an Array to Store Grades static 数据成员 意义 : 在类的所有对象之间共享, 称为 class variable( 类变量 ) public 类变量可以直接通过类名 +:: 访问 在类定义中只能声明, 不能初始化 在类外部给出定义和初始化 67 52
10.7 static Class Members --- 静态数据成员 数据成员 : 普通, const, static, static const 4 7.7 Case Study: Class GradeBook Using an Array to Store Grades static const 数据成员 特殊的 static 数据成员, 一般在类定义中只能声明 不能初始化, 须在类外部给出定义和初始化 但如果是整数数据成员 (integral), 则可在类定义中声明并初始化 (ISO C++) (Textbook P258 7.16-18 18) 69 53
10.7 static Class Members --- 静态成员函数 #include <iostream> using namespace std; class Obj { public: Obj( int n ){ num = n; } static void print() { cout << num << endl; } private: int num; }; int main( ) { Obj::print(); return 0; } 仅需在类定义中函数返回类型前, 加 static 可通过对象访问, 也可直接通过类名 +:: 访问 没有 this 指针, 不能访问非静态数据成员, 也不能调用非静态成员函数 error C2597: illegal reference to non-static member 'Obj::num' 54
10.7 static Class Members --- 静态成员函数 程序注意要点 : 静态成员函数和静态成员变量的使用 静态函数只能访问静态数据 静态数据独立于对象, 被所有对象共享 如何动态内存分配生成对象? new/delete 涉及对象的构造和析构 55
10.7 static Class Members --- 静态成员函数 static 数据成员 non-static 数据成员 static 成员函数 / X non-static 成员函数 / / 56
Summary 常量对象和常成员函数 对象的组合 友元函数和友元类 this 指针 动态内存分配 静态类成员 57
Homework 实验必选题目 ( 交实验报告 ): 10.7-10 实验任选题目 ( 不交实验报告 ): 作业题目 (Homework): 58