📄 07章 类与数据抽象(二).txt
字号:
C++大学教程(第7章 类与数据抽象(二)-01)
教学目标
●动态生成与删除对象
●指定const对象与const成员函数
●了解友元函数与友元类的用途
●了解如何使用static数据成员和成员函数
●了解容器类的概念
●了解遍历容器类元素的迭代类概念
●了解this指针的用法
7.1 简介
本章继续介绍类与数据抽象。我们要介绍更高级的课题并为第8章介绍类与运算符重载奠定基础。第6章到第8章的讨论鼓励程序员使用对象,我们称之为基于对象编程(object-based programming,OBP)。然后,第9章和第10章介绍继承与多态,这是真正面向对象编程(object-oriented programming,OOP)的技术。本章和后面几章要使用第5章介绍的C语言式字符串,帮助读者掌握C语言指针的复杂课题.以便在工作中处理近二十年来积累的遗留代码。第19章将介绍新的字符串样式,将字符串作为完全成熟的类对象。这样,读者将熟悉C++中生成和操作字符串的两种最主要方法。
7.2 const(常量)对象与const成员函数
我们一直强调,最低权限原则(principle of least privilege)是良好软件工程的最基本原则之一。下面介绍这个原则如何应用于对象。
有些对象需要修改,有些不需要。程序员可以用关键字const指定对象不能修改,且修改时会产生语法错误。例如:
const Time noon(12,O,O);
声明Time类对象noon为const,并将其初始化为中午12时。
软件工程视点7.1
将对象声明为const有助于实现最低权限原则,这样试图修改就会产生编译时错误不是执行时错误。
软件工程视点7. 2
使用const是正确的类设计、程序设计与编码的关键。
性能提示7.1
声明变量和对象为const不仅是有效的软件工程做法,而且能提高性能,因为如今复杂的优化编译器能对常量进行某些无法对变量进行的优化。
C++编译器不允许任何成员函数调用const对象,除非该成员函数本身也声明为const,即使get成员函数不修改对象时也是这样。声明const的成员函数不能修改对象,因为编译器不允许其修改对象。
函数在原型和定义中指定为const,即在函数参数表和函数定义的左花括号之间插入const关键字。例如,下列类A的成员函数:
int A::getValue() const{ reture privateDataMember);只是返回一个对象的数据成员值,可以声明为const。
常见编程错误7.1
定义修改对象数据成员的const成员函数是个语法错误。
常见编程错误7. 2
定义调用同一类实例的非const成员函数的const成员函数是个语法错误。
常见编程错误7.1
对const对象调用非consc成员函数是个语法借误。
软件工程视点7.3
const成员函数可以用非const版本重载。编译器根据对象是否为const自动选择所用的重载版本。
这里对构造函数和析构函数产生了一个有趣的问题,两者都经常需要修改对象。const对象的构造函数和析构函数不需要const声明。构造函数应允许修改对象,才能正确地将对象初始化。析构函数应能在对象删除之前进行清理工作。
常见编程错误7. 4
将构造函数和析构函数声明为const是个语法错误。
图7.1的程序实例化两个Time对象,一个非const对象和一个const对象。程序想用非const成员函数setHour(第100行)和printStandard(第106行)修改const对象noon。程序还演示了另外三个成员函数调用对象的组合,一个非Const成员函数调用非const对象(第98行)、一个const成员函数调用非const对象(第102行)和一个const成员函数调用const对象(第104与第105行)。输出窗口中显示了一个非const成员函数调用const对象时编译器产生的消息。
编程技巧7. 1
将所有不需要修改当前对象的成员函数声明为const,以便在需要时调用const对象。
1 // Fig. 7.1: time5.h
2 // Declaration of the class Time.
3 // Member functions defined in time5.cpp
4 #ifndef TIME5_H
5 #define TIME5_H
6
7 class Time {
8 public:
9 Time(int = 0,int = 0,int = 0); // default constructor
10
11 // set functions
12 void setTime( int, int, int ); // set time
13 void setHour( int ); // set hour
14 void setMinute( int ); // set minut
15 void setSecond( int ); // set second
16
17 // get functions (normally declared const)
18 int getHour() Const; // return hour
19 int getMinute() const; // return minute
20 int getSecond() const; // return second
21
22 // print functions (normally declared const)
23 void printMilitary() const; // print military time
24 void printStandard(); // print standard time
25 private:
26 int hour; // 0 - 23
27 int minute; // 0 - 59
28 int second; // 0 - 59
29 };
30
31 #endif
32 // Fig. 7.1: time5.cpp
34 #include <iostream.h>
35 #include "time5.h"
37 // Constructor function to initialize private data.
38 // Default values are 0 (see class definition).
39 Time::Time( int hr, int min, int sec )
40 { setTime{ hr, min, sec ); }
42 // Set the values of hour, minute, and second.
43 void Time::setTime( int h, int m, int s )
44 {
45 setHour( h );
46 setMinute( m );
47 setSecond( s );
48 }
49
50 // Set the hour value
51 void Time::setHour( int h )
52 { hour = ( h >= 0 && h < 24 ) ? h : 0; )
53
54 // Set the minute value
55 void Time::setMinute( int m )
56 { minute = ( m >= 0 && m < 60 ) ? m : 0; }
57
58 // Set the second value
59 void Time::setSecond( int s )
60{ second = ( s >= 0 && s < 60 ) ? s : 0; }
61
62 // Get the hour value
63 int Time::getHour() const {return hour;}
64
65 // Get the minute value
66 int Time::getMinute() const { return minute;}
67
68 // Get the second value
69 int Time::getSecond() const { return second;}
7O
71 // Display military format time: HH:MM:
72 void Time::printMilitary() const
73 {
74 cout << ( hour < 10 ? "0": "") << hour <<":"
75 << ( minute < 10 ? "0" : "") << minute; ) << minute;
76 }
77
78 // Display standard format time: HH:MM:SS AM (or PM)
79 void Time::printStandard{)
80 {
81 cout << ( ( hour == 12 ) ? 12 : hour % 12 ) << ":"
82 << ( minute < 10 ? "0" : "" ) << minute << ":"
83 << ( second < 10 ? "0" : "" ) << second
84 << ( hour < 12 ? "AM" : "PM" );
85 }
86 // Fig. 7.1:fig07 01.cpp
87 // Attempting to access a const object with
88 // non-const member functions.
89 #include <iostream.h>
90 #include "time5.h"
91
92 int main()
93 {
94 Time wakeUp{ 6, 45, 0 ); // non-constant object
95 const Time noon( 12, 0, 0 ); // constant object
96
97 // MEMBER FUNCTION OBJECT
98 wakeUp.setHour( 18 ); // non-const non-const
99
100 noon.setHour( 12 ); // non-const const
101
102 wakeUp.getHour(); // const non-const
103
104 noon.getMinute(); // const const
105 noon.p,intMilitary(); // const const
106 noon.printStandard(); // non-const const
107 return 0;
108 }
输出结果:
Compiling Fig07_01.cpp
Fig07_01.cpp(15):error: 'setHour':
cannot convert 'this' pointer from
'const class Time' to 'class Time &'
Conversion loses qualifiers
Fig07_01.cpp(21) : error: 'printStandazd' :
cannot convert 'this' pointer from
'const class Time' to 'class Time &'
Conversion loses qualifiers
注意,尽管构造函数应为非const成员函数,但仍然可以对const对象调用构造函数。Time构造函数的定义在第39行和第40行 Tlme:Time( int hr, int min,int sec )
{ setTime(hr, min, sec); }
其中Time构造函数调用另一个非const成员函数setTime进行Time对象的初始化。在const对象的构造函数调用中调用非const成员函数时是合法的。
软件工程视点7.4
const对象不能用赋值语句修改,因此应初始化。类的数据成员声明为const时.要用成员初始化值向构造函数提供类对象数据成员的初始值。
另外注意第106行(源文件中第21行):
noon.printStandard();// noon—const const
尽管Time类的成员函数printStandard不修改所调用的对象,但仍然产生一个编译错误。
图7.2演示用成员初始化值初始化Increment类的const数据成员increment。Increment的构造函数修改后如下所示:
Increment::Increment( int c,int i )
:increment( i )
{ count = c;}
符号:increment(i)将increment初始化为数值i。如果需要多个成员初始化值,则可以将其放在冒号后面以逗号分隔的列表中。所有数据成员都可以用成员初始化值的语法进行初始化,但const和引用必须用这种方式进行初始化。本章稍后会介绍,成员对象也要用这种方法进行初始化。第9章学习继承时,会介绍派生类的基类部分也要用这种方法进行初始化。
测试与调试提示7. 1
如果成员函数修改对象,则将其声明为const,这样可以减少许多错误。
1 // Fig. 7.2:fig07 02.cpp
2 // Using a member initializer to initialize a
3 // constant of a built-in data type.
4
5 #include <iostream.h>
6
7 class Increment {
8 public:
9 Increment( int c = 0, int i = 1 );
10 void addIncrement() { count += increment; }
11 void print() const;
12
13 private:
14 int count;
15 const int increment;
16 };
17
18 // Constructor for class Increment
19 Increment::Increment( int c, int i )
20 : increment( i ) // initializer for const member
21 {count = c;}
22
23 // Print the data
24 void Increment::print() const
25 {
26 cout << "count = "<< count
27 << ", increment = "<< increment << endl;
28 }
30 int main()
31 {
32 Increment value( 10, 5 );
33
34 cout << "Before incrementing: ";
35 value.print();
36
37 for ( int j = 0; j < 3; j++ ) {
28 value.addIncrement();
39 cout << "After increment "<< j << ": ";
40 value.print();
41 }
42
43 return 0;
44
输出结果:
Before incrementing: count = 10, increment = 5
After increment 1: count = 15, increment = 5
After increment 2: count = 20, increment = 5
After increment 3: count = 30,increment = 5
图7.2 用成员初始化值初始化内部数据类型的常量
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -