第三章
类(class)及其对象(object)的封装(encapsulation)
——数据及其操作的统一管理
3.1
封装性
封装可以解决数据与函数代码之间的相容性和数据的保护问题。先看C语言中不使用封装的例子。
[例1]不用封装的例子
// no_encapsul.cpp
// The case of no capsulation
#include
int sq_int(int a)
{
return a*a;
}
double sq_dbl(double a)
{
return a*a;
}
void main()
{
int j=5;
double d=2.5;
j = sq_int(j);
d = sq_dbl(d);
cout << j << endl;
cout << d << endl;
}
/* Results:
25
6.25
*/
此程序比较简练(当然还可以再简练些),但它改变了j和d的值。如果希望在不改变这两个值的情况下求出它们的平方值,则可以将主程序修改如下,增加两个变量:
void main()
{
int j=5, k;
double d=2.5, f;
k = sq_int(j);
f = sq_dbl(d);
cout << k << endl;
cout << f << endl;
}
以上程序中,虽然两个变量j和d没有改变,但其它函数仍有可能修改这两个变量。如何保证需要修改时才修改,不准修改时就不修改。这要依靠封装。
[例2]使用封装以便保护数据
// encapsul.cpp
// The case of using capsulation to protect datum 'i'(不是data)
#include
class integ
//用户建立一个类,用于封装
{
int j;
public:
integ( ) { j = 6; }
//构造函数
int sq() { return j*j; }
//求平方函数
void inc() { j++; }
//求增量函数
int read() { return j; }
//读数据函数
};
void main()
{
int j;
integ obj;
//建立对象并初始化
cout << obj.read() << endl;
//读数据
j = obj.sq( );
//求平方
cout << j << endl;
//读平方值
obj.inc( );
//求增量
cout << obj.read() << endl;
//读增量值
}
/* Results:
6
36
7
*/
从以上程序看出,不允许外部函数(如主函数main( ))等直接访问(读取或修改)class integ中的数据成员j,而必须通过访问类class integ的成员函数read( ),才能读取j。如需求其平方值,则须调用成员函数sq( ),但这些函数仍然无法改变数据j。如欲将数据加一,则须调用成员函数inc( )。
封装是指将对象的属性(attribute)(或称数据内容)和作用于这些属性上的操作(operation)(或称函数、行为或服务)集合包装在一起,组成为一个实体。
封装具有以下特点:
1. 用户无法访问所有私有数据和函数细节(即如何提供服务的细节,也即函数定义)。
2. 用户只知道函数接口部分所提供的操作功能,根据这些操作功能来调用函数,从而访问私有数据。
3.2
类(class)及其对象(object)
3.2.1
C++类及其构成
在第一章§1.2.1“编程语言特征”中曾提到:客观世界的各事物可按其共性分为各个类(称为基类),这些类是具有相同共性的各事物的集合,它是面向对象语言中相对独立的程序单位,是这些事物的统一抽象。
在面向对象程序设计语言中,“类”是一组具有相同数据结构和相同操作(方法、函数)的集合,是对一系列具有相同性质的对象的抽象,它描述的不是个别对象而是全体对象的共同特征。
C++中,“类”被表达为一个具有特定功能的程序块。一个类具有两个成员部分:数据成员(data member)和成员函数(member functions)。其数据成员可以是各种预定义类型和用户自定义类型的数据。本章§3.1“封装性”的[例2]中已介绍过一个类class integ。可以看出,它的首部是关键词class加上类名,类的全部内容(称为类体(class body),包括数据和函数)都包含于花括号内,最后以分号结束。
由于用户能够在需要时生成新的类的类型(详见第四章),因此C++是可扩展语言(extensible language)。
3.2.2
成员函数的说明和定义
类的成员函数可被大量调用。第二章中提到:调用函数的时间开销很大,那些频繁调用的函数可定义为宏。此时可以将那些函数定义为内联函数(in-line function,或称内置函数),使其具有宏的特性,从而提高程序的运行效率。
有两种方式定义内联函数:
1. 隐式(implicit)定义:
将整个函数定义全放置于类体内,该函数即成为内联函数。如以上提到的:
class integ
//用户建立一个类,用于封装
{
int j;
public:
integ( ) { j = 6; }
//构造函数
int sq() { return j*j; }
//求平方函数
void inc() { j++; }
//求增量函数
int read() { return j; }
//读数据函数
};
此类中的三个成员函数sq( )、inc( )和read( )的定义部分全被放置于类体内,因此它们都是内联函数。
2. 显式(explicit)定义:
当内联函数的定义部分很长时,将使类体变得很长,影响可读性。此时可只在类体内放置内联函数的说明部分,而将内联函数的定义部分放置于类体之外。如:
class integ
{
int j;
public:
integ( ) { j = 6; }
//构造函数
int sq();
void inc();
int read();
};
inline int sq() { return j*j; }
//求平方函数
inline void inc() { j++; }
//求增量函数
inline int read() { return j; }
//读数据函数
以上两种定义形式的效果是相同的。但第二种形式便于将类的接口部分与实现部分进行分离,所以更为
规范
编程规范下载gsp规范下载钢格栅规范下载警徽规范下载建设厅规范下载
。
有时,编译系统(或编译器)有权根据编译效率最后决定是否将所要求定义的内联函数(或内置函数)编译为内联函数。
3.2.3
对象及其内存存储内容
面向对象的方法以对象为中心和出发点。对象可以是现实生活中的一个物理实体,还可以是某一类概念实体的实例。
C++具有两种数据类型,第二章中介绍了第一种类型,即基本数据类型(即预定义类型)。本章将介绍第二种类型,即用户自定义的数据类型。主要是“类”。对象是类的实例(instance),它具有自己的属性,可以执行某些操作(函数)。
在上一节的类class integ的定义中,数据成员int j(一般情况下都有多个数据成员)可以具有不同数值或不同数值的组合,可以使用各种成员函数对它进行不同操作。这种情况下,只用一个类来表达是不够的。应该为该类建立不同对象来加以表达;正如对于整型数据需要建立不同整型变量那样。例如:
int j, k, m;
其中j、k和m是不同整型变量,它们是整型数据类型int的不同实例;而
integ obj1, obj2, obj3; 中的obj1、obj2和obj3则是class integ的不同对象,它们是class integ的不同实例,它们的数据成员具有不同数值组合。
此处,类是一种用户自定义的数据类型,而对象是一个类的实例。正如j、k和m是整型类数据的实例一样。
建立对象的过程称为类实例化(instantiation of classes)的过程。
建立对象有两种方法:
1. 定义类的同时建立对象:
class integ
{
int j;
public:
integ( ) { j = 6; }
//构造函数
int sq();
void inc();
int read();
}
obj;
此处同时定义class integ和建立对象obj。
此法的缺点是所建立的对象是全局对象,从软件工程的观点看是不合适的。因此以下法为宜。
2. 常用的方法:先定义类然后再建立对象:
例如在主函数外定义或说明一个类,然后在主函数内建立对象,即:
void main( )
{
integ obj;
……
}
本章§3.1“封装性”的[例2]中对象obj被如下使用:
void main()
{
int j;
integ obj;
//建立对象并初始化
cout << obj.read() << endl;
//读数据
j = obj.sq( );
//求平方
cout << j << endl;
//读平方值
obj.inc( );
//求增量
cout << obj.read() << endl;
//读增量值
}
/* Results:
6
36
7
*/
其中分别读对象obj的数据成员j值,求j的平方值,以及求j的增量值。
从主函数可以看出,访问对象成员时,使用成员访问符(access operator)“ . ”。此处obj.read( )和obj.integ::read( )的表达式是相同的。
对象可具有名称、指针和引用。在访问对象成员时可使用不同成员访问符。见下例。
[例1]现在看一下分别使用对象的名称和指针来使用(访问)对象成员的例子
// fig06_04.cpp of Deitels' book
// Demonstrating the class member
// access operators(成员访问符) . and ->
#include
class Count {
public:
int x;
void print() { cout << x << endl; }
};
void main()
{
Count obj, // create obj object
*objPtr = &obj, // pointer to obj
&objRef = obj; // reference to obj
/*使用对象名访问成员*/
obj.x = 7; // assign 7 to data member x
cout << "Print x using the object's name: ";
obj.print(); // call member function print
/*使用引用访问成员*/
objRef.x = 8; // assign 8 to data member x
cout << "Print x using a reference: ";
objRef.print();
/*使用指针访问成员*/
objPtr->x = 10; // assign 10 to data member x
cout << "Print x using a pointer: ";
objPtr->print();
//请注意:对象的指针可直接指向对象的成员
}
/* Results:
Print x using the object's name: 7
Print x using a reference: 8
Print x using a pointer: 10
*/
绝大多数情况下,一个对象被建立后,即被存储于内存栈区空间内。(极个别情况是静态对象,它被存储于数据空间内,本课程不讨论此类情况)。在该内存栈区空间内存储的是该对象的非静态数据。
对象的数据存储空间分为两部分:即内存栈区空间以及内存数据空间。其中内存数据空间用于存储该类的(即所有对象所共享的)静态数据。
[例2]确定对象大小和各种变量的地址
// obj_cont_1.cpp
// To get the sizes of the stored contents of the object of a class
// when there are both non-static and static data
#include "iostream.h"
class base {
public:
int x1;
static double s;
int inc( ) { return x1; }
// To show that inc( ) does not occupy space in stack area
};
double base::s = 1.1;
//静态数据必须说明或同时初始化
void main()
{
cout<< "size of class base is "<< sizeof(base) << endl;
cout<< "equal to size of an integer:"<< sizeof(int) << endl;
//To show that static datum occupies space in data area
cout<< "address of static base::s is "<< &base::s <
class integ
{
int j;
public:
integ(int a) { i = a; }
int sq();
};
class dbl
{
double d;
public:
dbl(double t) { d = t; }
double sq();
};
#endif
//_MULFILE_H
第一个隐藏源文件mulfile1.cpp(包括类的实现部分,供应商只提供目标文件mulfile1.obj)
// mulfilel.cpp
// definition of the member function
// only object file is provided
// The case of multi-file linking
#include "mulfile.h"
int jnteg::sq( )
{ return i*i; }
第二个隐藏源文件mulfile2.cpp(包括类的实现部分,供应商只提供目标文件mulfile2.obj)
// mulfile2.cpp
// definition of the member function
// only object file is provided
// The case of multi-file linking
#include "mulfile.h"
double dbl::sq( )
{ return d*d; }
主文件mulfile_tst.cpp(用户编写的应用文件)
// mulfile_tst.cpp
// main file used to test multi-file linking of encapsul.cpp
// To be linked with mulfile1.obj and mulfile2.obj
#include "mulfile.h"
void main()
{
int j;
double d;
integ ii(6);
dbl dd(1.5);
i = ii.sq();
d = dd.sq();
cout << i << endl;
cout << d << endl;
}
/* Results:
36
2.25
*/
连接多个文件的具体操作方法请参照“附录十:在VC++ 6.0下实现(连接)多文件应用程序”。
数据抽象(数据隐藏)的其他例子见§3.6.2.2“静态成员函数”中[例3]和“附录十一:数据隐藏及代理类”。
3.3
构造函数(constructor)和对象的初始化(initialization)
3.3.1
构造函数
****构造函数的主要功能是将对象初始化,具体为:
将初始值赋予位于内存栈区空间内的对象的非静态数据成员(不包括该类的静态数据成员)。
构造函数的特点:
(一)构造函数是类的特殊成员函数,它的名字与类名相同,它不能具有返回值。
(二)当构造函数的函数定义(即包括函数体的部分)放在类体之外时,应将它的函数原型[说明]放在类体内。
(三)构造函数可以没有也可以具有多个参数。
(四)构造函数可以重载(见第五章)。
(五)构造函数不能继承(见第四章)。
[例1]不使用构造函数将对象初始化
// initialize_1.cpp
// Not using constructor to initialize an object
#include
class point
{
int x, y;
public:
void setpoint( int vx, int vy) { x = vx; y = vy; }
void print( ) { cout<
class point
{
int x, y;
public:
point( int vx, int vy) { x = vx; y = vy; }
void print( ) { cout<
答案
八年级地理上册填图题岩土工程勘察试题省略号的作用及举例应急救援安全知识车间5s试题及答案
是它曾调用了缺省的构造函数(由系统所提供的)。所以,当用户没有定义构造函数时,创建对象时系统将自动调用缺省构造函数,但缺省构造函数的功能很差。
3.3.2
带缺省参数的构造函数(constructor with default arguments)
应该指出:对象的初始化不可能在类中进行而只能在建立对象时在每个对象中单独进行,以便通过内存栈区空间内的对象的非静态数据成员来体现各对象之间的差异性。
但个别时候希望使用相同参数将大部分对象初始化,例如人的体温可初始化为36.5摄氏度。此时可依靠带缺省参数的构造函数。
此外,当使用new将内存堆区空间分配给对象数组时,带缺省参数的构造函数可用于自动将对象数组中所有单元(也即数组内所有对象)初始化。
[例1]使用座标缺省参数的构造函数
// default_1.cpp
// default parameters for constructors
#include
class coordinates
{
int x, y;
//coordinates
public:
coordinates (int i=400, int j=300)
//正处于屏幕中央
{
x = i;
y = j;
}
void show( )
{
cout<
class point
{
int x, y;
//point coordinates
public:
point (int vx=50, int vy=100)
{
x = vx;
y = vy;
cout<<"CONS"<
class point
{
int x, y;
//point coordinates
public:
point (int vx=50, int vy=100)
{
x = vx;
y = vy;
cout<<"CONS"<show( );
}
/* Results:
CONS
CONS
CONS
CONS
50,100
50,100
50,100
50,100
*/
3.3.3
拷贝构造函数(copy constructor)
如果在建立新对象时希望将同一类中原有对象内所有非静态数据成员的值都复制到新对象中,则可调用拷贝构造函数。此时新旧对象共享内存代码空间中相同的成员函数代码和内存数据空间中相同的静态数据,所以建立另一新对象时只须拷贝旧对详在内存栈区空间内的非静态数据。
有两类拷贝构造函数:系统提供的和用户自己定义的。以下例1和例2分别介绍这两类拷贝构造函数的应用。
[例1]利用系统所提供的拷贝构造函数建立新对象
// copy_cons_1.cpp
// to use copy constructor of system
#include
class point {
int X, Y;
public:
point (int x, int y)
{ X = x; Y = y;
cout<<"Cons"<
#include
class string
{
char *contents;
int size;
public:
string(int sz) {
size = sz;
contents = new char[sz];
cout<<"constructor "<
class point {
int j;
public:
point ( int i )
{
j = i;
cout<<"Cons"<
class point {
int X, Y;
public:
point (int x, int y)
{ X = x; Y = y;
cout<<"Cons"<
格式
pdf格式笔记格式下载页码格式下载公文格式下载简报格式下载
。我们已熟悉:
int fun( int i )
{
return i;
}
但不熟悉:
AA fun( AA obj )
{
return obj;
}
将两者对比一下,就容易看出,前例中形参是预定义类型int变量i,而后例中形参是class AA的对象obj。
[例4] // copy_cons_5.cpp
// To show how copy constructor works when a function is called
#include
class AA {
public:
int X;
AA (int j)
{
X = j;
}
};
void inc_by_value (AA obj)
{
obj.X++;
}
void inc_by_refer (AA & obj)
//形参是class AA的对象obj的引用
{
obj.X++;
}
void main()
{
AA obj(10);
cout<<"before inc_by_value():X="<
class AA {
public:
int X;
AA (int x)
{ X = x;
cout<<"Cons-"<
class A
{
const int cons_1;
const int cons_2;
int noncons;
public:
A (int a, int b, int c)
: cons_1 (a),cons_2 (b)
{
noncons = c;
}
void modify_show( );
};
void A::modify_show ( )
{
int temp = 15;
cout<
class base
{
int n;
const int cn;
int tt;
public:
int& rn;
base( int j1, int j2, int& j3) : cn(j1), rn(j3)
{
// 初始化列表,
// here you may not use "cn = j1;" or "rn = j3;"
n = j2;
tt = 2*rn;
cout<<"n is "<
class A
{
int cons_1;
int cons_2;
mutable int noncons;
public:
A (int a, int b, int c)
{
cons_1 = a;
cons_2 = b;
noncons = c;
}
void modify_show( ) const;
};
void A::modify_show ( ) const
{
int temp = 15;
cout<
class sub_class {
int x;
public:
sub_class (int z)
{ x = z; }
//constructor
void display()
{ cout<
class sub_class {
int x;
public:
sub_class (int z)
//constructor
{
x = z;
cout<<"Constructor_sub "<
void create( void ); // prototype
class base {
int data;
public:
base( int i )
{ data = i;
cout<<"CONS : "<
class base {
public:
base( int ); // constructor declaration
~base(); // destructor declaration
private:
int data;
};
#endif
隐藏源文件:
// cons_des_hide.cpp
// Fig. 6.9 of Deitels' book
// Member function definitions for class base
// not the source file but only the object file
// is provided to the user
#include "cons_des.h"
// #include
//already included in cons_des.h
base::base( int value ) : data(value)
{
cout << "Object " << data << " constructor";
}
base::~base()
{ cout << "Object " << data << " destructor " << endl; }
主文件:
// cons_des.cpp
// Fig. 6.9: fig06_09.cpp of Deitels' book
// main file for demonstrating the order
// in which constructors and destructors are called.
// to be linked with cons_des_hide.obj
#include "cons_des.h"
// #include
//already included in cons_des.h
void create( void ); // prototype
base first( 1 ); // global object
void main()
{
cout << " (global created before main)" << endl;
base second( 2 ); // local object
cout << " (local automatic in main)" << endl;
static base third( 3 ); // local object
cout << " (local static in main)" << endl;
create( ); // call function to create objects
base sixth( 6 ); // local object
cout << " (local automatic in main)" << endl;
}
// ordinary function for creating objects
void create( void )
{
base fourth( 4 );
cout << " (local automatic in create)" << endl;
static base fifth( 5 );
cout << " (local static in create)" << endl;
}
/* Results:
Object 1
constructor
(global created before main)
Object 2
constructor
(local automatic in main)
Object 3
constructor
(local static in main)
Object 4
constructor
(local automatic in create)
Object 5
constructor
(local static in create)
Object 4
destructor
Object 6
constructor
(local automatic in main)
Object 6
destructor
Object 2
destructor
Object 5
destructor
Object 3
destructor
Object 1
destructor
*/
以上程序中,在主程序运行之前,已经建立第一个对象(即全局对象first),主程序运行之后,顺序地建立自动对象second和静态对象third。
接着调用函数create( ),在create( )中建立自动对象fourth和内部静态对象fifth,退出create( )时除静态对象fifth外,删除其它对象并调用它的析构函数,即fourth。
回至主程序后,建立自动对象sixth。
退出主程序时,先删除主程序内的自动对象并调用它们的析构函数,即fourth和second。然后删除内部静态对象并调用它们的析构函数,即second和fifth。最后删除主程序外的全局对象并调用它的析构函数,即first。
附录十二中还有“为数组动态分配空间时构造函数和析构函数的调用次数”的例子。
3.6
特殊指针和特殊类成员
3.6.1
this指针
建立一个对象时,系统为该对象分配内存栈区空间(位于栈区内),用于存储该对象的非静态数据成员。而该类的独一的一组成员函数则存于另外的代码分区内。这些成员函数一般都用于访问(读写)该类各对象的各个数据成员。试问:各成员函数怎么知道这些非静态数据成员的地址,也即各对象的地址?原来,每个对象的地址都存放在它自己的this指针内。而每个成员函数的参数表中都隐含着这个指针。例如,§3.3.1“构造函数”的[例2]程序initialize_2.cpp中的构造函数point( int vx, int vy) { x = vx; y = vy; }的参数表实际为point( point * this, int vx, int vy),同时class point的成员函数void print( ) { cout<
class Test {
int x;
public:
Test( int a ) { x = a; } // constructor
void print() const;
};
void Test::print() const
{
cout << " x = " << x
<< "\n this->x = " << this->x
<< "\n(*this).x = " << ( *this ).x << endl;
// ( ) is required for *this
}
void main()
{
Test obj( 12 );
obj.print();
}
/* Results:
x = 12
this->x = 12
(*this).x = 12
*/
遇到同名的全局数据怎么办?可用全局分辨符“::”来分辨,它是一元作用域运算符。如下例:
[例2]区别局部和全局变量的程序
// global_local_1.cpp
// To show how to differentiate between local and global variables
#include
int x = 10;
//global variable
class point
{
int x;
// local variable, same name
public:
point(int j)
{
x = i;
}
void print()
{
cout<<"local variable is " << x << endl;
// i.e. this->x
cout<<"global variable is " << ::x << endl;
}
};
void main()
{
point p(5);
p.print( );
}
/* Results:
local variable is 5
global variable is 10
*/
[例3] // global_1.cpp
// Using the unary scope resolution operator '::'
#include
#include
const double PI = 3.14159265358979;
void main()
{
const float PI = float ( ::PI );
// 也可用const float PI = static_cast< float >( ::PI );
//将在第五章§5.4“类型转换”中详述
cout << setprecision( 20 )
<< " Local float value of PI = " << PI
<< "\nGlobal double value of PI = " << ::PI << endl;
}
/* Results:
Local float value of PI = 3.14159
Global double value of PI = 3.14159265358979
*/
this指针的一个用法是允许依靠返回该类对象的引用值来连续调用该类的成员函数。见下例。
[例4]通过this指针连续调用成员函数
// this_2.cpp
// Cascading member function calls.
#include
class Time {
int hour;
int minute;
int second;
public:
Time( )
{
hour = 0; minute = 0; second = 0;
}
Time setHour( int hr )
{
hour = ( hr >= 0 && hr < 24 ) ? hr : 0;
return *this;
// enables cascading
}
Time setMinute( int mn )
{
minute = ( mn >= 0 && mn < 60 ) ? mn : 0;
return *this;
// enables cascading
}
Time setSecond( int sc )
{
second = ( sc >= 0 && sc < 60 ) ? sc : 0;
return *this;
// enables cascading
}
void print_time() const;
};
void Time::print_time() const
{
cout << hour << ":" << minute << ":"
<< second <
class base
{
public:
int i;
static int s;
};
int base::s = 23;
// this should not be omitted,
// otherwise there will be error LNK2001:
// unresolved external symbol "public: static int base::s"
void main()
{
base bb;
cout<<"static member bb.s="<
class counter
{
static int count;
//用于表示已建立的对象数目
int objnum;
//用于表示对象序号
public:
counter ( )
{
count++;
//每建立一个对象就加一
objnum = count;
//为当前对象序号赋值
}
void show( )
{
cout<<"obj"<
ppt
关于艾滋病ppt课件精益管理ppt下载地图下载ppt可编辑假如ppt教学课件下载triz基础知识ppt
[例3]以上例2也可用另一种形式表达,如下:
// static_12.cpp
// A static datum is shared by two objects
#include
class counter
{
static int count;
//用于表示已建立的对象数
int objnum;
//用于表示对象序号
public:
counter ( )
{
count++;
//每建立一个对象就加一
objnum = count;
//为当前对象序号赋值
}
void show( )
{
cout<<"obj"<show( );
counter *ptr2 = new counter;
ptr2->show( );
delete ptr1;
delete ptr2;
}
/* Results:
obj1 created
totally 1 object(s)
obj2 created
totally 2 object(s)
*/
[例4]两个对象共享静态数据成员
// static_4.cpp
// To show a static member for two objects
#include
class Myclass
{
int x, y;
static int Sum;
public:
Myclass (int a, int b);
int GetSum ( );
};
int Myclass::Sum;
// Type name "int" must be used.
// Objects not created yet.
// If this datum is to be used, this statememt should not be omitted,
// or there will be fatal linking error:
// unresolved external symbol "private: static int
// Myclass::Sum"
Myclass::Myclass(int a, int b)
{
cout<<(x = a)<<";"<<(y = b)<
class counter
{
static int count;
//用于表示已建立的对象数
int objnum;
//用于表示对象序号
public:
counter ( )
{
count++;
//每建立一个对象就加一
objnum = count;
//为当前对象序号赋值
cout<<"obj"<show( );
counter *ptr2 = new counter;
ptr2->show( );
delete ptr2;
delete ptr1;
}
/* Results:
obj1 created
totally 1 object(s)
obj2 created
totally 2 object(s)
*/
主函数第一句无法运行,是因为此时尚未建立任何对象。无法通过对象来调用任何非静态成员函数。但静态成员函数在对象建立之前就早已存在,所以可在对象建立之前调用。如下:
[例2]在对象建立之前使用静态成员函数读取静态数据
// static_fun_3.cpp
// A static datum is shared by two objects
#include
class counter
{
static int count;
//用于表示已建立的对象数
int objnum;
//用于表示对象序号
public:
counter ( )
{
count++;
//每建立一个对象就加一
objnum = count;
//为当前对象序号赋值
cout<<"建立"<show( );
counter *ptr2 = new counter;
ptr2->show( );
delete ptr2;
delete ptr1;
counter::show( );
}
/* Results:
共有0个对象
建立1号对象
共有1个对象
建立2号对象
共有2个对象
删除2号对象
删除1号对象
共有0个对象
*/
但静态函数也有不足之处,即它无法访问非静态数据。见下例:
[例3] 静态函数无法访问非静态数据
// static_fun_4.cpp
// not working!!!
#include
#include
class string
{
static int total_length;
int length;
public:
string (char * s)
{
length = strlen (s);
}
//
static int set_total_length( )
{
//
total_length = total_length + length;
// illegal reference to data member 'string::length'
// in a static member function
//
return total_length;
}
};
int string::total_length;
void main()
{
string obj1 ("The First Object");
//
cout<
#include
class string
{
static int total_length;
int length;
public:
string (char * s)
{
length = strlen (s);
}
static int set_total_length( string obj )
{
total_length = total_length + obj.length;
return total_length;
}
};
int string::total_length;
void main()
{
string obj1 ("The First Object");
cout<
// Modified class Count of §3.2.4
class Count
{
public:
Count(int j=0) { x = i; }
// constructor
void display() const { cout << x << endl; }
private:
int x; // data member
};
// Function tries to modify private data of Count,
// but cannot, because it is neither a member
// nor a friend of Count.
void cannotSetX( Count &c, int val )
{
// c.x = val;
// ERROR: 'x' : cannot access private
// member declared in class 'Count'
}
int main()
{
Count counter;
// cannotSetX( counter, 3 )?
// cannotSetX is neither a member nor a friend
return 0;
}
[例2]只有友元函数才能访问类的私有数据
// friend_5.cpp
// Friends can access private members of a class.
// 与以上friend_4.cpp不同之处用不同颜色标出
#include
// Modified class Count of §3.2.4
class Count
{
friend void setX( Count &, int ); // friend declaration
public:
Count(int j=0) { x = j; }
// constructor
void display() const { cout << x << endl; }
private:
int x; // data member
};
// setX can modify private data of Count, because
// it is declared as a friend function of Count
void setX( Count &c, int val )
{
c.x = val; // legal: setX is a friend of Count
}
void main()
{
Count counter;
cout << "counter.x after instantiation: ";
counter.display();
cout << "counter.x after call to friend function setX( ): ";
setX( counter, 8 ); // set x with a friend
counter.display();
}
/* Results:
counter.x after instantiation: 0
counter.x after call to friend function setX( ): 8
*/
须要指出,一个类中所说明的友元函数不是该类的成员,而是独立于该类的一个外部函数(它也可以是另一个类的成员函数)。
3.6.3.2
友元类
[例1]作为子对象的友元的类,可以访问子对象的私有成员
// friend_3.cpp
// friend class Y must be used, otherwise two
// private members of class sub_X cannot be accessed
#include
class sub_X
{
int j;
static int s;
public:
friend class Y;
void Set(int a) { i=a; }
void Display( )
{
cout<<"i="<
本文档为【C++课件--薛景瑄chapter_3】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑,
图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。