C++ Programming Lecture 15 Wei Liu (刘威) Dept. of Electronics and Information Eng. Huazhong University of Science and Technology Feb. 2014 The separation of Interfaces and implementations Interfaces (class definitions) in header file, e.g., myClass.h Implementation (member function definitions) in new source file, e.g., myClass.cpp Single source file e.g., try.cpp New source file e.g., myTry.cpp -2- 面向对象的编程 vs. 面向过程的编程 面向对象的编程 面向过程的编程 类的设计 类的调用: 类的实例化获得对象 对象可以维持自身数 据,同时还有方法 函数的设计 函数的调用: 主调函数需要 维持数据 -3- Lecture 15 Chapter 17. Classes: A Deeper Look, I 17.1 Introduction 17.2 Time Class Case Study 17.3 Class Scope and Accessing Class Members 17.4 Separating Interface from Implementation 17.5 Access Functions and Utility Functions 17.6 Time Class Case Study: Constructors with Default Arguments 17.7 Destructors 17.8 When Constructors and Destructors Are Called 17.9 Time Class Case Study: A Subtle Trap Returning a Reference to a private Data Member 17.10 Default Memberwise Assignment -4- 17.3 Class Scope and Accessing Class Members Class Scope 类的作用域 A class’s data members and member functions belong to that class’s scope. 类的数据成员与成员函数属于该类的作 用域 Within a class’s scope, class members are immediately accessible by all of that class’s member functions and can be referenced by name. 类的作用域内,各种成员可以通过 名称直接访问 Outside a class’s scope, public class members are referenced through one of the handles on an object—an object name, a reference to an object or a pointer to an object. 类的作用域外,只有public的成员可以通过某个对象 (形式可以是名称、引用或者指针)来访问 17.3 Class Scope and Accessing Class Members Scopes 作用域 Global namespace scope 全局名称空间作用域 Class scope 类的作用域 Function scope 函数的作用域 Relationships among scopes作用域的关系 Nonmember functions are defined at global namespace scope. 其他的函数属于全局名称空间的作用域 The same name function-scope variable in member function will hide the class-scope variable 类似于局部变量与全局变量的关系, 在成员函数内定义的同名变量拥有比同名数据成员更高的可见性 We can visit the class-scope variable with the class name followed by the scope resolution operator (::) 可以通过类的作用 域解析符显式访问类的数据成员 17.3 Class Scope and Accessing Class Members Accessing Class Members 访问类的成员 The dot member selection operator (.) is preceded by an object’s name or with a reference to an object to access the object’s members. 逗号成员选择符在某个对象的名称或者引用之后,可以 访问该对象的各种成员 The arrow member selection operator (->) is preceded by a pointer to an object to access the object’s members. 箭头成员选 择符在某个对象的指针之后,可以访问该对象的各种成员 Time a, *b; a.printUniversal(); b->printUniversal(); 创建类的指针、引用的语法与创建基本数据 类型的指针、引用的语法类似。 8 Lecture 15 Chapter 17. Classes: A Deeper Look, I 17.1 Introduction 17.2 Time Class Case Study 17.3 Class Scope and Accessing Class Members 17.4 Separating Interface from Implementation 17.5 Access Functions and Utility Functions 17.6 Time Class Case Study: Constructors with Default Arguments 17.7 Destructors 17.8 When Constructors and Destructors Are Called 17.9 Time Class Case Study: A Subtle Trap Returning a Reference to a private Data Member 17.10 Default Memberwise Assignment -9- 17.4 Separating Interface from Implementation 接口与实现的分离 Separating classes into two files a header file for the class definition (i.e., the class’s interface) 头文件 a source code file for the class’s member-function definitions (i.e., the class’s implementation) 源文件 Benefits this encourages ISVs(independent software vendors), they only provide the header files(readable text file) and the object modules(un-readable binary files) 便于软件厂商发布 他们的软件产品 Information only internally used in the class will not appear in header files, following the principle of least privilege 体现 了软件设计的最小权限原则 Separating Interface from Implementation 接口与实现的分离 Interfaces (class definitions) in header file, e.g., myClass.h Implementation (member function definitions in new source file, e.g., myClass.cpp source file e.g., myTry.cpp -11- Lecture 15 Chapter 17. Classes: A Deeper Look, I 17.1 Introduction 17.2 Time Class Case Study 17.3 Class Scope and Accessing Class Members 17.4 Separating Interface from Implementation 17.5 Access Functions and Utility Functions 17.6 Time Class Case Study: Constructors with Default Arguments 17.7 Destructors 17.8 When Constructors and Destructors Are Called 17.9 Time Class Case Study: A Subtle Trap Returning a Reference to a private Data Member 17.10 Default Memberwise Assignment -12- 17.5 Access Functions and Utility Functions Member functions 成员函数的作用有几种 Access function 访问函数 Access functions can read or display data. 通常是public 访问权限,提供对类的数据成员的访问,常见的例子有测 试条件是真是假,例如Time类里面的isAM()、isPM()等 Utility function 工具函数 A utility function is a private member function that supports the operation of the class’s public member functions. 通常是private访问权限,是public函数的辅助函 数,例如SalesPerson类里面的totalAnnualSales()函数 Chapter 15 Topic 3: What is inside the Class: Creation and Destroy 17.6 Time Class Case Study: Constructors with Default Arguments How default arguments can be used in a constructor. 17.7 Destructors Destructors that perform “termination housekeeping” on objects before they are destroyed. 17.8 When Constructors and Destructors Are Called The order in which constructors and destructors are called. 17.10 Default Memberwise Assignment To assign the data members of one object to those of another by default memberwise assignment 17.6 Constructors with Default Arguments Constructors with default argument 默认实参的 构造函数 Even if no values are provided in a constructor call, the constructor still initializes the data members 默认实参可以 帮助构造函数在无输入时初始化数据成员 A constructor that defaults all its arguments is also a default constructor—i.e., a constructor that can be invoked with no arguments. 对所有实参都有默认值的时候,该构造 函数即为默认构造函数,这种函数一个类只能有一个 15 在类的定义中,构造函数的所有参数都具有 默认实参,即为默认构造函数 构造函数中设置数据成员的工作通过调用 setXXX函数完成,减少重复代码、提高程序 的可靠性 构造函数可以调用其他工具函数,但是需要 注意数据成员的初始化问题 -17- Chapter 15 Topic 3: What is inside the Class: Creation and Destroy 17.6 Time Class Case Study: Constructors with Default Arguments How default arguments can be used in a constructor. 17.7 Destructors Destructors that perform “termination housekeeping” on objects before they are destroyed. 17.8 When Constructors and Destructors Are Called The order in which constructors and destructors are called. 17.10 Default Memberwise Assignment To assign the data members of one object to those of another by default memberwise assignment 17.7 Destructors Destructor析构函数 The name of the destructor for a class is the tilde character (~) followed by the class name. 函数名即为 类名加上~ Called implicitly when an object is destroyed. 在对象 销毁时被隐式调用 Receives no parameters and returns no value.析构函 数无形式参数、无返回值 A class may have only one destructor. 一个类只能有 一个析构函数 A destructor must be public. 析构函数必须是public的 19 17.7 Destructors 析构函数 Destructor The destructor itself does not actually release the object’s memory—it performs termination housekeeping before the object’s memory is reclaimed. 析构函数不能释放对象的内存,但是可以对 对象的各种数据成员进行清理 If you do not explicitly provide a destructor, the compiler creates an “empty” destructor. 如果用户没有 提供析构函数,编译器将自动创建一个空的析构函数 20 17.8 When Constructors and Destructors Are Called Constructors and destructors are called implicitly. 构造函 数和析构函数都是被隐式调用的 The order in which these function calls occur depends on the order in which execution enters and leaves the scopes where the objects are instantiated. The storage classes of objects can alter the order in which destructors are called.其被调用的顺序与对象的作用域、 存储类型、中止方式都有关 17.8 When Constructors and Destructors Are Called Issues on scope Objects in global scope 具有全局作用域的对象 Constructors are called before any other function (including main) in that file begins execution. The corresponding destructors are called when main terminates. 构造函数在main()函数之前调用,析构函数 在main函数结束时调用 Objects in local scope 具有本地作用域的对象 Constructors and destructors for automatic objects are called each time execution enters and leaves the scope of the object. 构造函数和析构函数在进入和离开 该对象的作用域时调用 22 17.8 When Constructors and Destructors Are Called Issues on storage type Static object 静态对象 The constructor is called only once, when execution first reaches the point where the object is defined— the corresponding destructor is called when main terminates or the program calls function exit. 构造函 数在碰到该对象时被调用一次,析构函数在main()结 束时调用 Global and static objects are destroyed in the reverse order of their creation. 全局以及静态对象的 析构函数调用次序与构造函数相反 23 17.8 When Constructors and Destructors Are Called Issues on termination method exit() Function exit (keyword) forces a program to terminate immediately and does not execute the destructors of automatic objects. Exit 函数强迫函数 中止,不会调用自动对象析构函数 abort() Function abort (keyword) performs similarly to function exit without allowing the destructors of any objects to be called. Abort 函数亦强迫函数中止,且 不允许调用任何对象的析构函数 24 25 -26- Global object Static objects -27- Experiment 改进日期类,观察对象的创建与销毁 date.h main.cpp date.cpp -28- Chapter 15 Topic 3: What is inside the Class: Creation and Destroy 17.6 Time Class Case Study: Constructors with Default Arguments How default arguments can be used in a constructor. 17.7 Destructors Destructors that perform “termination housekeeping” on objects before they are destroyed. 17.8 When Constructors and Destructors Are Called The order in which constructors and destructors are called. 17.10 Default Memberwise Assignment To assign the data members of one object to those of another by default memberwise assignment 17.10 Default Memberwise Assignment Memberwise assignment 逐个成员赋值 The assignment operator (=) can be used to assign an object to another object of the same type. 同类型的对象可以通过赋值表达 式进行复制 Each data member of the object on the right of the assignment operator is assigned individually to the same data member in the object on the left of the assignment operator. 此时对象的每个数 据成员将被赋值给另外一个对象的数据成员(如果数据成员是指 针类型,则存在一定风险) Objects may be passed as function arguments and may be returned from functions using pass-by-value by default. C++ creates a new object and uses a copy constructor to copy the original object’s values into the new object. 对象作为函数形参或 者返回值时,C++会启用复制构造函数来实现逐个成员的赋值 30 同类型的对象可以通过赋值表达式进行复制, 此时对象的每个数据成员将被赋值给另外一个 对象的数据成员 31 Chapter 18. Classes: A Deeper Look, II 18.2 const (Constant) Objects and const Member Functions 18.6 static Class Members 18.3 Composition: Objects as Members of Classes 18.4 friend Functions and friend Classes 18.7 Data Abstraction and Information Hiding 18.5 Using the this Pointer -32- 18.2 const (Constant) Objects and const Member Functions const object 声明一个对象为常量,本质上与声明常量变量没有区别 const Time noon(12,0,0); const member function 声明一个类的成员函数为常量成员函数,即约定该函数 不能修改类的数据成员 prototype 声明 void display(int) const ; definition 定义 void display(int a) const { cout << “testing ” << a << endl; } -33- A member function is specified as const both in its prototype and in its definition. A member function is specified as const both in its prototype and in its definition. const member function 常量成员函数 声明一个类的成员函数为常量成员函数,即约定该函数 不能修改类的数据成员 prototype 声明 void display(int) const ; const data members 常量数据成员 声明一个类的数据成员为常量,即约定该数据成员运行 期间不允许被修改 这种数据成员的初始化必须通过“成员初始化器” (member initializers)来实现 const int increment; -37- member initializer member initializer for const data member 常量数据成员的初始化器 Increment::Increment(int c, int i) : count( c ), increment( I ) { ... } 在构造函数参数列表之后、构造函数体之前 以:开头,以类似函数的形式书写,其中每个数据成员 的名称为“函数名”,所需要初始化的值为“函数的参数” -38- Member initializers appear between a constructor’s parameter list and the left brace that begins the constructor’s body. 初始化列表 构造函数+初始化列表 vs. 构造函数内赋值 初始化列表在构造体之前执行 对内置类型的数据成员没有什么大的区别 对用户自定义类型的数据成员,推荐使用类构造函 数初始化列表 下列必须用带有初始化列表的构造函数: 成员类型是没有默认构造函数的类。若类没有默认 构造函数,则编译器尝试使用默认构造函数将会失 败。 const成员或引用类型的成员。因为const对象或引 用类型只能初始化,不能对他们赋值。 -41- Chapter 18. Classes: A Deeper Look, II 18.2 const (Constant) Objects and const Member Functions 18.6 static Class Members 18.3 Composition: Objects as Members of Classes 18.4 friend Functions and friend Classes 18.7 Data Abstraction and Information Hiding 18.5 Using the this Pointer -42- 18.6 static Class Members A static data member 静态数据成员 In certain cases, only one copy of a variable should be shared by all objects of a class, such a variable represents “class-wide” information. 静态数据成员是 在类的所有对象之间所共享的一个变量的唯一副本 A class’s static members exist even when no objects of that class exist. 静态的数据成员不依赖于任 何对象的创建,在程序开始运行后、没有对象被创建时 就已经存在 To access a public static class member when no objects of the class exist, prefix the class name and the binary scope resolution operator (::) to the name of the data member. 为了访问类的静态数据成员, 需要采用类的作用域声明符:: 18.6 static Class Members A static data member 静态数据成员的初始化 A fundamental-type static data member is initialized by default to 0. 缺省初始化为0 If you want a different initial value, a static data member can be initialized once. A static const data member of int or enum type can be initialized in its declaration in the class definition. 整数 类型的静态数据成员可以在类的定义中初始化 All other static data members must be defined at global namespace scope and can be initialized only in those definitions. 其它静态数据成员需要在全局作用域内初始 化 If a static data member is an object of a class that provides a default constructor, the static data member need not be initialized because its default constructor will be called. 18.6 static Class Members A static member function 静态的成员函数 To access a private or protected static class member when no objects of the class exist, provide a public static member function and call the function by prefixing its name with the class name and binary scope resolution operator. 为了访问静态的数据成员,需要通过公共的静态成员函 数实现 A static member function is a service of the class, not of a specific object of the class. 静态的成员函数属于整个类,而非某个具体的对象 A member function should be declared static if it does not access non-static data members or nonstatic member functions of the class. Experiment 观察常量成员/静态成员/静态常量数据成员的区别 class Count { private: int a_; const int b_; static int c_; static const int d_ = 4; public: Count(int a = 1, int b = 2): a_(a), b_(b) { } int Count::c_ = 3; void create( void ); int main() { Count countA; countA.display(); countA.update(); countA.display(); void display ( void ) { cout << a_ << "," << b_ << "," << c_ << "," << d_ << endl; } Count countB( 11, 22 ); countB.display(); countB.update(); countB.display(); return 0; void update ( void ) { a_ += 100; c_ += 100; } }; } -48- Chapter 18. Classes: A Deeper Look, II 18.2 const (Constant) Objects and const Member Functions 18.6 static Class Members 18.3 Composition: Objects as Members of Classes 18.4 friend Functions and friend Classes 18.7 Data Abstraction and Information Hiding 18.5 Using the this Pointer -49- 18.4 friend Functions and friend Classes friend function 友元函数 defined outside that class’s scope, yet has the right to access the non-public (and public) members of the class. 定 义在类的范围之外的,可以访问类的非公共数据的函数 Standalone functions, entire classes or member functions of other classes may be declared to be friends of another class. 可以声明为友元的包括函数、类、类的成员函数等 The friendship relation is neither symmetric nor transitive. 友元的关系不是对称的,也不能传递 Friendship is granted, not taken. 友元的关系是以在类的 声明中授权方式进行的,函数不能自动获得 Using friend functions can enhance performance.因为减 少了数据的传递,友元函数的执行效率要高一些 Friendship: enables a class designer to specify nonmember functions that can access a class’s non-public members Chapter 18. Classes: A Deeper Look, II 18.2 const (Constant) Objects and const Member Functions 18.6 static Class Members 18.3 Composition: Objects as Members of Classes 18.4 friend Functions and friend Classes 18.7 Data Abstraction and Information Hiding 18.5 Using the this Pointer -52- Data Abstraction and Information Hiding Data Abstraction 数据抽象 C/C++内置数据类型:int, double, … 用户自定义的数据类型:class 其实都是抽象数据类型 abstract data type, ADT, 是计 算机对现实数据的模拟 ADT的两个方面:数据表示、数据操作 Information hiding 信息隐藏 类对于用户隐藏实现的细节 类的私有数据不对外开放 友元函数是否破坏了信息隐藏? 类的声明控制了友元的权限 友元和类方法是表达类接口的两种不同的机制 -53- 18.5 Using the this Pointer this 指针 this 指针是C++提供的用于访问对象自己的地址 的特殊指针,也是C++的关键词之一 Every object has access to its own address through a pointer called this. The this pointer is not part of the object itself. 每个对象都内置了一个访问自己地 址的this 指针,this指针不是对象的数据成员,而是 由系统提供的 Objects use the this pointer implicitly or explicitly to reference their data members and member functions. 对象可以通过this指针显式或者隐式的访问自己的 数据成员与成员函数 18.5 Using the this Pointer (cont.) Another use of the this pointer is to enable cascaded member-function calls this 指针的重要用途之一是实现级联式的函数调 用 invoking multiple functions in the same statement The dot operator (.) associates from left to right, so first evaluates t.setHour(18), then returns a reference to object t as the value of this function call. The remaining expression is then interpreted as t.setMinute( 30 ).setSecond( 22 ); The t.setMinute( 30 ) call executes and returns a reference to the object t. The remaining expression is interpreted as t.setSecond( 22 ); 57 Returning *pointer to get the reference 获得当前对象的引用 Experiment 运行Time类,观察this指针的使用 作业 18.6 修改Date类 18.9 修改Time类 19.8 实现复数Complex类 -60- 谢谢! 刘威 副教授 互联网技术与工程研究中心 华中科技大学电子与信息工程系 电话:13986224922 Email: liuwei@hust.edu.cn 网址:http://itec.hust.edu.cn