C++面向对象程序设计计算机与信息学院罗宪第三章 类和对象本章主要介绍:
类和对象的基本概念(类的定义、对象的定义)
构造函数与析构函数(对象的初始化)
对象数组、对象指针及引用
向函数传递对象
静态成员
常量对象及常量成员
友元
对象成员
对象的存储类
标识符的作用域、可见性和名空间第一节 类和对象的基本概念
( P84-98)
一、结构与类
C++中的类实际上是在 C语言的结构体的基础上扩充得到的。即 C++的结构体的成员中可用函数 ——成员函数:
C++结构体的成员分为数据成员及成员函数
数据成员分为公有成员( public)、私有成员 (private)、
保护成员( protected)。
㈠ C++对结构的扩充例 【 3.1】 已知复数的实部,虚部,分别输出复数的实部,
虚部及绝对值 ——初步熟悉成员函数第一节 类和对象的基本概念
#include <iostream.h>
#include <math.h>
struct complex
{ double real;
double imag;
void init(double r,double i)
{ real=r;imag=i;}
double realcomplex( )
{return real;}
double imagcomplex( )
{return imag;}
double abscomplex( )
{ double t;
t=sqrt(real*real+imag*imag);
return t;}
};
第一节 类和对象的基本概念
void main()
{ complex A;
A.init(3.0,4.0);
cout <<"real of complex A=";
cout <<A.realcomplex()<<endl;
cout <<"imag of complex A=";
cout <<A.imagcomplex()<<endl;
cout <<"abs of complex A= ";
cout <<A.abscomplex()<<endl;
}
程序运行输出结果:
real of complex A=3
imag of complex A=4
abs of complex A=5
第一节 类和对象的基本概念
C++结构的扩充:
在 C++中,在定义一个结构时,可以定义成员函数;
如程序中的 init( ),realcomplex( ),imagcomplex( )、
abscomplex ( )等;
在 C++的结构中,成员分公有成员 (public)和私有成员
(private),私有成员只能在结构内使用,公有成员既可以在结构内使用,亦可在其它地方使用;
在结构中,未指明为公有成员或私有成员的成员默认为公有成员;
成员函数的调用方法:结构变量,成员函数 (实参表 );
上例结构体的定义部分程序可改为:
第一节 类和对象的基本概念
struct complex
{ private
double real;
double imag;
public
void init(double r,double i)
{ real=r;imag=i;}
double realcomplex( )
{ return real;}
double imagcomplex( )
{return imag;}
double abscomplex( )
{ double t;
t=sqrt(real*real+imag*imag);
return t;}
};
第一节 类和对象的基本概念
㈡ C++对结构的扩充 ——类
C++提供了一种比结构体更为安全的数据类型 ——类。
类与结构的扩充形式非常相似,但凡是未定义为公有成员( public)或保护成员 (protected)的成员均为私有成员 (private)——封装性的体现。
例 【 3.2】 将例 【 3.1】 用类表示。
第一节 类和对象的基本概念
#include <iostream.h>
#include <math.h>
class complex
{ private:
double real;double imag;
public:
void init(double r,double i)
{ real=r;imag=i;}
double realcomplex( ){return real;}
double imagcomplex( )
{return imag;}
double abscomplex( )
{ double t;t=sqrt(real*real+imag*imag);
return t;}
};
第一节 类和对象的基本概念
void main()
{ complex A;
A.init(3.0,4.0);
cout <<"real of complex A=";
cout<<A.realcomplex()<<endl;
cout <<"imag of complex A=";
cout<<A.imagcomplex()<<endl;
cout <<"abs of complex A= ";
cout<<A.abscomplex()<<endl;
}
第一节 类和对象的基本概念
㈢ 类的声明(定义)
1、格式:
class 类名
{ [private,]
私有数据成员或成员函数
public,
公有数据成员或成员函数
protected
保护数据成员或成员函数
} ;
第一节 类和对象的基本概念
2、说明:
类的声明 ( 定义 ) 中,private,protected,public可以按任意顺序出现任意次;但通常 private放在前面,
protected放在中间,public放在最后面;
类中的数据成员通常说明为私有成员,而成员函数通常说明为保护成员或公有成员;
㈣ 抽象数据类型及描述(教材① P4—P5)
如:将例 【 3.2】 用抽象数据类型表示为:
第一节 类和对象的基本概念
ADT Complex is
data
表示实部( real)、虚部( imag)的双精度数
operations
init
input:表示实部、虚部的双精度数
process:把二个双精度数赋值合实部,虚部
realcomplex
output:返回实部值
imagcomplex
output:返回虚部值
abscobplex
process:计算复数的模
output:返回复数的模
end ADT complex
第一节 类和对象的基本概念例 【 3.3】 定义一个日期抽象数据类型,其要求为:
设臵年、月、日的具体值;
判断该年是否为闰年;
显示年、月、日
1、分析:
抽象数据类型,Date
用整型数 year,month,day表示年,月、日;
对 Date的操作:
setdate( ):设臵日期
isleapyear( ):判断是否为闰年
printdate( ):显示日期
2,ADT(抽象数据类型)规范化描述:
第一节 类和对象的基本概念
ADT Date is
data
表示年、月、日的整数值
operations
setdate
input:表示年、月、日的整数值
process:把年、月、日的整数值赋值年、月、日
isleapyear
process:计算某年是否闰年
output:返回 1(闰年)或 0(不是闰年)
printdate
process:显示年、月、日
end ADT Date
第一节 类和对象的基本概念
3、类的定义
#include<iostream.h>
class Date
{ private:
int year,month,day;
public:setdate(int y=2000,int m=1,int d=1)
{year=y;month=m;day=d;}
int isleapyear(void);
void printdate(void)
{cout<<year<<'-'<<month<<'-'<<day<<endl;}};
int Date::isleapyear(void)
{ int p;
p=(year%4==0&&year%100!=0)||(year%400==0);
return p;}
第一节 类和对象的基本概念
4、主程序
void main(void)
{ Date da1,da2;
da1.setdate(2004,5,1);
da2.setdate(2006,10,1);
da1.printdate();da2.printdate();
cout<<"2004 year ";
if(da1.isleapyear())
cout<<"is a leap year."<<endl;
elsecout<<"is not a leap year."<<endl;
cout<<"2006 year ";
if(da2.isleapyear())
cout<<"is a leap year."<<endl;
elsecout<<"is not a leap year."<<endl;
}
第一节 类和对象的基本概念
㈤ 成员访问限制
1、公有成员,public——所定义的成员是开放的,可在任何地方访问(类内部及程序的其他地方);
2、私有成员,private —— 所定义的成员是隐藏的,只能在类内部访问,不能在程序的其他地方访问;
3、保护成员,protected ——所定义的成员是半开放的,
可在类内部访问,也可在其派生类中访问,但不能在程序的其他部分访问。
4、数据成员通常定义为私有成员,以实现数据的隐藏;
成员函数通常设为公有的,以通过消息传递访问数据成员 ;保护成员主要用于继承。
第一节 类和对象的基本概念二、数据成员
1、一般定义格式,类型 数据成员名
2、说明:
数据成员中,不能使用 auto,register,extern等进行说明; ( 请思考为什么? ) ;
——定义类时,计算机并不给数据成员分配内存空间,
在定义对象时才分配空间;
不能在定义时给类中的成员赋初值 。 ( 请思考为什么 )
数据成员既可为基本数据类型,也可以是复杂数据类型 ( 数组,指针,引用,结构体,也可以是类变量等 )
第一节 类和对象的基本概念三,成员函数
在面向对象程序设计中,成员函数也称方法 ——是面向对象程序设计消息传递机制的实现方式 。
㈠ 在类外定义
在类中只给出成员函数的原型,成员函数体在类的外部定义 。
格式:
返回类型 类名,:函数名 (形参表 )
{ 函数体 }
例 【 3.4】 定义座标点的类 point
第一节 类和对象的基本概念
1、分析:
抽象数据类型,Point
数据:
用实型数表示点的 x,y坐标;
对 Point的操作:
set_point( ):设臵点的坐标;
read_x( ):读取 x值;
read_y( ):读取 y值;
move( ):点的移动;
2,ADT(抽象数据类型)规范化描述:
第一节 类和对象的基本概念
ADT Point is
Data
用单精度实数表示点的 x,y坐标值
operations
set_point
input:表示坐标的二个实数
process:把二个实数分别赋值组点的 x,y坐标
read_x
output:返回点的 x坐标值
read_y
output:返回点的 y坐标值
move
input:表示坐标位移的二个单精度实数
process:把二个实数分别加到 x,y坐标值上
end ADT Point
第一节 类和对象的基本概念
class point
{ private:
float x,y;
public:
void set_point(float a,float b);float read_x(void);
float read_y(void);
void move(float a,float b);
};
void point::set_point(float a=0,float b=0){x=a ; y=b; }
float point::read_x(void)
{ return x;}
float point::read_y(void)
{ return y;}void point::move (float a,float b)
{ x+=a;y+=b;}
第一节 类和对象的基本概念
上面定义的类可以放在头文件 hpoint.h中。再在 main()
测试。
#include<iostream.h>
#include"hpoint.h"
void main(void)
{ point p1;
p1.set_point(20,30);
cout<<"x="<<p1.read_x()<<endl;
cout<<"y="<<p1.read_y()<<endl;
p1.move(5,5);
cout<<"x="<<p1.read_x()<<endl;
cout<<"y="<<p1.read_y()<<endl;
}
第一节 类和对象的基本概念
说明:
通常,当成员函数代码量较大时,在类外定义成员函数 。
在定义成员函数以前一定要加上类名,类名与函数名间一定要用,,:”;
成员函数中,应给出形参名,当无形参时,通常应加上 void;
成员函数的返回类型,与函数原型中的返回类型必须相同 。
成员函数与普通函数一样,可以设臵参数的默认值第一节 类和对象的基本概念
㈡ 在类内部定义 ——将成员函数定义为内臵函数
1,隐式定义 ——直接将函数直接定义在类的内部 。 如:
class point
{ private,
float x,y;
public,
void set_point(float a,float b)
{x=a ; y=b; }
float read_x( )
{ return x;}
float read_y( )
{ return y;}
void move (float a,float b)
{ x+=a;y+=b;}
};
第一节 类和对象的基本概念
2,显式定义
显示定义是指在定义内臵函数时,仍将函数放在类定义体之外,但为了使之能起到内臵函数的作用,在函数定义前加上 inline。 如:
第一节 类和对象的基本概念
class point
{ private:
float x,y;
public:
void set_point(float a,float b);float read_x(void);
float read_y(void);
void move(float a,float b);
};
inline void point::set_point(float a,float b){x=a ; y=b; }
inline float point::read_x(void)
{ return x;}
inline float point::read_y(void)
{ return y;}inline void point::move (float a,float b)
{ x+=a;y+=b;}
第一节 类和对象的基本概念四,对象的定义及引用
㈠ 类与对象的关系
1,面向对象程序设计概念 ( 第一章 )
类是具有相同性质和功能的对象的抽象
对象是类的实例 。
2,C++面向对象程序设计语言
类是将具有相同数据成员,成员函数的操作对象的概括 ( 或抽象 ) ——是对象的模板 。
对象是类的实例 ( 具体化 ) ——类变量 。
第一节 类和对象的基本概念
㈡ 对象的定义 ——与定义结构体变量相同
1,在声明 ( 定义 ) 类的同时定义对象 ( 类变量 )
如,class point
{ private,
float x,y;
public,
void setpoint(float a,float b);
float getx(void);
float get y(void );
} op1,op2;
但通常不采用此种方法 ——为什么?
——类的定义通常是独立的,直接定义对象为全局对象第一节 类和对象的基本概念
2,先定义类再定义对象
格式,类名 对象名表如,point op1,op2;
说明:
⑴ 在定义类时,计算机并不给类分配存储空间,只有定义了对象后,计算机才为对象分配存储空间;
⑵ 在定义类时定义的对象为全局对象 ——它并非在函数中定义;
⑶ 通常用第二种方法定义类对象 ——定义为局部类对象 。
第一节 类和对象的基本概念
㈢ 对象的使用对象的使用与结构体变量的使用相同 。
1,数据成员的使用格式,对象名,数据成员名
2,成员函数的使用格式,对象名,成员函数名 ( 实参表 )
或,对象名,类名,:成员函数名 ( 实参表 )
例 【 3.5】 用 point类求任意二个点之间的距离 。
分析:
程序:
第一节 类和对象的基本概念
#include<iostream.h>
#include<math.h>
#include"hpoint.h"
void main(void)
{ point p1,p2;
float x1,x2,y1,y2,dx,dy,s;
cout<<"x1=";cin>>x1;cout<<"y1=";cin>>y1;
cout<<"x2=";cin>>x2;cout<<"y2=";cin>>y2;
p1.set_point(x1,y1);
p2.set_point(x2,y2);
dx=p2.read_x()-p1.read_x();
dy=p2.read_y()-p1.read_y();
s=sqrt(dx*dx+dy*dy);
cout<<"两点间的距离为,"<<s<<endl;
}
第一节 类和对象的基本概念
3,说明:
私有成员不能在类的外部被使用 ——私有数据成员只能通过公有成员函数使用 ——数据的封装性;
可以定义指向对象的指针来使用对象 ——对象指针访问其成员时用操作符,->”;
可以定义对象的引用对象 。
例 【 3.6】 分析程序的运行结果 。
第一节 类和对象的基本概念
#include<iostream.h>
#include"hpoint.h"
void main(void)
{ point p1,*p;
point &p2=p1;
p1.set_point(20,30);
cout<<"x="<<p1.read_x();
cout<<" y="<<p1.read_y()<<endl;
cout<<"x="<<p2.read_x();
cout<<" y="<<p2.read_y()<<endl;
p2.move(5,5);
p=&p1;
cout<<"x="<<p->read_x();
cout<<" y="<<p->read_y()<<endl;
}
第一节 类和对象的基本概念
㈣ 对象赋值语句
对象赋值:用赋值表达式将一个对象赋值给另一个变量 。
对象赋值的作用:是将,=”右边对象中的成员逐一赋值左边的对象 ——亦称为对象的拷贝 。
说明:
在使用对象赋值语句时,两个对象必须是相同的类;
对象赋值后,二个对象仍是分离的,当改变某一对象成员的值时,另一对象不会因此而改变;
对象赋值是一种浅拷贝:在一般情况下,对象赋值是成功的,但当类中存在指针时,可能会产生错误 。
例 【 3.7】 分析程序的运行结果 。
第一节 类和对象的基本概念
#include<iostream.h>
#include"hpoint.h"
void main(void)
{ point p1,p2;
p1.set_point(20,30);
p2=p1;
cout<<"p1,x="<<p1.read_x();
cout<<" y="<<p1.read_y()<<endl;
cout<<"p2,x="<<p2.read_x();
cout<<" y="<<p2.read_y()<<endl;
p1.move(5,5);
cout<<"p1,x="<<p1.read_x();
cout<<" y="<<p1.read_y()<<endl;
cout<<"p2,x="<<p2.read_x();
cout<<" y="<<p2.read_y()<<endl;
}
第一节 类和对象的基本概念四,类的作用域
1,类的作用域:是指在类定义时,{”与,}”之间所形成的作用域 。
2,说明:
一个类的所有成员都在该类的作用域内;
在类的内部,成员函数可以不受限制地使用所有数据成员 ( 直呼其名 ) 或成员函数;
在类的作用域外,只能引用类的公有成员 ( 通常为成员函数 ),不能引用私有成员 ——私有成员只能通过公有成员访问 。
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
本节中,将介绍两个特殊的成员函数 ——构造函数,
析构函数:
在定义一个类对象时,计算机将给该对象分配相应的存储单元,同时进行必要的初始化 ——由构造函数完成 ——用户提供或由系统提供 。
当撤销类对象时,通常应释放内存空间,并做一些收尾工作 ——由析构函数完成 ——由用户定义或系统提供 。
一,构造函数
㈠ 构造函数的概念
观察 Date类的成员函数
void set_date(int y=2000,int m=1,int d=1){year=y;month=m;day=d;}
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
——该函数的作用是为私有数据成员 year,month、
day赋值 ——即:通过调用对象成员函数为对象的数据成员赋值;
观察P oint类的成员函数
void set_point(float a,float b)
{x=a ; y=b; }
——该函数的作用是为私有数据成员 x,y赋值 ——即:
通过调用对象成员函数为对象的数据成员赋值;
结论:通常每个类都应提供一个函数为类中的数据成员赋值。
问题:基本类型的变量可以在定义时赋初值,定义对象时是否可以赋初值 ——即:在定义对象时直接给数据成员赋初值?
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
解决办法:将上述函数分别改为以下形式即可:
Date类:
Date(int y=2000,int m=1,int d=1)
{year=y;month=m;day=d;}
在定义类时可直接给 Date对象赋初值。如:
Date da1(2004,5,1);
Point类,
Point(float a,float b)
{x=a ; y=b; }
在定义类时可直接给 Date对象赋初值。如:
Point p1(10.0,15.5);
——即函数名与类名相同(无返回值类型) ——构造函数。
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
例 【 3.8 】 将例 【 3.3 】 用构造函数实现。
1、类的定义(保存在 hdate.h文件中):
第二节 对象的初始化
——构造函数与析构函数
class Date
{ private:
int year,month,day;
public:
Date(int y=2000,int m=1,int d=1)
{year=y;month=m;day=d;}
int isleapyear(void);
void printdate(void)
{cout<<year<<'-'<<month<<'-'<<day<<endl;}
};
int Date::isleapyear(void)
{ int p;
p=(year%4==0&&year%100!=0)||(year%400==0);
return p;
}
第二节 对象的初始化
——构造函数与析构函数
2、主函数 #include<iostream.h>
#include " hdate.h "
void main(void)
{ Date da1 (2004,5,1),da2(2006,10,1);
da1.printdate();da2.printdate();
cout<<"2004 year ";
if(da1.isleapyear())
cout<<"is a leap year."<<endl;
elsecout<<"is not a leap year."<<endl;
cout<<"2006 year ";
if(da2.isleapyear())
cout<<"is a leap year."<<endl;
elsecout<<"is not a leap year."<<endl;
}
第二节 对象的初始化
——构造函数与析构函数
㈡ 构造函数的定义
格式:类名(形参表);
性质:
构造函数的名字必须与类名相同;
构造函数的形参可以是任意数据类型,但不能有返回值类型;
定义对象时,编译系统会自动调用构造函数。
例 【 3.9】 定义对象时自动调用构造函数举例 ——运行程序,观察程序的输出结果。
第二节 对象的初始化
——构造函数与析构函数
#include <iostream.h>
class myclass
{ public:
myclass()
{ cout <<"constructing "<<endl;}
};
void main()
{ myclass A;
}
main()函数中,只定义了一个对象,但直接调用了构造函数 ——构造函数是在定义对象时自动执行的。
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
㈡ 构造函数的调用构造函数的调用是在定义对象时调用的。
格式,类名 对象名(实参表)。
或,类名 对象名 =构造函数名(实参表)
例 【 3.10】 用构造函数实现对复数类( complex)对象的初始化。
1、分析:
2、类的定义:
定义复数类 complex并将其存入 hcomplex.h中第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
class complex
{ private:
double real;
double imag;
public:complex(double r,double i)
{ real=r;imag=i;}
double realcomplex(void)
{return real;}
double imagcomplex(void){return imag;}
double abscomplex(void)
{ double t;
t=sqrt(real*real+imag*imag);
return t;}
};
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
#include <iostream.h>
#include <math.h>
#include "hcomplex.h"
void main(void)
{ complex A(3.0,4.0);
cout <<"real of complex A="<<A.realcomplex();
cout<<endl;
cout <<"imag of complex A="<<A.imagcomplex();
cout<<endl;
cout <<"abs of complex A="<<A.abscomplex();
cout<<endl;
}
3、定义 complex对象并初始化 ——main( )函数第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
#include <iostream.h>
#include <math.h>
#include "hcomplex.h"
void main(void)
{ complex A= complex(3.0,4.0);
cout <<"real of complex A="<<A.realcomplex();
cout<<endl;
cout <<"imag of complex A="<<A.imagcomplex();
cout<<endl;
cout <<"abs of complex A="<<A.abscomplex();
cout<<endl;
}
也可将其改为:
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
㈢ 说明
构造函数必须与类同名;
构造函数没有返回值;但构造函数前不能加 void
类型符(其他没有返回值的成员函数必须加类型符
void);
在实际应用中,在定义每个类时都应定义一至多个构造函数 (重载 ),以对各数据成员进行初始化;
如果不给出构造函数,系统将自定义一个构造函数;
构造函数可以不带任何参数;
构造函数可以用初始化表对参数进行初始化;
如:
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
class A
{ int i; char j; float f;
public,
A(int I,char J,float F)
{i=I;j=J;f=F;}};
class A
{ int i; char j;float f;
public,
A(int I,char J,float F),i(I),j(J),f(F)
{ }};
可改为:
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
如果数据成员为数组,必须用赋值语句。如:
class A
{ int i;
char j;
float f;
char name[20];
public,
A(int I,char J,float F,char N[ ]),i(I),j(J),f(F)
{ strcpy(name,N);}
};
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
对于没有定义构造函数的类,其公有数据成员要以用初始值表进行初始化。如:
#include <iostream.h>
class myclass
{public,
char name[20];
int no;
};
void main(void)
{ myclass a={"chen",25};
cout <<a.name<<" "<<a.no<<endl;
}
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
构造函数不能单独调用。如:
complex A ;A= complex(3.0,4.0);
——构造函数错误的调用方法。
思考:构造函数能否取代为对象提供值的函数(如:
set_date( int y,int m,int d) )?
二、缺省参数的构造函数
在实际应用中,有些构造函数的参数值通常是不变的,只是在一些特殊情况下才需改变其值 ——可定义带缺省参数的构造函数。
例 【 3.11】 分析程序的输出结果第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
#include <iostream.h>
#include <math.h>
class complex
{ private:
double real;
double imag;
public:
complex(double r=0.0,double i=0.0)
{ real=r;imag=i;}
double abscomplex()
{ double t;
t=sqrt(real*real+imag*imag);
return t;}
};
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
void main()
{ complex A(3.0,4.0);
complex B(3.0);
complex C;
cout <<"abs of complex A="<<A.abscomplex();
cout<<endl;
cout <<"abs of complex B="<<B.abscomplex();
cout<<endl;
cout <<"abs of complex C="<<C.abscomplex();
cout<<endl;
}
abs of complex A=5
abs of complex B=3
abs of complex C=0
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
三、析构函数
㈠ 析构函数及作用
1、析构函数
析构函数也是一种特殊的成员函数,执行的是与构造函数相反的操作 ——通常用作执行一些清理任务(如释放对象所占用的内存等)。
2、析构函数的特点:
析构函数与构造函数的名字相同,但在其前面加上,~”;
析构函数没有参数,也没有返回值,不能重载 —
—一个类中只能有一个析构函数;
当拆销对象时,自动调用析构函数。
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
例 【 3.12】
先将,hcomplex.h”头文件中的内容改为以下程序,
并以 hhcomplex.h”存盘。
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
class complex
{ private:
double real;
double imag;
public:complex(double r,double i)
{real=r;imag=i;cout<<"constructing..."<<endl;}~ complex(void)
{cout<<"destructing..."<<endl;}
double realcomplex(){return real;}
double imagcomplex()
{return imag;}
double abscomplex()
{ double t;t=sqrt(real*real+imag*imag);
return t;}
};
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
用以下主函数测试析构函数的调用:
#include <iostream.h>
#include <math.h>
#include"hhcomplex.h"
void main()
{ complex A(3.0,4.0);
cout <<"abs of complex A="
cout<<A.abscomplex()<<endl;
}
结论:析构函数是在对象撤销时自动调用的运行程序的输出结果为:
constructing…
abs of complex A=5
destructing…
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
㈡ 说明
每个类必须有一个析构函数,如果未定义析构函数,则系统自定义一个析构函数;
对于大多数类而言,缺省的析构函数已够用,但有时需进行内部处理,则需要显示定义析构函数。
例 【 3.13】 显示定义析构函数举例第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
#include<iostream>
#include<string.h>
using namespace std;
class string_data
{ private:
char *str;
public:
string_data(char *s);
~string_data(void);
char *get_info(void)
{return str;}
};
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
string_data::string_data(char *s)
{ cout<<"constructing,为字符串分配内存 ";
cout<<endl;
str=new char[strlen(s)+1];
strcpy(str,s);
}
string_data::~ string_data(void)
{ delete str;
cout<<"destructing:释放字符串占用内存 ";
cout<<endl;
}
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
void main(void)
{ string_data str1("teacher");
cout<<str1.get_info()<<endl;
}
结论,当在构造函数中用 new动态分配内存空间时,
应显式定义析构函数,用 delete释放已分配的内存空间。
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
四、重载构造函数与一般的成员函数一样,构造函数也可以重载。如:
class A
{ …
public,
A( void);
A(int);
A(int,char);
A(float,char);

}
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
在 main( )函数中可分别调用:
main( )
{ A x;
A y(10);
A z(10,’z’);
A w(5.5,’W’);

}
例 【 3.14】 分析下列程序,熟悉构造函数的重载。
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
#include <iostream.h>
class timer
{
private:
int seconds;
public:
timer()
{seconds=0;}
timer(int i)
{seconds=i;}
timer(int min,int sec)
{seconds=min*60+sec;}
int gettime()
{return seconds;}
};
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
void main(void)
{ timer a,b(10),c(1,10);
cout<<"seconds1="<<a.gettime()<<endl;
cout<<"seconds2="<<b.gettime()<<endl;
cout<<"seconds3="<<c.gettime()<<endl;
}
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
五、拷贝构造函数(复制构造函数)
㈠ 什么是拷贝构造函数例 【 3.15】 分析程序的结果
#include<iostream.h>
#include"hpoint.h"
void main(void)
{ point p1,p2;p1.set_point(20,30);
p2=p1;point p3(p1);
cout<<"p1,x="<<p1.read_x();
cout<<" y="<<p1.read_y()<<endl;
cout<<"p2,x="<<p2.read_x();
cout<<" y="<<p2.read_y()<<endl;cout<<"p3,x="<<p3.read_x();
cout<<" y="<<p3.read_y()<<endl;
}
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
其中:
p2=p1;
point p3(p1);
—— 对象的拷贝可以把 hpoint.h中的类定义改为如下形式(存放在
hhpoint.h中 ):
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
class point
{ private:
float x,y;
public:
point(void) //无参构造函数
{x=0;y=0;}
point(float a,float b) //构造函数重载
{x=a ; y=b; }
point(const point &ob) //拷贝构造函数
{x=ob.x;y=ob.y;}
void set_point(float a,float b);
float read_x(void);
float read_y(void);
void move(float a,float b);
};
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
void point::set_point(float a,float b)
{x=a ; y=b; }
float point::read_x(void)
{ return x;}
float point::read_y(void)
{ return y;}
void point::move (float a,float b)
{ x+=a;y+=b;}
再测试。其中:
point(const point &ob)
{x=ob.x;y=ob.y;}
——拷贝构造函数第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
㈡ 拷贝构造函数的定义
格式:
类名( [const] 类名 &对象名)
{ 函数体 } —— 通常为数据成员拷贝
㈢ 默认拷贝构造函数
如果不定义拷贝构造函数,系统会自定义一个拷贝构造函数,实现对数据成员的拷贝。
㈣ 说明:
拷贝构造函数无返回值,也不能有 void;
通常不用单独编写拷贝构造函数,直接使用默认拷贝构造函数 ;
默认拷贝构造函数是一种浅拷贝 ——当在类中用 new
分配内存空间时,通常应编制相应的拷贝构造函数。
第二节 对象的初始化
—构造函数与析构函数 (教材① P99-120)
例 【 3.16】 调试下列程序( P115例 3.11)。
——进入 VC打开 e316.cpp
第三节 对象数组、对象指针及对象引用(教材① P120-132)
一、对象数组
可以定义对象数组处理多个对象例 【 3.17】 利用 hhpoint.h中的 point类建立对象数组并分析程序的结果。
#include<iostream.h>
#include"hhpoint.h"
void main(void)
{ point p[5];
int i;
for (i=0;i<5;i++)
{ p[i].set_point(i+1,2*(i+1));}
for(i=0;i<5;i++)
cout<<p[i].read_x()<<" "<<p[i].read_y()<<endl;
}
第三节 对象数组、对象指针及对象引用(教材① P120-132)
可以用缺省参数构造函数为对象数组赋初值。如:将上例改为:
#include<iostream.h>
#include"hhpoint.h"
void main(void)
{ point p[5]; //自动调用缺省参数构造函数
int i;
for(i=0;i<5;i++)
cout<<p[i].read_x()<<" "<<p[i].read_y()<<endl;
for (i=0;i<5;i++)
{ p[i].set_point(i+1,2*(i+1));}
for(i=0;i<5;i++)
cout<<p[i].read_x()<<" "<<p[i].read_y()<<endl;
}
第三节 对象数组、对象指针及对象引用(教材① P120-132)
二、对象指针可以使用指针引用对象或对象数组。
方法:
定义对象指针
将指针指向某一对象(或对象数组)
用指针引用对象(或对象数组)
格式,对象指针 ->公有成员例 【 3.18】 分析程序的输出结果第三节 对象数组、对象指针及对象引用(教材① P120-132)
#include<iostream.h>
#include"hhpoint.h"
void main(void)
{ point p1(5.5,12.0),p2[5],*p;
int i;
p=&p1; //p指向对象 p1;
cout<<p->read_x()<<" "<<p->read_y()<<endl;
cout<<endl;
for (i=0;i<5;i++)
{ p2[i].set_point(i+1,2*(i+1));}
p=p2; //p指向对象数组 p2,或改为,p=&p2[0];
for(i=0;i<5;i++)
cout<<p->read_x()<<" "<<p->read_y()<<endl;
}
第三节 对象数组、对象指针及对象引用(教材① P120-132)
三、对象引用
可以定义对象的引用 ——在前面已作介绍四,this指针
1,this指针
C++提供了一个特殊的对象指针 ——this指针 ;
this指针为成员函数所属对象的指针,指向对象的地址 ;
this指针是一种隐含指针,隐含于每个类的成员函数中,即调用某成员函数时,都将自动产生一个 this指针。
this指针可以隐式调用 ——即在类内部直呼其名(前面介绍的所有例子) ——通常均采用此种方法。
第三节 对象数组、对象指针及对象引用(教材① P120-132)
2、显式调用 this指针
格式,this->成员名例 【 3.19】 将 hhpoint.h文件中的 point类的定义改为:
第三节 对象数组、对象指针及对象引用(教材① P120-132)
class point
{ private:
float x,y;
public:
point(void) //无参构造函数
{this->x=0;this->y=0;}
point(float a,float b) //构造函数重载
{this->x=a ; this->y=b; }
point(const point &ob) //拷贝构造函数
{this->x=ob.x;this->y=ob.y;}
void set_point(float a,float b);
float read_x(void);
float read_y(void);
void move(float a,float b);
};
第三节 对象数组、对象指针及对象引用(教材① P120-132)
void point::set_point(float a,float b)
{this->x=a ; this->y=b; }
float point::read_x(void)
{ return this->x;}
float point::read_y(void)
{ return this->y;}
void point::move (float a,float b)
{ this->x+=a;this->y+=b;}
注意,this指针是系统自定义的,用户不能再定义第四节 向函数传递对象
在 C++中,可以用对象作为函数的参数 ——形参或实参。主要有以下形式:
形参、实参均为对象 ——传值调用;
形参为对象指针,实参为对象指针或对象地址 —
—传址调用;
形参为对象引用,实参为对象 ——传址调用;
形参、实参为对象指针或对象数组 ——传址调用。
1、形参、实参均为对象 ——传值调用例 【 3.20】 分析下列程序的输出结果第四节 向函数传递对象
#include<iostream.h>
#include"hhpoint.h"
void f1(point p)
{ p.move(5.5,10.5);
cout<<p.read_x()<<" "<<p.read_y()<<endl;
}
void main()
{ point p1(10.2,20.8);
cout<<p1.read_x()<<" "<<p1.read_y()<<endl;
f1(p1); //调用 f1:将实参对象 p1传递给形参 p
cout<<p1.read_x()<<" "<<p1.read_y()<<endl;
}
结果,10.2 20.8
15.7 31.3
10.2 20.8
第四节 向函数传递对象
2、形参为对象指针,实参为对象指针或对象地址例 【 3.21】 分析下列程序的输出结果
#include<iostream.h>
#include"hhpoint.h"
void f2(point *p)
{ p->move(5.5,10.5);
cout<<p->read_x()<<" "<<p->read_y()<<endl;
}
void main()
{ point p1(10.2,20.8);
cout<<p1.read_x()<<" "<<p1.read_y()<<endl;
f2(&p1); //调用 f2:将对象 p1的地址传递给形参 p
cout<<p1.read_x()<<" "<<p1.read_y()<<endl;
}
第四节 向函数传递对象
3、形参为对象引用,实参为对象例 【 3.22】 分析下列程序的输出结果
#include<iostream.h>
#include"hhpoint.h"
void f3(point &p)
{ p.move(5.5,10.5);
cout<<p.read_x()<<" "<<p.read_y()<<endl;
}
void main()
{ point p1(10.2,20.8);
cout<<p1.read_x()<<" "<<p1.read_y()<<endl;
f3(p1); //调用 f3:将对象传递给引用对象 p
cout<<p1.read_x()<<" "<<p1.read_y()<<endl;
}
第四节 向函数传递对象
4、形参、实参为对象指针或对象数组
例 【 3.23】 定义处理 3科成绩及平均成绩的类,用动态数组类实现 n个学生的成绩处理 ——成绩的输入、输出、
计算 n个学生的平均成绩均用普通函数实现
(程序在 e323.cpp中)
例 【 3.24】 综合举例。将上例改为处理任意个学生的任意科(不超过 10科成绩),要求:
成绩的输入
成绩的输出
n个学生的平均成绩
按平均由高到低成绩排序后输出
(将成绩类的定义放在 hscore.h中)
第四节 向函数传递对象
1、类的定义(定义类并将其放在 hscore.h文件中)
数据成员(私有):
sc[M]——成绩,aver——平均成绩,m——科数;
成员函数(公有):
score(void)——对 sc[M],aver臵 0;
score(float x[],int n)——成绩初始化(赋值);
void set_score(float x[],int n)——成绩赋值;
float get_score(int i) ——得到第 i科成绩;
float get_aver(void)——得到平均成绩;
void print_score(void);——输出成绩及平均成绩。
第四节 向函数传递对象
const int M=10;
class score
{ private:
float sc[M],aver;
int m;
public:
score(void); //无参构造函数
score(float x[],int n);//构造函数重载:初始化成绩
void set_score(float x[],int n); //提供成绩
float get_score(int i) //得到第 i科成绩
{return sc[i];}
float get_aver(void) //得到平均成绩
{return aver;}
void print_score(void);
};
第四节 向函数传递对象
score::score(void) //无参构造函数
{ int i;
m=M;
for(i=0;i<m;i++)
sc[i]=0;
aver=0;
}
score::score(float x[],int n)
//构造函数重载:初始化成绩
{ int i;float sum=0;
m=n;
for(i=0;i<m;i++)
{ sc[i]=x[i];sum+=sc[i];}
aver=sum/m;
}
第四节 向函数传递对象
void score::set_score(float x[],int n) //提供成绩
{ int i;float sum=0;
m=n;
for(i=0;i<m;i++)
{ sc[i]=x[i];sum+=sc[i];}
aver=sum/m;
}
void score::print_score(void) //输出成绩、平均成绩
{ int i;
for(i=0;i<m;i++)
cout<<" "<<sc[i];
cout<<" "<<aver<<endl;
}
第四节 向函数传递对象
2、成绩处理 ——用动态数组处理任意个学生的成绩:
void input(score *p,int n,int m) ——n个学生 m科成绩的输入
void print(score *p,int n,int m) ——n个学生 m科成绩的输出
score &average(score *p,int n,int m) ——m科成绩及平均成绩的平均成绩计算
void sort(score *p,int n,int m) ——n个学生 m科成绩按平均成绩排序第四节 向函数传递对象
void main(void)
{ int n,m;cout<<"学生人数,";cin >>n;
cout<<"考试科数,";cin>>m;score *p,aver;
p=new score[n]; //动态分配内存单元 ——动态数组
if(p==NULL){ cout<<"内存分配失败 "<<endl;
return ;}
input(p,n,m); //调用输入成绩函数
print(p,n,m); //调用输出成绩函数aver=average(p,n,m); //调用平均值计算函数
aver.print_score();sort(p,n,m);
print(p,n,m); //调用输出成绩函数
delete []p; //释放内存}
第四节 向函数传递对象
void input(score *p,int n,int m)
{ int i,j;float x[M];for(i=0;i<n;i++)
{ cout<<"第 "<<i+1<<"个学生成绩,"<<endl;for(j=0;j<m;j++)
{ cout<<"第 "<<j+1<<"科成绩,";
cin>>x[j];}
p[i].set_score(x,m);}
}
void print(score *p,int n,int m)
{ int i;for(i=0;i<n;i++)
p[i].print_score();
}
第四节 向函数传递对象
//求平均值
score &average(score *p,int n,int m)//用返回引用的方法
{ int i,j; float s[M]={0};static score aver;
//返回的对象必须是静态的
for(j=0;j<m;j++){ for(i=0;i<n;i++)
s[j]=s[j]+p[i].get_score(j);s[j]=s[j]/n;
}
aver.set_score(s,n);return aver;
}
第四节 向函数传递对象
//选择法排序
void sort(score *p,int n,int m)
{ score t;float a;
int i,j,k;
for(i=0;i<n-1;i++){ a=p[i].get_aver();k=i;
for(j=i+1;j<n;j++)
if(a<p[j].get_aver())
{ a=p[j].get_aver();k=j;}
if(k!=i){ t=p[i];p[i]=p[k];p[k]=t;}
}
}
第五节 静态成员(教材① P132-139)
静态成员分为静态数据成员及静态成员函数。
一、静态数据成员
1、静态数据成员的定义(类中定义)
格式,static 类型 数据成员名表;
2、静态成员的作用
静态成员在一个类中只有一个拷贝 ——它不属于某一个对象,而属于一个类(属于所有对象) ——无论建立多少个对象,都共享静态成员 ——是连接各个对象的桥梁;
静态数据成员主要用于各个对象公用数据。如:总数的统计(如窗口的打开次数等)、平均数等。
第五节 静态成员(教材① P132-139)
例 【 3.25】 分析程序的输出结果。
#include<iostream.h>
class counter
{ private:
static int count; //count为静态数据成员
char ch; //普通数据成员
public:
counter(char c)//构造函数
{ count ++;ch=c;}
void print_counter(void)
{cout<<ch<<","<<count<<endl;}
~counter(void)//析构函数
{count--;}
};
第五节 静态成员(教材① P132-139)
int counter::count=100;//静态数据成员赋初值
void main(void)
{ counter c1('A');
c1.print_counter();
cout<<endl;
counter c2('B');
c1.print_counter();
c2.print_counter();
cout<<endl;
counter c3('C');
c1.print_counter();
c2.print_counter();
c3.print_counter();
}
第五节 静态成员(教材① P132-139)
3、说明
静态数据成员可以说明为公有的、私有的或保护的,
说明为公有的可直接访问,类名,:静态成员名
( P134例 3.16)——通常不定义为公有的
私有或保护静态数据成员只能通过公有成员函数访问
( P134例 3.17)——通常定义为私有的
静态数据成员不能在类中进行初始化(不分配内存空间),在程序开始运行时即生成(不依赖任何对象),
必须在任何函数之外进行初始化操作;格式:
类型 类名,:静态数据成员 =初值;
静态成员不同于全局变量 ——封装在类内部 ——使用静态数据成员取代全局变量,使数据更为安全;
静态数据成员与静态变量是两个完全不同的概念。
第五节 静态成员(教材① P132-139)
4、结论,当在类中需要共享某一数据成员时应定义为静态数据成员
——如:记录建立对象的个数;在窗口打开时记录打开的次数等。
二、静态成员函数
1、为什么提出静态成员函数
在类中使用静态数据成员后,只要某一个对象改变了其值,整个类的对象相应的静态数据成员的值均改变 ——强耦合(?)
有效解决办法 ——静态成员函数 ——用于访问静态数据成员
2、静态成员函数的定义
在成员函数名前加上 static。
第五节 静态成员(教材① P132-139)
例 【 3.26】 分析程序的输出结果,熟悉静态成员函数的定义及访问静态数据成员的方法。
(原文件 e326.cpp)
3、说明:
静态成员函数可以在类内部定义(必须在上 static),
也可在类外定义 ——即在类内部声明,在类外定义 —
—类外定义时方法与普通成员函数相同。
静态成员函数属于一个类,可用类名调用格式,类名,:静态成员函数名(实参表);
如,simple,,sum1(&ob1); simple,,sum1(ob2);
静态成员可以用对象调用格式,对象名,静态成员函数名(实参表);
如,ob1,sum1(&ob1); ob2,sum1(ob2);
第五节 静态成员(教材① P132-139)
静态成员函数由于无 this指针( this指针属于某一对象,
静态成员属于一个类),只能访问静态数据成员,不能访问普通数据成员 ——静态成员函数专门用来访问静态数据成员。
例,将上例改为,定义静态成员函数 sum——用于专门访问静态数据成员。
原文件 e3261.cpp)
注意:普通成员函数既可以访问普通数据成员,也可以访问静态数据成员。
结论,当在类中定义有静态数据成员时,通常应定义静态成员函数来访问静态数据成员。
第六节 常量对象及常量成员
(教材① P139-145)
在 C++中可以用 const定义常量对象(常对象)、常量成员函数(常成员函数)、常量数据成员(常数据成员)
一、常量对象
定义格式:
类名 const 对象名表;
或:
const 类名 对象名表例 【 3.27】 调试程序,熟悉常对象与普通对象的不同。
(源文件,e327.cpp)
第六节 常量对象及常量成员
(教材① P139-145)
通过调试程序,可以得出以下结论:
常量对象不能被普通成员函数访问;
常量对象可以被常量成员访问;
常量对象的数据成员的值不能改变二、常量成员函数
1、常量成员函数的定义格式
在类中定义:
格式:
返回类型 成员函数名 (参数表 ) const
{ 函数体 }
第六节 常量对象及常量成员
(教材① P139-145)
在类外定义先声明,返回类型 成员函数名 (参数表 ) const ;
格式:
返回类型 类名,:成员函数名 (参数表 ) const
{ 函数体 }
2、说明:
常量对象只能调用其常量成员函数,不能调用普通成员函数;
普通对象既可调用普通成员函数,也可以调用常量成员函数
普通成员函数可以访问本类的常量成员函数
常量成员函数不能访问本类的普通成员函数第六节 常量对象及常量成员
(教材① P139-145)
常量成员函数与普通成员函数同名时,构成函数的重载 ——参数类型和个数可以相同,用 const加以区分。
三、常量数据成员
在类中定义数据成员时,可以定义为常量
1、定义格式:
const 数据类型 成员名或数据类型 const 成员名
例 【 3.28】 分析 P144例 3.21中的程序,熟悉常量数据成员及应用第六节 常量对象及常量成员
(教材① P139-145)
2、说明:
常量数据成员是指在一个对象中该数据成员的值不能改变;
同普通数据成员一样,常量数据成员隶属于某一对象
(与静态数据成员的差异);
常量数据成员必须在构造函数中通过初始化列表进行初始化。如例中构造函数:
Airliner(char *str,int a,int b):maxpassenger(a)
——用 a 的值初始化常量数据成员 maxpassenger。
第七节 友元(教材① P145-154)
一、为什么要采用友元
在类中定义的私有成员,只能通过公有成员访问 ——
数据的封装与隐藏。优点:
提高软件的可靠性、可重用性、可维护性不足:
增加了程序运行时函数的调用开销 ——当成员函数调用很频繁时,将导致执行效率的降低 ——增加程序的运行时间。如例 【 3.24】 中的成绩类对象数组的排序:
void sort(score *p,int n,int m)
{ score t;float a;
int i,j,k;
for(i=0;i<n-1;i++)
{ a=p[i].get_aver();k=i;
for(j=i+1;j<n;j++)
if(a<p[j].get_aver())
{ a=p[j].get_aver();k=j;}
if(k!=i)
{ t=p[i];p[i]=p[k];p[k]=t;}
}
}
假定 n=5,外循环执行 4次,1-4次外循环内循环分别执行 4,3,2,1次,共执行 10次,每一次外循环调用 1次
get_aver()函数,每一次内循环调用 2次 get_aver()函数
(共 25次) ——n越大调用次数越多。
第七节 友元(教材① P145-154)
二、友元函数
1、友元函数的定义 ——在函数前 friend。
例 【 3.29】 分析程序的运行结果,熟悉友元函数的定义及应用(源程序文件,e329.cpp)
第七节 友元(教材① P145-154)
#include<iostream.h>
#include<string.h>
class girl
{ private:
char *name;
int age;
public:
girl(char *na,int n)
{ name=new char[strlen(na)+1];
strcpy(name,na);
age=n;
}
~girl(void)
{delete name;} //释放 new分配的内存第七节 友元(教材① P145-154)
char *get_name(void)
{return name;}
int get_age(void)
{return age;}
void print_girl(void);
friend void disp(girl &g);
//声明 disp为友元函数
};
void girl::print_girl(void)
{ cout<<"姓名,"<<name;
cout<<" 年龄,"<<age<<endl;
}
第七节 友元(教材① P145-154)
void disp(girl &g)
//定义友元函数:不是类的成员不需要作用域运算符
{ cout<<"姓名,"<<g.name;
cout<<" 年龄,"<<g.age<<endl;
}
void main(void)
{ girl g1(“李小丫,,12);
girl g2(“王永兰,,15);
girl g3("赵梦美 ",13);
cout<<"姓名,"<<g1.get_name();
cout<<" 年龄,"<<g1.get_age()<<endl;
g2.print_girl();
disp(g3);//调用友元函数:与普通函数调用相同
}
第七节 友元(教材① P145-154)
2、说明:
友元函数不是类的成员函数 ——除非确因成员函数频繁,一般不要成义友元函数(与面向对象思想矛盾);
友元函数主要用于运算符的重载
友元函数可以访问对象的私有成员,只是在类中声明时加上 friend,由于不是成员函数,定义时不必在函数名前加上“类名,:”
友元函数无 this指针,因此,一般应带有一个入口参数 ——通过入口参数传递的对象名来引用该对象的成员。
当一个函数需要访问二个或以上的类时,友元函数非常有用例 【 3.30】 分析程序的输出结果(源文件 e330.cpp)
第七节 友元(教材① P145-154)
#include<iostream.h>
#include<string.h>
class boy;//类 boy的声明 ;
class girl
{ private:
char *name;
int age;
public:
girl(char *na,int n)
{ name=new char[strlen(na)+1];
strcpy(name,na);
age=n;
}
~girl(void)
{delete name;} //释放 new分配的内存
friend void disp(girl &g,boy &b); //声明 disp为 girl类友元函数
};
第七节 友元(教材① P145-154)
class boy
{ private:
char *name;
int age;
public:
boy(char *na,int n)
{ name=new char[strlen(na)+1];
strcpy(name,na);
age=n;
}
~boy(void)
{delete name;} //释放 new分配的内存
friend void disp(girl &g,boy &b);
//声明 disp为 boy类友元函数
};
第七节 友元(教材① P145-154)
void disp(girl &g,boy &b)
//定义友元函数:不是类的成员不需要作用域运算符
{ cout<<"女孩姓名,"<<g.name;
cout<<" 年龄,"<<g.age<<endl;
cout<<"男孩姓名,"<<b.name;
cout<<" 年龄,"<<b.age<<endl;
}
void main(void)
{ girl g1("李小丫 ",12);boy b1("张海兵 ",15);
disp(g1,b1);//调用友元函数:与普通函数调用相同
}
第七节 友元(教材① P145-154)
三、友元成员
友元成员是指一个类的成员函数可以是另一个类的友友元函数 ——即可以通过一个类的成员函数访问另一个类的私有成员。
例 【 3.31】 分析程序的输出结果(源文件 e331.cpp)
第七节 友元(教材① P145-154)
#include<iostream.h>
#include<string.h>
class boy;//类 boy的声明 ;
class girl
{ private:
char *name;
int age;
public:
girl(char *na,int n)
{ name=new char[strlen(na)+1];
strcpy(name,na);
age=n;
}
~girl(void)
{delete name;} //释放 new分配的内存
void disp(boy &b);//声明 disp为 girl类的成员函数
};
第七节 友元(教材① P145-154)
class boy
{ private:
char *name;
int age;
public:
boy(char *na,int n)
{ name=new char[strlen(na)+1];
strcpy(name,na);
age=n;
}
~boy(void)
{delete name;} //释放 new分配的内存
friend void girl::disp(boy &b);
//声明 disp为 boy类的友员函数
//disp既是 girl的成员函数,又是 boy的友元函数
};
第七节 友元(教材① P145-154)
void girl::disp(boy &b)
//定义友元函数:不是类的成员不需要作用域运算符
{ cout<<"女孩姓名,"<<name;
cout<<" 年龄,"<<age<<endl;
cout<<"男孩姓名,"<<b.name;
cout<<" 年龄,"<<b.age<<endl;
}
void main(void)
{ girl g1("李小丫 ",12);boy b1("张海兵 ",15);
g1.disp(b1);//调用对象 g1的成员函数
}
第七节 友元(教材① P145-154)
说明:
一个类的成员函数要作为另一个类的友元函数时,
必须先定义这个类(如 girl类);
由于友元函数所在的类名先于类的定义出现,应先声明。
四、友元类
友元类是指一个类可以作为另一个类的友元 ——可以用友元类的成员函数访问类中的成员。
例 【 3.32】 分析程序的输出结果(源文件 e332.cpp)
第七节 友元(教材① P145-154)
#include<iostream.h>
#include<string.h>
class boy;//类 boy的声明 ;
class girl
{ private:
char *name;
int age;
public:
girl(char *na,int n)
{ name=new char[strlen(na)+1];
strcpy(name,na);
age=n;
}
~girl(void)
{delete name;} //释放 new分配的内存
void disp(boy &b);//声明 disp为 girl类的成员函数
};
第七节 友元(教材① P145-154)
class boy
{ private:
char *name;
int age;
friend girl; //声明 girl是 boy的友元类
public:
boy(char *na,int n)
{ name=new char[strlen(na)+1];
strcpy(name,na);
age=n;
}
~boy(void)
{delete name;} //释放 new分配的内存
};
第七节 友元(教材① P145-154)
void girl::disp(boy &b)
//定义友元函数:不是类的成员不需要作用域运算符
{ cout<<"女孩姓名,"<<name;
cout<<" 年龄,"<<age<<endl;
cout<<"男孩姓名,"<<b.name;
cout<<" 年龄,"<<b.age<<endl;
}
void main(void)
{ girl g1("李小丫 ",12);boy b1("张海兵 ",15);
g1.disp(b1);//调用友元函数:与普通函数调用相同
}
第七节 成员对象与容器类
(教材① P170-172)
一、对象成员及容器类的概念
在 C++中,在定义一个类的数据成员时,除可以是基本数据成员外,还可以是其它类的对象 ——称为对象成员。如,class A
{ … };
class B
{ …
A a;

};
如果一个类包含有对象成员,称为容器类 ——组合技术第七节 成员对象与容器类
(教材① P170-172)
二、容器类构造函数
在容器类中的构造函数,可以通过初始化表为对象成员初值。方法为:
类名 (参数表 ):成员名 1(参数表 1),…,成员名 n(参数表 n)
例 【 3.33】 分析程序的输出结果(源文件 e333.cpp)
熟悉用构造函数为对象成员赋初值。
第七节 成员对象与容器类
(教材① P170-172)
#include<iostream.h>
#include<string.h>
class string
{ private:
char *str;
public:
string(char *s)
{ str=new char[strlen(s)+1];
strcpy(str,s);
}
void print(void)
{ cout<<str;}
~string()
{delete []str;}
};
第七节 成员对象与容器类
(教材① P170-172)
class girl{ private,
string name;
int age;
public:
girl(char *st,int ag):name(st){age=ag;}
void print(void)
{ cout<<"姓名,";
name.print();
cout<<" 年龄,"<<age<<endl;}
};
void main(void)
{ girl gl("张小丫 ",15);
gl.print();}
第七节 成员对象与容器类
(教材① P170-172)
三、说明:
容器类中至少应有一个构造函数,以通过参数表为成员对象赋初值 ——系统不为容器类提供构造函数;
创建容器类对象时,先调用对象成员所属类的构造函数,其执行顺序是对象成员的定义顺序;
释放容器类对象时,先调用容器类的析构函数,再调用对象成员的析构函数;
可以用容器类对象调用对象成员所属类的公有成员函数:
容器类对象成员名,对象成员所属类名,:成员函数名 (实参表 );
第七节 成员对象与容器类
(教材① P170-172)
例 【 3.34】 定义一个 date类,数据成员包括 year、
month,day(均为整型);再定义一个 person类,包括 name(字符指针),sex(字符数组),birthday
(为 date型)。
date类的定义放在,hdate.h”文件中;
person类的定义放在,hperson.h”文件中;
将主函数放在 e334.cpp文件中。
第七节 成员对象与容器类
(教材① P170-172)
class date
{ private:
int year,month,day;
public,
date(void)
{ year=1980;month=1;day=1;}
date(int y,int m,int d)
{ year=y;month=m;day=d;}
int get_year(void) {return year;}
int get_month(void) {return month;}
int get_day(void) {return day;}
};
第七节 成员对象与容器类
(教材① P170-172)
#include<string.h>
class person
{ private:
char *name;
char sex[2];
date birthday;
public:
person(char *na,char *s,int y,int m,int d):birthday(y,m,d)
{ name=new char[strlen(na)+1];
strcpy(name,na);
strcpy(sex,s);
}
char *get_name(void){return name;}
char *get_sex(void){return sex;}
第七节 成员对象与容器类
(教材① P170-172)
int get_year(void){return birthday.get_year();}
int get_month(void){return birthday.get_month();}
int get_day(void){return birthday.get_day();}
~ person(void){delete []name;}
void print(void);
};
void person::print(void)
{ cout<<"姓名,"<<name<<endl;
cout<<"性别,"<<sex<<endl;
cout<<"出生日期,"<<birthday.get_year()<<"年 ";
cout<<birthday.get_month()<<"月 ";
cout<<birthday.get_day()<<"日 "<<endl;
}
第七节 成员对象与容器类
(教材① P170-172)
#include<iostream.h>
#include"hdate.h"
#include"hperson.h"
void main(void)
{ person ps1("张小三 ","男 ",1985,12,15);
person ps2("李小丫 ","女 ",1986,3,9);
ps1.print();
cout<<endl;
ps2.print();
}
第八节 标识符的作用域、可见性和名空间(教材① P154-166)
本节内容主要为自学内容。主要包括:
一、标识符的作用域规则
1、标识符:包括函数名、常量名、变量名、类名、对象名、成员名、语句标号等
2、标识符的可见性:是指在某一区域内是否可以访问
(存取)
3、标识符作用域的大小:程序级、文件级、函数级、
类级、块级(复合语句内)。
二、作用域的种类
1、程序级:在整个程序中均可见 ——在某个文件中定义,在其它文件中声明(如用 extern声明的外部变量 )
第八节 标识符的作用域、可见性和名空间(教材① P154-166)
2、文件级:从定义处开始到文件的结束(如在某一文件中定义的全局变量)
3、类的作用域:即类体
普通数据成员、成员函数为类作用域,类名为文件级作用域;
友元不属于成员函数,为文件级作用域
静态成员属于整个类,为文件级作用域
4、函数级作用域:函数中的语句标号是唯一的函数级作用域。
5、块级作用域 ——在复合语句块内(,{”开始、,}”结束)定义的标识符。如:局部变量(自动、局部静态、
寄存器变量等)
第八节 标识符的作用域、可见性和名空间(教材① P154-166)
三、头文件
C++程序的组织方式:
1、用工程的方式 ——在一个工程下有多个源文件;
2、用头文件四、标识符的名空间
在由许多人共同开发一个大型软件时,往往用标识符的作用域仍然不能避免标识符的重复定义 ——有效的解决办法:使用标识符的名空间。
1、名空间的定义
namespace 名空间名
{ <名空间体 >}
另外,在名空间内可再定义另外的名空间第八节 标识符的作用域、可见性和名空间(教材① P154-166)
2、名空间的使用:
一般名空间的使用,using namespae 名空间名 ;
如,use namespace std;
内存名空间的使用:
using namespace 外层名空间名,:内层名空间名;
第九节 对象的存储类
(教材① P173-181)
本部分内容与,C语言程序设计中,及前面所介绍的内容中都已涉及,不再介绍,由学生自学。