📄 07章 类与数据抽象(二).txt
字号:
159 cout << endl;
160 return O;
161 }
输出结果:
Date object constructor for date 7/24/1949
Date object Constructor for date 3/12/1988
Employee object constructor Bob Jones
Jones, Bob
Hired: 3/12/1988 Birth date 7/24/1949
Test Date constructor with invalid values:
Month 14 invalid. Set to month 1.
Day 35 invalid. Set to day 1.
Date object constructor for date 1/1/1994
Date object destructor for date 1/1/1994
Employee object destructor: Jones, Bob
Date object destructor for date 3/12/1988
Date object destructor for date 7/24/1949
图7.4使用成员对象的初始化值
记住,const成员和引用也在成员初始化值列表中初始化(第9章会讲到,派生类的基类部分也是这样初始化的)。Date类和Employee类各有一个析构函数,分别在删除Date和EmpLoyee对象时打印一个消息。这样就可以从程序输出中确认对象由内向外建立,由外向内删除(即先删除Employee对象,再删除其中包含的Date对象)。
成员对象不需要通过成员初始化值显式初始化。如果不提供成员初始化值,则隐式调用成员对象的默认构造函数。默认构造函数建立的值(若有)可以用set函数重定义。
常见编程错误7.6
没有为成员对象提供初始化值的情况下,也没有为成员对象提供默认的构造函数,这样就会产生语法错误。
性能提示7.2
通过成员初始化值显式初始化成员对象,这样可以消除两次初始化成员对象的开销,一次是在调用成员对象的默认构造函数时,一次是在用set函数初始化成员对象时。
软件工程视点7. 8
如果一个类用其他类对象作为成员,则将这个成员对象定为public不分破坏该成员对象private成员的封装与隐藏。
注意第43行调用Date成员函数print。C++中许多类的成员函数不需要参数。这是因为每个成员函数包含所操作对象的隐式句柄(指针形式)。7.5节将介绍隐式指针this。
在第一版的Employee类中(为了便于编程),我们用两个25字符数组表示EmpLoyee的姓和名。这些数组如果存储短名称可能浪费内存空间(记往每个数组中有一个字符是字符串的null终止符 '\0',),超过24个字符的姓名要截尾之后才能放得下。本章稍后将介绍另一种形式的Employee类,动态生成适合姓和名的数组长度,还可以用两个string对象表示姓名。第19章详细介绍string标准库类。
7.4 友元函数与友元类
类的友元函数(friendfunetlon)在类范围之外定义,但有权访问类的private(和第9章“继承”介绍的受保护)成员。函数或整个类都可以声明为另一个类的友元。
利用友元函数能提高性能,这里将介绍一个友元函数的例子。本书后面要用友元函数通过类对象和生成迭代类来重载运算符。迭代类对象用于连续选择项目或对容器类(见7.9节)对象中的项目进行操作。容器类对象能够存放项目。成员函数无法进行某些操作时可以使用友元函数(见第8章“运算符重载”)。
要将函数声明为类的友元,就要在类定义中的函数原型前面加上friend关键字。要将类ClassTwo声明为类ClassOne的友元,在类ClassOne的定义中声明如下:
friend class classTwo;
软件工程视点7. 9
尽管类定义中有友元函数的原型,但友元仍然不是成员函数。
软件工程视点7.10
private、protected和public的成员访问符号与友元关系的声明无关,因此友元关系声明可以放在类定义中的任何地方。
编程技巧7.2
将类中所有友元关系的声明放在类的首部之后,不要在其前面加上任何成员访问说明符。
友元关系是”给予”的,而不是”索取”的,即要让B成为A的友元,A要显式声明B为自己的友元。此外,友元关系既不对称也不能传递,例如.如果A是D的友元,B是C的友元,并不能说B就是A的友元(不对称)、c就是B的友元或A就是C的友元(不传递)。
软件工程视点7. 11
OOP组织中的有些人认为友元关系会破坏信息隐藏和降低面向对象设计方法的价值。
图7.5演示了声明与使用友元函数setX来设置Count类的private数据成员x。注意,类声明中(习惯上)首先是友元声明,放在public成员函数的声明之前。图7.6的程序演示了调用非友元函数cannotSetX修改private数据成员x时编译器产生的消息。图7.5和7. 6介绍了使用友元函数的“结构”,今后各章会介绍使用友元函数的实际例子。
1 // Fig. 7.5:fig0705.cpp
2 // Friends can access private members of a class.
3 #include <iostream.h>
4
5 // Modified Count class
6 class Count {
7 friend void setX( Count &,int ); // friend declaration
8 public:
9 Count() { x = 0; } // constructor
10 void print()const {cout << x << endl; } // output
11 private:
12 int x; // data member
13 };
14
15 // Can modify private data of Count because
16 // setX is declared as a friend function of Count
17 void setX( Count &c, int val )
18 {
19 c.x = val; // legal: setX is a friend of Count
2O }
21
22 int main()
23 {
24 Count counter;
25
26 cout << "counter.x after instantiation: ";
27 counter.print();
28 cout << "counter.x after call to setx friend function: ";
29 setX( counter, 8 ); // set x with a friend
30 counter.print();
31 return 0;
32 }
输出结果:
counter.x after intantiation: 0
counter.x after call to setX friend function: 8
图7.5 友元可以访问类的private成员
注意第17行中函数setX是C语言式的独立函数,而不是Count类的成员函数,为此,对counter对象调用setX时,我们用第29行的语句:
setX(counter,8 ); // set x with a friend
来提取counter参数而不是用句柄(如对象名)调用如下函数:
counter.setX(s);
软件工程视点7.12
由于C++是个混合语言,经常在一个程序中并行采用两种函数调用,类C语言的调用将基本数据或对象传递给函数,而C++调用将函数(或消息)传递给对象。
1 // Fig. 7.6: fig07_O6.cpp
2 // Non-friend/non-member functions cannot access
3 // private data of a class.
4 #include <iostream.h>
5
6 // Modified Count class
7 class Count {
8 public:
9 Count() { x = 0; } // constructor
10 void print() const { cout << x << endl; } // output
11 private:
12 int x; // data member
13 };
14
15 // Function tries to modify private data of Count,
16 // but cannot because it is not a friend of Count.
17 void cannotSetX( Count &c, int val )
16 {
19 c.x = val; // ERROR: 'Count::x' is not accessible
20 }
21
22 int main()
23 {
24 Count counter;
25
26 cannotSetX( counter, 3 ); // cannotSetX is not a friend
27 return 0;
28 }
输出结果:
Compiling...
Fig07 06.cpp
Fig07_06.cpp(19) : error: 'x' :
Cannot access private member declared in class 'Count'
图7.6 非友元/非成员函数不能访问类的Private成员
可以指定重载函数为类的友元。每个重载函数如果要作为友元,就要在类定义中显式声明为类的友元。
7.5 使用this指针
每个对象都可以通过this指针访问自己的地址。对象的this指针不是对象本身的一部分,即this指针不在对该对象进行sizeof操作的结果中体现。但this指针在每次非static成员函数调用对象时(static成员见7.7节介绍)作为第一个隐式参数传递给对象(通过编译器)。
this指针隐式引用对象的数据成员和成员函数(当然也可以显式使用)。this指针的类型取决于对象类型和使用this的成员函数是否声明为const。在Employee类的非常量成虽函数中,this指针的类型为Employee *const(Employee对象的常量指针)。在Employee类的常量成员函数中,this指针的类型为const Employee *const(为常量Employee对象的常量指针)。
下面介绍一个显式使用this指针的简单例子,本章稍后和第8章将介绍一些使用this的复杂例子。每个非static成员函数都能访问所调用成员所在对象的this指针。
性能提示7.3
为了节约存储空间,每个类的每个成员函数只有一个副本,该类的每个对象都可调用这个成员函数。另一方面每个对象又有自己的类数据成员副本。
图7.7演示了显式使用this指针,从而使Test类的成员函数打印Test对象的private数据x。
1 // Fig. 7.7: fig07_07.cpp
2 // Using the this pointer to refer to object members,
3 #include <iostream.h>
4
5 class Test {
6 public:
7 Test( int = 0 }; // default constructor
8 void print() const;
9 private:
10 int x;
11 };
12
13 Test::Test( int a ) { x = a; } // constructor
14
15 void Test::print() Const // () around *this required
16 {
17 cout <<" x =" << x
18 << "\n this->x "<< this->x
19 << "\n(* this).x =" << ( *this ).x << endl;
2O }
21
22 int main()
23 {
24 Test testObject( 12 );
25
26 testObject.print();
27
28 return 0;
29 }
输出结果:
x = 12
this-> = 12
(*this).x = 12
图 7. 7使用this指针
作为演示,图7.7中的print成员函数首先直接打印x。然后print用两个不同符号通过this指针访问x,一个是this指针和箭头运算符(->),一个是this指针和圆点运算符(.)。
注意*this和圆点(成员选择)运算符一起使用时要用括号括起来。这个括号是必需的,因为圆点运算符的优先级高于*运算符。如果没有括号,则下列表达式:
*this.x
求值为:
*( this.x)
这是个语法错误,因为圆点运算符不能和指针一起使用。
常见编程错误7.7
将对象指针和成员选择运算符(.)一起使用是个语法错误,因为成员选择运算符和对象或对该对象的引用一起使用。
this指针的一个有趣用法是防止对象赋值给自己。第8章“运算符重载”中将会介绍,自我赋值可能在对象包含动态分配内存的指针时导致严重的错误。
this指针的另一用法是允许连续使用成员函数调用。图7.8演示了返回Time对象的引用,使Time类的成员函数调用可以连续使用。成员函数setTime、setHour、setMinute和setSecond都可以返回Time&返回类型的*this。
1 // Fig. 7.8: time6.h
2 // Cascading member function calls.
3
4 // Declaration of class Time.
5 // Member functions defined in time6.cpp
6 #ifndef TIME6_H
7 #define TIME6_H
8
9 class Time {
10 public:
11 Time( int = 0, int= 0, int = 0 ); // default constructor
12
13 // set functions
14 Time &setTime( int, int, int ); // set hour, minute, second
15 Time &setHour( int ); // set hour
16 Time &setMinute( int ); // set minute
17 Time &setSecond( int ); // set second
18
19 // get functions (normally declared const)
20 int getHour() const; // return hour
21 int getMinute() const; // return minute
22 int getseeend() const; // return second
23
24 // print functions (normally declared const}
25 void printMilitary() const; // print military time
26 void printstandard() const; // print standard time
27 private:
28 int hour; // O - 23
29 int minute; // O - 59
30 int second; // O - 58
31 };
33 #endif
34 // Fig. 7.8: time.cpp
36 #include "time6.h"
37 #include <iostream.h>
38
39 // Constructor function to initialize private data.
40 // Calls member function setTime to set variables.
41 // Default values are 0(see class definiation).
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -