第三章 类与对象本章主要内容:
1,类和对象,类的创建、对象的创建和使用 --重点 (掌握 )
2,this指针 -- 重点 难点 (掌握 )
3,访问控制、封装与信息隐藏 -- 重点 难点 (理解 )
4,友元 -- 重点
5,静态成员
6,const与类成员
20:43:40
§ 1 概 念程序举例
.银行帐户
.链表操作
20:43:40
存在的问题
– 函数名可能与对其它结构进行操作的函数发生冲突
– 客户程序员易忘记调用 initialize()和 cleanup()
– 数据和对数据进行的操作分离,不符合现实世界中事物的实际情况,也不符合人的思维习惯
– 数据完全对外暴露,无法保证对数据的访问的合法性,安全性
解决方法
把对数据进行操作的函数做成结构体的成员,以解决名字冲突和数据与操作的分离问题例,.修改后的银行帐户,修改后链表操作
将结构体改换为类,并在类上进行访问控制以解决数据外露的问题
例,.银行帐户类,链表类
用类的构造函数与析构函数保证初始化和清除工作一定会进行 --- 第四章
20:43:40
概念:
– 对象,现实世界中事物或实体在计算机中的抽象描述或表示
– 类,对象的抽象描述和概括,从总体上描述了这一类对象拥有的属性 (说明对象的结构 )和具有的行为
– 成员变量,结构体或类内部的变量称为结构体或类的成员变量
– 成员函数,结构体或类内部的函数称为结构体或类的成员函数二者统称为结构体或类的成员
20:43:40
§ 2 类与对象
class 类名
{
private,
私有成员表 ;
public:
公有成员表 ;
protected:
保护成员表 ;
};
例,点类
class Point{
private:
float fX,fY;
public:
void setX(int x){fX=x;}
void setY(int y){fY=y;}
float getX(){return fX;}
float getY(){return fY;}
};
一,类
.类的定义:
20:43:40
说明,
.class:关键字
.类名,需满足标识符的要求
.成员表,若干成员构成的列表成员包括数据成员 (对应于属性 )和函数成员 (对应于行为、
操作 )
数据成员一般是变量或其它类的对象 ----- 成员变量函数成员一般是函数的声明或定义 ----- 成员函数
.private,public,protected:关键字,用于对类中的成员进行访问控制,在类中可以任意次序出现任意多次,分别表示其后的成员为私有成员,公有成员和保护成员
.一般将成员变量定义成私有的,而将成员函数定义成公有的,将提供给客户程序员使用的函数声明成公有的,而将在类内部使用的辅助函数声明成私有的
20:43:40
类的成员函数
.在类中声明或定义的函数
.成员函数定义,两种方法
.在类中定义,创建类的同时,在类中直接给出函数体例,class Point{
float fX,fY;
public:
void setX(int x){fX=x;}
void setY(int y){fY=y;}
float getX( ){ return fX;}
float getY( ){ return fY;}
};
这样的成员函数将成为 内联函数,故其函数体应尽可能简洁
.在类中声明,在类外定义 (实现 ):使用作用范围解析运算符,:
一般把声明放在,h的文件中,该文件称做类的 接口文件而把定义放在,cpp的文件,该文件称做类的 实现文件例,.银行帐户,链表类
20:43:40
二,对象
.对象,把数据和对数据的操作集成在一起形成的独立实体类的变量,一个类的对象是该类的一个实例
.创建 (定义 )对象的一般形式,
类名 对象名列表 ;
例,Point pt1,pt2,*pPtr;
.对象的使用,与结构体变量的使用方法相近
.通过对象名使用对象的成员 (公有成员 ):
对象名,成员名例,pt1.setX(100);
pt1.setY(10);
.通过指向对象的指针使用对象对象指针变量 ->成员名例,pPtr=new Point; //或 pPtr=&pt1;
pPtr->setX(10);
pPtr->setY(20);
20:43:40
对象在内存中的存储
.创建对象时以类为样板,分配内存空间
Point pt1,pt2;
pt1.setX(10);pt1.setY(15);
pt2.setX(100);pt2.setY(12);
15
10
fY
fX
setX()
setY()
getX()
getY()
pt1
12
100
fY
fX
setX()
setY()
getX()
getY()
pt2
getY()代码
getX()代码
setY()代码
setX()代码类 Point的公用区
,创建对象时通常仅分配成员变量的空间,而成员函数的代码由各个对象共享
,逻辑上 每个对象都包含了一份类中定义的成员
20:43:40
§ 3 this指针问题:
类 Account的成员函数中,并没有指明引用的 sName,sID
和 fBalance分别是哪一个对象的成员,若创建了多个该类的对象,比如:
Account acc1,acc2,… ;
如 何 保 证 acc1.Initialize(… ),acc1.Deposit(… ),
acc1.Withdraw(… )和 acc1.Get_balance()操作的是 acc1内部的成员变量,而 acc2.Initialize(… ),acc2.Deposit(… )、
acc2.Withdraw(… )和 acc2.Get_balance()操作的是 acc2中的成员变量?
20:43:40
this指针
– 类的 非静态成员函数 (例如 Account类的 Initialize()等 )
带有一个隐含的参数,该参数就是 this指针,在内部通常它是成员函数的第一个参数
– 通过类的某个对象调用该函数时,this指针 自动 指向该对象,其值 自动 为该对象的地址例如:
调用 acc1.Initialize(“张三,,”S981020”,500);
时 this指针指向对象 acc1,而调用 acc2.Initialize(“李四,,”S981021”,1000);
时 this指针指向对象 acc2
20:43:40
– 成员函数的函数体中出现的对成员的引用,相当于通过该
this指针引用
– this是指针常量,自动指向调用成员函数的对象,函数体中不能改变其值,不能使之指向其它对象例,Account acc2;
this=&acc2;
例:
void Account::Initialize(char *name,char *id,
float amount){
strcpy(sName,name); //? strcpy(this->sName,name);
strcpy(sID,id); //? strcpy(this->sID,id);
fBalance=amount; //? this->fBalance=amount;
}
void Account::Deposit(float amount){
fBalance+=amount; //? this->fBalance+=amount;
}
20:43:40
.protected:保护成员保护成员介于私有成员与公有成员之间,在该类外部不能被访问,但在类内部和派生类中都可以被访问
§ 4 访问控制、封装与信息隐藏
通过三个关键字实现,public,private,protected
三个关键字限定其后的成员分别为公有成员,私有成员和保护成员,直到遇到下一个访问控制关键字或,}”
一,访问控制
.public:公有成员公有成员既可在本类的成员函数中被访问,也可被其它函数访问
.private:私有成员私有成员只能被本类的成员函数访问,其它函数中都不能访问这些成员
C++中类成员缺省的访问控制是 private(私有的 )
--- 类与结构体的唯一区别 ( 结构体成员缺省的访问控制是 public)
三个关键字在类中可以任意次序出现任意多次原则:
一般情况下,成员变量应定义成私有的,成员函数应定义成公有的提供给客户程序员使用的函数声明成公有的,而把在类内部使用的辅助函数声明成私有的
20:43:40
二,封装与信息隐藏
– 访问控制的实质就是在类 (对象 )上设置限制 (明确定义的边界 ),以实现封装,达到隐藏内部信息的目的
– 访问控制实现在类上,但其作用体现在对象上通过访问控制和接口与实现的分离就可以在很大程度上实现信息隐藏
– 除此之外,要实现信息隐藏,还应做到 接口与实现的分离
– 类中只包含成员变量的定义与成员函数的声明,且将这些代码放入一个,h( 头 ) 文件中,称为 接口文件
– 成员函数的定义 (实现 ) 放入一个,cpp文件中,称为 实现文件
– 发布该类时,向客户程序员仅提供头文件和实现文件编译后得到的,obj文件
20:43:40
封装的作用
– 将数据和类内部的实现细节 (比如成员函数如何实现等 )
对客户程序员隐藏,使之无法直接访问对象内部的数据,
不必要知道内部的实现细节,只能调用提供的公有成员函数,从而确保对数据的操作是安全的,正确的
– 允许类的创建者修改类的内部实现,只要保持公有的成员函数声明部分不变就不会影响客户程序员使用该类的方法,客户程序员的程序只需重新编译即可使用修改以后的类的功能,而不需要做任何修改
20:43:40
**与封装相关的一些思想:
.数据与操作的集成:与现实世界中真实情况对应
.封装性:尽量隐藏不必让客户程序员知道的细节
.对象是一个自治系统,自己管理自己
.客户程序员只需向对象 发送消息 (向对象提出请求 ),对象就能响应消息,完成任务
-- 从程序设计角度来看,通过调用对象的成员函数向对象发送消息,提出请求面向对象的编程模式接近于现实生活中的实际情况,符合人的思维习惯,思考问题比较自然,
20:43:40
§ 5 友 元
.对象的私有成员只能通过成员函数访问,这样可实现信息隐藏,但使得不同对象协同操作时开销较大或不方便
.为了操作方便,C++提供了一种辅助手段,允许使用关键字
friend声明该类的 友元,友元可以访问类的所有成员,包括私有成员
20:43:40
//Point.cpp
#include,Point.h”
#include <math.h>
void Point::setX(double x)
{X=x;}
void Point::setY(double y)
{Y=y;}
double Point::getX()
{return X;}
double Point::getY()
{return Y;}
//Point.cpp
#include,Point.h”
#include <math.h>
void Point::setX(double x)
{X=x;}
void Point::setY(double y)
{Y=y;}
double Point::getX()
{return X;}
double Point::getY()
{return Y;}
double Distance(Point &a,
Point &b){
double dx=a.X-b.X;
double dy=a.Y-b.Y;
return sqrt(dx*dx+dy*dy);
}
//Point.h
class Point{
double X,Y;
public:
void setX(double x);
void setY(double y);
double getX();
double getY();
};
声明友元的 3种方法
1.普通函数声明成类的友元函数
friend double Distance(
Point &a,Point &b);
};
例:计算两点之间的距离
20:43:40
2.一个类的成员函数声明成另一个类的友元函数
//Demo.h
class B;
class A{
public:
void mFuncOfA(B &bObj,
int x,int y);
};
class B{
int x,y;
public:
int getX();
int getY();
friend void A::mFuncOfA(
B&,int,int);
};
//Demo.cpp
#include "Demo.h"
void A::mFuncOfA(B &bObj,
int x,int y){
bObj.x=x;
bObj.y=y;
}
int B::getX(){return x;}
int B::getY(){return y;}
20:43:40
3.友元类:把类 B声明成类 A的友元:
类 B中的所有成员函数都成为类 A的友元函数
//Demo.h
class A{
friend class B;
public:
void Display();
private:
int x;
};
class B{
public:
void set(int i);
void Display();
private:
A AObject;
};
//Demo.cpp
#include <iostream>
#include "Demo.h"
using namespace std;
void A::Display(){
cout << x << endl;
}
void B::set(int i){
AObject.x=i;
}
void B::Display(){
AObject.Display();
}
20:43:40
注意:
– 友元可在类内部声明或定义,但它不是类的成员,友元函数没有 this指针
– 友元不受访问控制关键字的限制
– 友元破坏了封装性,故应尽可能少用:在逼不得已或程序运行效率极端重要的情况下使用
20:43:40
§ 6 类的静态成员
– 关键字 static在类上的使用,亦即在类中定义成员变量或声明成员函数时,前面加上关键字 static
– 静态成员提供了一种同类对象共享数据的机制
– 对于类的非静态成员,每个对象都包含一份该成员的拷贝
– 类成员声明为 static(静态 )成员时,程序中无论创建该类的多少个对象,内存中都只存在一份该 static成员的拷贝,
亦即 static成员被该类的所有对象共享
20:43:40
此处创建了 counter类的两个对象 a,b,这两个对象共享一份静态成员变量 num
1.静态成员变量相当于作用于类范围内的全局变量,受 public,private
和 protected访问控制关键字的限制
– 例:静态成员变量
//Counter.cpp
int counter::num=0;
void counter::setnum(int i)
{num = i;}
void counter::shownum()
{cout << num <<?\t?; }
//Counter.h
class counter{
static int num;
public:
void setnum(int i);
void shownum();
};
void main(){
counter a,b;
a.shownum(); b.shownum();
a.setnum(10);
a.shownum(); b.shownum();
}
– 说明:
类中出现的静态成员变量只是其声明,还需要在类外对静态成员变量进行 定义,一般把定义放在类的实现文件中
20:43:40
静态成员变量的使用方法
– 类内部成员函数中访问:与普通的成员变量一样
– 类外部访问,在访问控制许可的情况下,有两种方式
1)通过对象名访问 2)通过类名访问例:静态成员变量的使用
//Counter.h
class counter{
public:
static int num;
void setnum(int i);
void shownum();
};
//Counter.cpp
int counter::num=1;
void counter::setnum(int i)
{ num=i; }
void counter::shownum()
{cout << num <<?\t? ;}
//main.cpp
#include,Counter.h”
void main(){
counter a;
int i;
for(i=0;i<5;i++){
a.num+=1;
cout << counter::num <<?\t?;
}
}
20:43:40
2.静态成员函数
– 与静态成员变量一样,属于一个类而不仅仅属于某个对象
– 可以通过对象名或指向对象的指针调用
– 更合理的调用方法则是通过类名调用类名,:静态成员函数名 (实参列表 );
class X{
int DatMem;
public:
static void StaFun(int i,X *ptr){
ptr->DatMem=i;
}
};
void main(){
X obj;
obj.StaFun(10,&obj);
X::StaFun(1,&obj);
}
说明:
静态成员函数没有 this指针,
故不能通过 this直接访问非静态成员变量,也不能通过 this
直接调用非静态成员函数
DatMem=i; //× 错误
20:43:40
§ 7 const与类成员
– 关键字 const在类上的使用
1.const与成员变量把成员变量定义成 const的,将使该成员变量的值在类范围内 (亦即该成员变量的作用范围内 )不能改变例,const的成员变量
//Fred.cpp
Fred::Fred(int sz):size(sz)
{ }
void Fred::Print()
{
cout << size << endl;
}
//Fred.h
class Fred{
const int size;
public:
Fred(int sz);
void Print();
};
说明:
const成员变量不是编译时求值的常量,不能在定义该成员变量的同时赋初值,需在定义构造函数时用 初始化列表 进行初始化,且初始化后对象中该成员变量的值不再变化
20:43:40
例:错误的用法
class Fred{
const int size=100; //错误,不能在此处赋初值
int m_Arr[size]; //错误,size不是编译时求值的常量
public:
Fred(int sz):size(sz){}
void Print(){cout << size << endl;
};
– 如需要仅在类范围内使用编译时常量,可以使用枚举常量例,class Fred
{
enum {sz=100};
int m_Arr[sz];
public:
…
};
20:43:40
2.const与成员函数
–const修饰成员函数的形参与 const修饰普通函数的形参一样,函数体中不能改变形参的值,引用做形参时,就可以保证不会改变实参的值例,const与成员函数
class MbFunc{
int x;
public:
void setX(const int xx);
int getX()const;
void f();
};
void MbFunc::setX(const int xx)
{
x=xx;
}
xx=10; //错误,形参 xx是 const变量
20:43:40
– 不能改变成员变量值的成员函数声明和定义成员函数时在括号后使用 const修饰的函数
int MbFunc::getX() const
{
return x; //正确,读 x的值,并没有使其改变
}
说明:
不能改变成员变量值的成员函数在 声明和定义中二者必须都使用关键字 const
– 常量对象,创建对象时用 const修饰的对象例,const MbFunc Obj;
.常量对象内部成员变量的值不能被改变
.通过常量对象,只能调用不能改变成员变量值的成员函数例,cout << Obj.getX() << endl;
Obj.setX(10); //setX()会改变成员变量 x的值
x=100; //本函数不能改变成员变量的值
20:43:40
1,类和对象,类的创建、对象的创建和使用 --重点 (掌握 )
2,this指针 -- 重点 难点 (掌握 )
3,访问控制、封装与信息隐藏 -- 重点 难点 (理解 )
4,友元 -- 重点
5,静态成员
6,const与类成员
20:43:40
§ 1 概 念程序举例
.银行帐户
.链表操作
20:43:40
存在的问题
– 函数名可能与对其它结构进行操作的函数发生冲突
– 客户程序员易忘记调用 initialize()和 cleanup()
– 数据和对数据进行的操作分离,不符合现实世界中事物的实际情况,也不符合人的思维习惯
– 数据完全对外暴露,无法保证对数据的访问的合法性,安全性
解决方法
把对数据进行操作的函数做成结构体的成员,以解决名字冲突和数据与操作的分离问题例,.修改后的银行帐户,修改后链表操作
将结构体改换为类,并在类上进行访问控制以解决数据外露的问题
例,.银行帐户类,链表类
用类的构造函数与析构函数保证初始化和清除工作一定会进行 --- 第四章
20:43:40
概念:
– 对象,现实世界中事物或实体在计算机中的抽象描述或表示
– 类,对象的抽象描述和概括,从总体上描述了这一类对象拥有的属性 (说明对象的结构 )和具有的行为
– 成员变量,结构体或类内部的变量称为结构体或类的成员变量
– 成员函数,结构体或类内部的函数称为结构体或类的成员函数二者统称为结构体或类的成员
20:43:40
§ 2 类与对象
class 类名
{
private,
私有成员表 ;
public:
公有成员表 ;
protected:
保护成员表 ;
};
例,点类
class Point{
private:
float fX,fY;
public:
void setX(int x){fX=x;}
void setY(int y){fY=y;}
float getX(){return fX;}
float getY(){return fY;}
};
一,类
.类的定义:
20:43:40
说明,
.class:关键字
.类名,需满足标识符的要求
.成员表,若干成员构成的列表成员包括数据成员 (对应于属性 )和函数成员 (对应于行为、
操作 )
数据成员一般是变量或其它类的对象 ----- 成员变量函数成员一般是函数的声明或定义 ----- 成员函数
.private,public,protected:关键字,用于对类中的成员进行访问控制,在类中可以任意次序出现任意多次,分别表示其后的成员为私有成员,公有成员和保护成员
.一般将成员变量定义成私有的,而将成员函数定义成公有的,将提供给客户程序员使用的函数声明成公有的,而将在类内部使用的辅助函数声明成私有的
20:43:40
类的成员函数
.在类中声明或定义的函数
.成员函数定义,两种方法
.在类中定义,创建类的同时,在类中直接给出函数体例,class Point{
float fX,fY;
public:
void setX(int x){fX=x;}
void setY(int y){fY=y;}
float getX( ){ return fX;}
float getY( ){ return fY;}
};
这样的成员函数将成为 内联函数,故其函数体应尽可能简洁
.在类中声明,在类外定义 (实现 ):使用作用范围解析运算符,:
一般把声明放在,h的文件中,该文件称做类的 接口文件而把定义放在,cpp的文件,该文件称做类的 实现文件例,.银行帐户,链表类
20:43:40
二,对象
.对象,把数据和对数据的操作集成在一起形成的独立实体类的变量,一个类的对象是该类的一个实例
.创建 (定义 )对象的一般形式,
类名 对象名列表 ;
例,Point pt1,pt2,*pPtr;
.对象的使用,与结构体变量的使用方法相近
.通过对象名使用对象的成员 (公有成员 ):
对象名,成员名例,pt1.setX(100);
pt1.setY(10);
.通过指向对象的指针使用对象对象指针变量 ->成员名例,pPtr=new Point; //或 pPtr=&pt1;
pPtr->setX(10);
pPtr->setY(20);
20:43:40
对象在内存中的存储
.创建对象时以类为样板,分配内存空间
Point pt1,pt2;
pt1.setX(10);pt1.setY(15);
pt2.setX(100);pt2.setY(12);
15
10
fY
fX
setX()
setY()
getX()
getY()
pt1
12
100
fY
fX
setX()
setY()
getX()
getY()
pt2
getY()代码
getX()代码
setY()代码
setX()代码类 Point的公用区
,创建对象时通常仅分配成员变量的空间,而成员函数的代码由各个对象共享
,逻辑上 每个对象都包含了一份类中定义的成员
20:43:40
§ 3 this指针问题:
类 Account的成员函数中,并没有指明引用的 sName,sID
和 fBalance分别是哪一个对象的成员,若创建了多个该类的对象,比如:
Account acc1,acc2,… ;
如 何 保 证 acc1.Initialize(… ),acc1.Deposit(… ),
acc1.Withdraw(… )和 acc1.Get_balance()操作的是 acc1内部的成员变量,而 acc2.Initialize(… ),acc2.Deposit(… )、
acc2.Withdraw(… )和 acc2.Get_balance()操作的是 acc2中的成员变量?
20:43:40
this指针
– 类的 非静态成员函数 (例如 Account类的 Initialize()等 )
带有一个隐含的参数,该参数就是 this指针,在内部通常它是成员函数的第一个参数
– 通过类的某个对象调用该函数时,this指针 自动 指向该对象,其值 自动 为该对象的地址例如:
调用 acc1.Initialize(“张三,,”S981020”,500);
时 this指针指向对象 acc1,而调用 acc2.Initialize(“李四,,”S981021”,1000);
时 this指针指向对象 acc2
20:43:40
– 成员函数的函数体中出现的对成员的引用,相当于通过该
this指针引用
– this是指针常量,自动指向调用成员函数的对象,函数体中不能改变其值,不能使之指向其它对象例,Account acc2;
this=&acc2;
例:
void Account::Initialize(char *name,char *id,
float amount){
strcpy(sName,name); //? strcpy(this->sName,name);
strcpy(sID,id); //? strcpy(this->sID,id);
fBalance=amount; //? this->fBalance=amount;
}
void Account::Deposit(float amount){
fBalance+=amount; //? this->fBalance+=amount;
}
20:43:40
.protected:保护成员保护成员介于私有成员与公有成员之间,在该类外部不能被访问,但在类内部和派生类中都可以被访问
§ 4 访问控制、封装与信息隐藏
通过三个关键字实现,public,private,protected
三个关键字限定其后的成员分别为公有成员,私有成员和保护成员,直到遇到下一个访问控制关键字或,}”
一,访问控制
.public:公有成员公有成员既可在本类的成员函数中被访问,也可被其它函数访问
.private:私有成员私有成员只能被本类的成员函数访问,其它函数中都不能访问这些成员
C++中类成员缺省的访问控制是 private(私有的 )
--- 类与结构体的唯一区别 ( 结构体成员缺省的访问控制是 public)
三个关键字在类中可以任意次序出现任意多次原则:
一般情况下,成员变量应定义成私有的,成员函数应定义成公有的提供给客户程序员使用的函数声明成公有的,而把在类内部使用的辅助函数声明成私有的
20:43:40
二,封装与信息隐藏
– 访问控制的实质就是在类 (对象 )上设置限制 (明确定义的边界 ),以实现封装,达到隐藏内部信息的目的
– 访问控制实现在类上,但其作用体现在对象上通过访问控制和接口与实现的分离就可以在很大程度上实现信息隐藏
– 除此之外,要实现信息隐藏,还应做到 接口与实现的分离
– 类中只包含成员变量的定义与成员函数的声明,且将这些代码放入一个,h( 头 ) 文件中,称为 接口文件
– 成员函数的定义 (实现 ) 放入一个,cpp文件中,称为 实现文件
– 发布该类时,向客户程序员仅提供头文件和实现文件编译后得到的,obj文件
20:43:40
封装的作用
– 将数据和类内部的实现细节 (比如成员函数如何实现等 )
对客户程序员隐藏,使之无法直接访问对象内部的数据,
不必要知道内部的实现细节,只能调用提供的公有成员函数,从而确保对数据的操作是安全的,正确的
– 允许类的创建者修改类的内部实现,只要保持公有的成员函数声明部分不变就不会影响客户程序员使用该类的方法,客户程序员的程序只需重新编译即可使用修改以后的类的功能,而不需要做任何修改
20:43:40
**与封装相关的一些思想:
.数据与操作的集成:与现实世界中真实情况对应
.封装性:尽量隐藏不必让客户程序员知道的细节
.对象是一个自治系统,自己管理自己
.客户程序员只需向对象 发送消息 (向对象提出请求 ),对象就能响应消息,完成任务
-- 从程序设计角度来看,通过调用对象的成员函数向对象发送消息,提出请求面向对象的编程模式接近于现实生活中的实际情况,符合人的思维习惯,思考问题比较自然,
20:43:40
§ 5 友 元
.对象的私有成员只能通过成员函数访问,这样可实现信息隐藏,但使得不同对象协同操作时开销较大或不方便
.为了操作方便,C++提供了一种辅助手段,允许使用关键字
friend声明该类的 友元,友元可以访问类的所有成员,包括私有成员
20:43:40
//Point.cpp
#include,Point.h”
#include <math.h>
void Point::setX(double x)
{X=x;}
void Point::setY(double y)
{Y=y;}
double Point::getX()
{return X;}
double Point::getY()
{return Y;}
//Point.cpp
#include,Point.h”
#include <math.h>
void Point::setX(double x)
{X=x;}
void Point::setY(double y)
{Y=y;}
double Point::getX()
{return X;}
double Point::getY()
{return Y;}
double Distance(Point &a,
Point &b){
double dx=a.X-b.X;
double dy=a.Y-b.Y;
return sqrt(dx*dx+dy*dy);
}
//Point.h
class Point{
double X,Y;
public:
void setX(double x);
void setY(double y);
double getX();
double getY();
};
声明友元的 3种方法
1.普通函数声明成类的友元函数
friend double Distance(
Point &a,Point &b);
};
例:计算两点之间的距离
20:43:40
2.一个类的成员函数声明成另一个类的友元函数
//Demo.h
class B;
class A{
public:
void mFuncOfA(B &bObj,
int x,int y);
};
class B{
int x,y;
public:
int getX();
int getY();
friend void A::mFuncOfA(
B&,int,int);
};
//Demo.cpp
#include "Demo.h"
void A::mFuncOfA(B &bObj,
int x,int y){
bObj.x=x;
bObj.y=y;
}
int B::getX(){return x;}
int B::getY(){return y;}
20:43:40
3.友元类:把类 B声明成类 A的友元:
类 B中的所有成员函数都成为类 A的友元函数
//Demo.h
class A{
friend class B;
public:
void Display();
private:
int x;
};
class B{
public:
void set(int i);
void Display();
private:
A AObject;
};
//Demo.cpp
#include <iostream>
#include "Demo.h"
using namespace std;
void A::Display(){
cout << x << endl;
}
void B::set(int i){
AObject.x=i;
}
void B::Display(){
AObject.Display();
}
20:43:40
注意:
– 友元可在类内部声明或定义,但它不是类的成员,友元函数没有 this指针
– 友元不受访问控制关键字的限制
– 友元破坏了封装性,故应尽可能少用:在逼不得已或程序运行效率极端重要的情况下使用
20:43:40
§ 6 类的静态成员
– 关键字 static在类上的使用,亦即在类中定义成员变量或声明成员函数时,前面加上关键字 static
– 静态成员提供了一种同类对象共享数据的机制
– 对于类的非静态成员,每个对象都包含一份该成员的拷贝
– 类成员声明为 static(静态 )成员时,程序中无论创建该类的多少个对象,内存中都只存在一份该 static成员的拷贝,
亦即 static成员被该类的所有对象共享
20:43:40
此处创建了 counter类的两个对象 a,b,这两个对象共享一份静态成员变量 num
1.静态成员变量相当于作用于类范围内的全局变量,受 public,private
和 protected访问控制关键字的限制
– 例:静态成员变量
//Counter.cpp
int counter::num=0;
void counter::setnum(int i)
{num = i;}
void counter::shownum()
{cout << num <<?\t?; }
//Counter.h
class counter{
static int num;
public:
void setnum(int i);
void shownum();
};
void main(){
counter a,b;
a.shownum(); b.shownum();
a.setnum(10);
a.shownum(); b.shownum();
}
– 说明:
类中出现的静态成员变量只是其声明,还需要在类外对静态成员变量进行 定义,一般把定义放在类的实现文件中
20:43:40
静态成员变量的使用方法
– 类内部成员函数中访问:与普通的成员变量一样
– 类外部访问,在访问控制许可的情况下,有两种方式
1)通过对象名访问 2)通过类名访问例:静态成员变量的使用
//Counter.h
class counter{
public:
static int num;
void setnum(int i);
void shownum();
};
//Counter.cpp
int counter::num=1;
void counter::setnum(int i)
{ num=i; }
void counter::shownum()
{cout << num <<?\t? ;}
//main.cpp
#include,Counter.h”
void main(){
counter a;
int i;
for(i=0;i<5;i++){
a.num+=1;
cout << counter::num <<?\t?;
}
}
20:43:40
2.静态成员函数
– 与静态成员变量一样,属于一个类而不仅仅属于某个对象
– 可以通过对象名或指向对象的指针调用
– 更合理的调用方法则是通过类名调用类名,:静态成员函数名 (实参列表 );
class X{
int DatMem;
public:
static void StaFun(int i,X *ptr){
ptr->DatMem=i;
}
};
void main(){
X obj;
obj.StaFun(10,&obj);
X::StaFun(1,&obj);
}
说明:
静态成员函数没有 this指针,
故不能通过 this直接访问非静态成员变量,也不能通过 this
直接调用非静态成员函数
DatMem=i; //× 错误
20:43:40
§ 7 const与类成员
– 关键字 const在类上的使用
1.const与成员变量把成员变量定义成 const的,将使该成员变量的值在类范围内 (亦即该成员变量的作用范围内 )不能改变例,const的成员变量
//Fred.cpp
Fred::Fred(int sz):size(sz)
{ }
void Fred::Print()
{
cout << size << endl;
}
//Fred.h
class Fred{
const int size;
public:
Fred(int sz);
void Print();
};
说明:
const成员变量不是编译时求值的常量,不能在定义该成员变量的同时赋初值,需在定义构造函数时用 初始化列表 进行初始化,且初始化后对象中该成员变量的值不再变化
20:43:40
例:错误的用法
class Fred{
const int size=100; //错误,不能在此处赋初值
int m_Arr[size]; //错误,size不是编译时求值的常量
public:
Fred(int sz):size(sz){}
void Print(){cout << size << endl;
};
– 如需要仅在类范围内使用编译时常量,可以使用枚举常量例,class Fred
{
enum {sz=100};
int m_Arr[sz];
public:
…
};
20:43:40
2.const与成员函数
–const修饰成员函数的形参与 const修饰普通函数的形参一样,函数体中不能改变形参的值,引用做形参时,就可以保证不会改变实参的值例,const与成员函数
class MbFunc{
int x;
public:
void setX(const int xx);
int getX()const;
void f();
};
void MbFunc::setX(const int xx)
{
x=xx;
}
xx=10; //错误,形参 xx是 const变量
20:43:40
– 不能改变成员变量值的成员函数声明和定义成员函数时在括号后使用 const修饰的函数
int MbFunc::getX() const
{
return x; //正确,读 x的值,并没有使其改变
}
说明:
不能改变成员变量值的成员函数在 声明和定义中二者必须都使用关键字 const
– 常量对象,创建对象时用 const修饰的对象例,const MbFunc Obj;
.常量对象内部成员变量的值不能被改变
.通过常量对象,只能调用不能改变成员变量值的成员函数例,cout << Obj.getX() << endl;
Obj.setX(10); //setX()会改变成员变量 x的值
x=100; //本函数不能改变成员变量的值
20:43:40