一.类的其它成员 :常成员 、静态成员、友元
1.常成员
(1)常成员
Ø常数据成员为使用const说明的数据成员
如果在一个类中说明了常数据成员,那么构造函数就只能通过初始化列表对该数据成员进行初始化,而任何其他函数都不能对该成员赋值。且被初始化后,其值不能改变。
Ø在类的成员函数说明后面可以加const关键字,则该成员函数成为常量成员函数
class Mclass
{
public :
int k;
const int M; //说明常数据成员
Mclass() : M(5) { } //用初始化对常数据成员赋值
void testFun()
{ //M++; //错误,不能在成员函数中修改常数据成员
k++; //可以修改一般数据成员
}
} ;
int main()
{ Mclass t1, t2;
t1.k=100;
//t1.M=123; //错误,不能在类外修改常数据成员
struct Date { int year, month, day ; }; //结构
class Student
{
public:
Student (int y,int m,int d, int num=0, sring name="no name");
void PrintStudent()const; //常成员函数
private:
const int code; //常数据成员
string name;
Date birthday; //结构数据成员
};
带参数的构造函数完成对数据成员的初始化;
Student::Student( int y, int m, int d, int num, string name ) : code( num )
{ this->name=name;
birthday.year=y;
birthday.month=m;
birthday.day=d;
}
(2)常对象:如果在说明对象时用const修饰,则被说明的对象为常对象。
- 常对象的说明形式如下: 类名 const对象名[(参数表)]; 或者 const 类名 对象名[(参数表)];
说明:
(1) 在定义常对象时必须进行初始化,而且不能被更新。C++不允许直接或间接更改常对象的数据成员。
(2)C++规定常对象只能调用它的常成员函数、静态成员函数、构造函数(具有公有访问权限)。
class T_class
{ public:
int a, b;
T_class( int i, int j )
{ a=i; b=j;}
} ;
int main()
{ const T_class t1( 1, 2 ); //常对象;
T_class t2( 3, 4 ); //一般对象;
//t1.a=5;
//t1.b=6; //常对象的数据成员不能更改;
t2.b=8; //a和b为公有数据成员;
t2.a=7;
cout<<"t1.a="<<t1.a<<'\t'<<"t1.b="<<t1.b<<endl;
cout<<"t2.a="<<t2.a<<'\t'<<"t2.b="<<t2.b<<endl;
} //编译结果为t1.a=1 t1.b=2
t2.a=7 t2.b=8
(3)常成员函数:在类中使用关键字const说明的函数为常成员函数
- 常成员函数的说明格式如下: 类型说明符 函数名(参数表) const;
- const是函数类型的一个组成部分,因此在函数的实现部分也要带关键字const。
- 常成员函数不能更新对象的数据,也不能调用非const修饰的成员函数(静态成员函数、构造函数除外)
2.静态成员
Ø类成员冠以static声明时,称为静态成员。
Ø 静态数据成员为同类对象共享。在定义或说明前加static,如:static int s;
Ø 静态成员函数与静态数据成员协同操作。
•静态成员不属于某一个单独的对象,而是为类的所有对象所共有
•静态成员函数的作用不是为了对象之间的沟通,而是为了能处理静态数据成员:保证在不依赖于某个对象的情况下,访问静态数据成员
- 对于类的普通数据成员,每一个对象都各自拥有一个副本。(分配不同的存储空间)
- 对于静态数据成员,每个类只拥有一个副本。(在静态存储区分配一个存储空间,对所有对象都是可见的)
- 公有访问权限的静态成员,可以通过下面的形式进行访问
- 类名::静态成员的名字
- 对象名.静态成员名字
- 对象指针->静态成员的名字
- 在静态成员函数内部,直接访问。
(1)静态数据成员声明及初始化
在类外进行静态数据成员的声明:类型 类名::静态数据成员[=初始化值]; //必须进行声明
- 不能在成员初始化列表中进行初始化
- 如果未进行初始化,则编译器自动赋初值(默认值是0)
- 初始化时不能使用访问权限
(2)静态成员函数
- 静态函数仅可以访问静态成员,主要用来访问同一类中的静态数据成员。
静态成员函数不访问类中的非静态数据成员。如有需要,只能通过对象名(或指向对象的指针)访问该对象的非静态成员。
- 静态成员函数和静态数据成员一样,它们都属于类的静态成员,它们都不是对象成员。因此,对静态成员的引用不需要用对象名。
- 静态成员函数没有this指针,只能对静态数据操作
定义静态成员函数的格式如下: static 返回类型 静态成员函数名(参数表); 在类外定义时不用static前缀。
与静态数据成员类似,调用公有静态成员函数的一般格式有如下几种:
类名::静态成员函数名(实参表)
对象. 静态成员函数名(实参表)
对象指针->静态成员函数名(实参表)
说明:
- 私有静态成员函数不能在类外部或用对象访问。
- 可以在建立对象之前处理静态数据成员。
- 编译系统将静态成员函数限定为内部连接(在其他文件中不可见)。
二.友元函数
如果在本类(类A)以外的其他地方定义了一个函数(函数B),这个函数可以是不属于任何类的非成员函数,也可以是其他类的成员函数,在类体中用friend对其(函数B)进行声明,此函数就称为本类(类A)的友元函数。友元函数(函数B)可以访问这个类(类A)中的私有成员。
三.类的包含(类的组合)
Ø当一个类中含有已经定义的类类型成员,带参数的构造函数对数据成员初始化,须使用初始化语法形式。
构造函数 ( 形参表 ) : 对象成员1(形参表 ) , … , 对象成员n (形参表 ) ;
(1)对象成员的初始化
- 出现成员对象时,该类的构造函数要包含对象成员的初始化。如果构造函数的成员初始化列表没有对成员对象初始化时,则使用成员对象的无参(缺省)构造函数。
- 建立一个类的对象时,要先执行成员对象自己的构造函数,再执行当前类的构造函数。
-
成员对象的构造函数调用次序和成员对象在类中的说明次序一致(声明顺序为:a1、b1、b2),与它们在成员初始化列表中出现的次序无关(初始化列表顺序为:b1、b2、a1)。
-
析构函数的调用顺序相反
四.对象数组:指每一数组元素都是对象的数组。
- 定义一个一维对象数组的格式如下: 类名 数组名[下标表达式];
- 对象数组的初始化:
(1):当对象数组所属的类中包含带参的构造函数,可用初始化列表完成对象数组的初始化。
#include <iostream>
using namespace std;
class Point
{
int x, y;
public:
Point(){x=y=0;}
Point(int xi,int yi)
{ x=xi;y=yi; }
Point (int c)
{ x=y=c; }
void Print()
{ cout<<"("<<x<<","<<y<<")"; }
};
int main(){ //熟知数组的几种方式;
Point p1[4];
Point p2[4]={5,6,7,8};
Point p3[4]={Point(9,10),Point(11,12),Point(13,14),Point(15,16)};
Point p4[4]={Point(17,18),Point(10,20)};
Point p5[2][2]={21,22,Point(23,24)};
for(int i=0;i<4;i++) p1[i].Print();
cout<<endl;
for(int i=0;i<4;i++) p2[i].Print();
cout<<endl;
for(int i=0;i<4;i++) p3[i].Print();
cout<<endl;
for(int i=0;i<4;i++) p4[i].Print();
cout<<endl;
for(int i=0;i<2;i++)
for( int j=0;j<2;j++) p5[i][j].Print();
cout<<endl;
return 0;
}
编译结果为:
(0,0)(0,0)(0,0)(0,0)
(5,5)(6,6)(7,7)(8,8)
(9,10)(11,12)(13,14)(15,16)
(17,18)(10,20)(0,0)(0,0)
(21,21)(22,22)(23,24)(0,0)
(2):当对象数组所属的类中包含无参的构造函数,也可以先定义,再给每个数组元素赋值
int main()
{
Point p1[4];
p1[0]=Point(1,1);
}
(3): 当对象数组所属的类中包含单个参数的构造函数,可简写。
eg:Point p2[4]={5,6,7,8};
//Pointp2[4]={Point(5), Point(6), Point(7), Point(8)};
(4)无法通过初始化成员列表对对象数组进行初始化
错误举例:
class A
{
public:
A(int i = 0):x(i) {}
int x;
};
class B
{
public:
B():a[0](0),a[1](1) {} // error C2059: 语法错误 : “[”
A a[2];
};
正确举例:
class B
{
public:
B() //成员对象数组已经通过A的默认构造函数初始化了,下面是进行赋值
{
a[0] = A(0);
a[1] = A(1);
}
A a[2];
};