i+=10;other();
return0;
}
运行结果:
例5_3具有静态和动态生存期对象的时钟程序
//例5_3具有静态和动态生存期对象的时钟程序.cpp
#include
usingnamespacestd;
classClock{//时钟类定义
public:
//外部接口
Clock();
voidsetTime(intnewH,intnewM,intnewS);//三个形参均具有函数原型作用域
voidshowTime();
private:
//私有数据成员
inthour,minute,second;
};
//时钟类成员函数实现
Clock:
:
Clock():
hour(0),minute(0),second(0){}//构造函数
voidClock:
:
setTime(intnewH,intnewM,intnewS){//三个形参均具有局部作用域
hour=newH;
minute=newM;
second=newS;
}
voidClock:
:
showTime(){
cout<"<"<}
ClockglobClock;//声明对象globClock,具有静态生存期,命名空间作用域
//由缺省构造函数初始化为0:
0:
0
intmain(){//主函数
cout<<"Firsttimeoutput:
"<//引用具有命名空间作用域的对象globClock:
globClock.showTime();
//对象的成员函数具有类作用域
//显示0:
0:
0
globClock.setTime(8,30,30);//将时间设置为8:
30:
30
ClockmyClock(globClock);
//声明具有局部作用域的对象myClock
//调用拷贝构造函数,以globClock为初始值
cout<<"Secondtimeoutput:
"<myClock.showTime();
//引用具有局部作用域的对象myClock
//输出8:
30:
30
return0;
}
运行结果:
5.3 类的静态成员
5.3.1 静态数据成员
●静态数据成员
■用关键宇static声明
■为该类的所有对象共享,静态数据成员具有静态生存期。
■必须在类外定义和初始化,用(:
:
)来指明所属的类。
●例5-4具有静态数据成员的Poinr类
//例5_4具有静态数据成员的Poinr类.cpp(P155)
#include
usingnamespacestd;
classPoint{//Point类定义
public:
//外部接口
Point(intx=0,inty=0):
x(x),y(y){//构造函数
count++;//在构造函数中对count累加,所有对象共同维护同一个count
}
Point(Point&p){//拷贝构造函数
x=p.x;
y=p.y;
count++;
}
~Point(){count--;}
intgetX(){returnx;}
intgetY(){returny;}
voidshowCount(){//输出静态数据成员
cout<<"Objectcount="<}
private:
//私有数据成员
intx,y;
staticintcount;//静态数据成员声明,用于记录点的个数
};
intPoint:
:
count=0;//静态数据成员定义和初始化,使用类名限定
intmain(){//主函数
Pointa(4,5);//定义对象a,其构造函数回使count增1
cout<<"PointA:
"<a.showCount();//输出对象个数
Pointb(a);//定义对象b,其构造函数回使count增1
cout<<"PointB:
"<b.showCount();//输出对象个数
return0;
}
运行结果:
5.3.2 静态函数成员
●类外代码可以使用类名和作用域操作符来调用静态成员函数。
●静态成员函数主要用于处理该类的静态数据成员,可以直接调用静态成员函数。
●如果访问非静态成员,要通过对象来访问。
例5-5具有静态数据、函数成员的Point类
//例5_5具有静态数据、函数成员的Point类源.cpp(P157)
#include
usingnamespacestd;
classPoint{//Point类定义
public:
//外部接口
Point(intx=0,inty=0):
x(x),y(y){//构造函数
//在构造函数中对count累加,所有对象共同维护同一个count
count++;
}
Point(Point&p){//拷贝构造函数
x=p.x;
y=p.y;
count++;
}
~Point(){count--;}
intgetX(){returnx;}
intgetY(){returny;}
staticvoidshowCount(){//静态函数成员
cout<<"Objectcount="<}
private:
//私有数据成员
intx,y;
staticintcount;//静态数据成员声明,用于记录点的个数
};
intPoint:
:
count=0;//静态数据成员定义和初始化,使用类名限定
intmain(){//主函数
Pointa(4,5);//定义对象a,其构造函数回使count增1
cout<<"PointA:
"<Point:
:
showCount();//输出对象个数
Pointb(a);//定义对象b,其构造函数回使count增1
cout<<"PointB:
"<Point:
:
showCount();//输出对象个数
return0;
}
运行结果:
5.4 类的友元
友元是C++提供的一种破坏数据封装和数据隐藏的机制。
通过将一个模块声明为另一个模块的友元,一个模块能够引用到另一个模块中本是被隐藏的信息。
可以使用友元函数和友元类。
为了确保数据的完整性,及数据封装与隐藏的原则,建议尽量不使用或少使用友元。
5.4.1友元函数
友元函数是在类声明中由关键字friend修饰说明的非成员函数,在它的函数体中能够通过对象名访问 private 和 protected成员
作用:
增加灵活性,使程序员可以在封装和快速性方面做合理选择。
访问对象中的成员必须通过对象名。
例5-6 使用友元函数计算两点间的距离
//例5_6使用友元函数计算两点间的距离.cpp(P160)
#include
#include
usingnamespacestd;
classPoint{//Point类定义
public:
//外部接口
Point(intx=0,inty=0):
x(x),y(y){}
intgetX(){returnx;}
intgetY(){returny;}
friendfloatdist(Point&p1,Point&p2);//友元函数声明
private:
//私有数据成员
intx,y;
};
floatdist(Point&p1,Point&p2){//友元函数实现
doublex=p1.x-p2.x;//通过对象访问私有数据成员
doubley=p1.y-p2.y;
returnstatic_cast(sqrt(x*x+y*y));
}
intmain(){//主函数
Pointmyp1(1,1),myp2(4,5);//定义Point类的对象
cout<<"Thedistanceis:
";
cout<return0;
}
运行结果:
5.4.2友元类
若一个类为另一个类的友元,则此类的所有成员都能访问对方类的私有成员。
声明语法:
将友元类名在另一个类中使用friend修饰说明。
classA{
friendclassB;
public:
voiddisplay(){
cout<}
private:
intx;
};
classB{
public:
voidset(inti);
voiddisplay();
private:
Aa;
};
voidB:
:
set(inti){
a.x=i;
}
voidB:
:
display(){
a.display();
}
类的友元关系是单向的
如果声明B类是A类的友元,B类的成员函数就可以访问A类的私有和保护数据,但A类的成员函数却不能访问B类的私有、保护数据。
5.5共享数据的保护
● 对于既需要共享、又需要防止改变的数据应该声明为常类型(用const进行修饰)。
● 对于不改变对象状态的成员函数应该声明为常函数。
常类型
● 5.5.1常对象:
必须进行初始化,不能被更新。
■ const类名对象名
● 5.5.2常成员
■ 用const进行修饰的类成员:
常数据成员和常函数成员
● 5.5.3常引用:
被引用的对象不能被更新。
■ const 类型说明符 &引用名
● 常数组:
数组元素不能被更新(详见第6章)。
■ 类型说明符 const 数组名[大小]...
● 常指针:
指向常量的指针(详见第6章)。
常对象
● 用const修饰的对象
● 例:
classA
{
public:
A(inti,intj){x=i;y=j;}
...
private:
intx,y;
};
Aconsta(3,4);//a是常对象,不能被更新
● 思考:
哪些操作有试图改变常对象状态的危险?
常成员
● 用const修饰的对象成员
● 常成员函数
■ 使用const关键字说明的函数。
■ 常成员函数不更新对象的数据成员。
■ 常成员函数说明格式:
类型说明符 函数名(参数表)const;
这里,const是函数类型的一个组成部分,因此在实现部分也要带const关键字。
■ const关键字可以被用于参与对重载函数的区分
● 通过常对象只能调用它的常成员函数。
●常数据成员
■ 使用const说明的数据成员。
例5-7 常成员函数举例
//例5_7常成员函数举例.cpp(P164)
#include
usingnamespacestd;
classR{
public:
R(intr1,intr2):
r1(r1),r2(r2){}
voidprint();
voidprint()const;
private:
intr1,r2;
};
voidR:
:
print(){
cout<"<}
voidR:
:
print()const{
cout<}
intmain(){
Ra(5,4);
a.print();//调用voidprint()
constRb(20,52);
b.print();//调用voidprint()const
return0;
}
运行结果:
例5-8 常数据成员举例
//例5_8常数据成员举例.cpp(P165)
#include
usingnamespacestd;
classA{
public:
A(inti);
voidprint();
private:
constinta;
staticconstintb;//静态常数据成员
};
constintA:
:
b=10;//静态常数据成员在类外说明和初始化
//常数据成员只能通过初始化列表来获得初值
A:
:
A(inti):
a(i){}
voidA:
:
print(){
cout<"<
}
运行结果:
常引用
● 如果在声明引用时用const修饰,被声明的引用就是常引用。
● 常引用所引用的对象不能被更新。
● 如果用常引用做形参,便不会意外地发生对实参的更改。
常引用的声明形式如下:
■ const 类型说明符 &引用名;
例5-9 常引用作形参
//例5_9常引用作形参.cpp(P167)
#include
#include
usingnamespacestd;
classPoint{//Point类定义
public:
//外部接口
Point(intx=0,inty=0):
x(x),y(y){}
intgetX(){returnx;}
intgetY(){returny;}
friendfloatdist(constPoint&p1,constPoint&p2);
private:
//私有数据成员
intx,y;
};
floatdist(constPoint&p1,constPoint&p2){//常引用作形参
doublex=p1.x-p2.x;
doubley=p1.y-p2.y;
returnstatic_cast(sqrt(x*x+y*y));
}
intmain(){//主函数
constPointmyp1(1,1),myp2(4,5);//定义Point类的对象
cout<<"Thedistanceis:
";
cout<return0;
}
运行结果:
5.6多文件结构和编译预处理命令
注意:
视频中例5-10程序代码有误,请看下面的更正:
Point(intx=0,inty=0):
x(x).y(y){}更正为
Point(intx=0,inty=0):
x(x),y(y){count++;}
5.6.1c++程序的一般组织结构
●一个工程可以划分为多个源文件
■类声明文件(.h)
■类实现文件(.cpp)
■类的使用文件(main()所在的.cpp文件)
●利用工程来组合各个文件
例5-10多文件的工程
//文件1,类的定义,Point.h
classPoint{//类的定义
public:
//外部接口
Point(