类与对象的成员

相比于一般变量和函数,在类中定义的成员更加复杂,本文主要讨论以下七种对象的成员,包括内联函数、常数据成员、常成员函数、静态数据成员、静态成员函数、静态常数据成员和友元函数。

首先看内联函数,内联函数主要是用于函数程序优化,展开代码而不是调用。详细来讲就是对于很多比较短的代码,如果经常需要执行的话,每次都要通过函数调用的方式去执行的话是很不经济的,CPU用于处理函数调用过程的时间要比执行函数体本身还长,内联函数能在调用的地方直接展开代码(这点和C语言中宏实现的函数很类似),从而减少调用函数的开销。但使用内联函数需要注意以下几点:

1.必须在函数定义前加上inline关键字,仅仅在函数原型前加上inline无效;

2.类定义中中给出函数体的成员函数自动成为内联函数,即直接在类中定义的函数;

3.函数体内代码量很大或者在循环体内尽量不使用内联,否则代码量急剧膨胀;

4.构造、析构函数尽量不要使用内联函数;

5.inline关键字仅作为对编译器的提示,具体实现由编译器决定;

6.编译器必须能看见内联函数才能在代码编译期展开,内联函数必须实现在头文件中。

// 示例:inline 函数名称(参数)

inline void Circle::GetOrigin( double * x, double * y )

{

*x = this->x;

*y = this->y;

}

inline void Circle::SetOrigin( double x, double y )

{

this->x = x;

this->y = y;

}

类的定义里除了可以带有变量之外,还可以带有常量,这就是常数据成员。在对象的生命期内,常数据成员的值不能被改变,所以对于常数据成员而言只有初始化而不能被赋值,对它设定值的唯一机会只能通过构造函数的初始化列表完成。

// 示例:const 类型 数据成员名称

class A

{

public:

A( int a );

private:

const int num;

};

A::A( int a ) : num(a) { …… };    // 只能通过初始化列表完成初始化

与常数据成员相联系的成员是常成员函数,如果某个成员函数是不会修改成员的值,此时就应该使用常成员函数。需要注意的是,声明常成员函数和定义常成员函数都需要在函数的参数列表后加上const,只有二者匹配才可以,否则编译器会认为他们是两个不同的函数。除此之外,常成员函数还需要注意的是:

1.常成员函数不能调用非常成员函数,因为非常成员函数有可能修改数据成员的的值;

2.静态成员函数不能定义为常成员函数;

3.如果对象定义为常量,则只能调用其常成员函数。

// 示例:类型 成员函数名称(参数) const

class Circle{

public:

double GetArea() const;

……

};

double Circle::GetArea() const{ …… }    // 只能访问常数据成员

接下来是静态数据成员,在C语言中静态全局变量被本模块内任何函数所共享,不过在C++中,静态数据成员是被所有的对象所共享,这点和普通的数据成员有很大区别。虽然所有对象都可以共享静态数据成员,但它不隶属于任何一个对象,而是定义该静态数据成员的类的一个属性。所以有以下几点需要注意:

1.静态数据成员不会在对象上分配空间,而是在静态区定义一份;

2.定义的格式是“类型  类名称::静态数据成员名 = 初始值”;

3.初始化工作只能在类外部进行,初始化动作与访问控制无关;

4.静态数据成员初始化工作应放在源文件中,而非头文件中。

// 示例:static 类型 静态数据成员名称

class A

{

private:

static int count;

};

int A::count = 0;    // 只能在类外初始化

正如常数据成员有常成员函数相对应,静态数据成员也有对应的静态成员函数。与静态数据成员类似,静态成员函数在类上定义而不是在对象上调用。因为静态成员函数的存在主要就是为了访问静态数据成员而存在的,所以可以直接访问某个类中的静态数据成员,但因为它隶属于类而非对象,所以它没有this指针去指向某个对象的其他变量,如要完成对其他数据成员的访问需要专门指定对象或者指向对象的指针。

// 示例:static 成员函数名称(参数)

// test.h

class A

{

public:

static int f();

static int g( const A & a );

private:

static int count;

int num;

};

// test.c

int A::count = 0;    // 只能在类外初始化

int A::f()    // 通过类名定义静态成员函数

{

return count;

}

int A::g( const A & a )

{

return a.num;    // 访问非静态数据成员必须指定对象

}

这里先停下来做一个小小的总结和对比:常数据成员和常成员变量、静态数据成员和静态成员变量。常函数是针对常数据的访问,静态函数式针对静态数据的访问这边二者是相同的。但常数据和常函数属于对象,静态数据和静态函数属于类,这点二者有所区别。

静态常数据成员混合了常数据成员和静态数据成员的特征,值不仅在程序运行期间不可以改变,且每个类只有一份,其定义和静态数据成员一样,只能在类外部进行初始化。静态常数据成员不仅在类中声明时候需要加上const,在类外定义时也不能省略const关键字,而static只需要在类的数据成员声明时写上即可,在定义时候不需要再加上。

// static const  类型  数据成员名称

class A

{

private:

static const int count;

};

const int A::count = 10;    // 只能类外定义,且必须加上const

最后需要说的类的成员是友元函数和友元类。在类中,被定义成private权限的变量和函数都是该类私有的,即使是派生类都不能直接访问,但是友元却能访问这些私有数据,它打破了数据封装和信息隐藏,所以友元需要慎重使用。友元函数和友元类具有以下特点:

1.友元可以是函数、其他类成员函数,也可以是类;

2.友元可以访问类的私有与保护成员;

3.两个类的友元关系不可逆,除非互为友元;

4.友元的声明不涉及访问控制,无论声明在那个访问控制之下均不影响。

// 示例:friend 函数或类声明

class Circle

{

friend double Get_Radius();

friend class Globe;  // 将Globe类所有成员函数声明为友元

private:

double radius;

};

回顾一下本文的主要内容,其中inline关键字主要是用来提示编译器将内联函数直接展开而非通过函数调用方式来减少开销。常数据成员和静态数据成员是最为核心的两个内容,分别是常数据和共享数据,最核心的两点是常数据成员必须通过初始化列表区初始化而不能通过赋值方式,静态数据成员只能隶属于类而非对象,只能在类外初始化。常成员函数和静态成员函数对应的是对上面那两种数据的操作。最后友元函数和友元类等则是提供了类外访问私有数据和函数的方法,但在使用过程中需要注意,因为其破坏了类数据封装与信息隐藏。

Advertisements