转 第13章、类

类定义包含两部分:
类头,由关键字 class 及其后面的类名构成
类体,由一对花括号包围起来
类定义后面必须接一个分号或一列声明。例如:
class Screen { /* ... */ };
class Screen { /* ... */ } myScreen, yourScreen;
在类体中,对类的数据成员和成员函数进行声明,并指定这些类成员的访问级别。类体定义了类成员表
类体定义了一个域,在类体中的类成员声明把这些成员名字引入到它们的类的域中。如果两个类有同名的成员,那么程序不会出错,并且这两个成员将指向不同的对象
在引入类类型之后我们可以以两种方式引用这种类类型
1 指定关键字class,后面紧跟类名
2 只指定类名
1.1 、数据成员
    数据成员的声明看起来很像在块域或名字空间域中的变量声明。但是, 除了静态( static 数据成员外,数据成员不能在类体中被显式地初始化。例如:
class First {
int memi = 0; // 错误
double memd = 0.0; // 错误
};
类的数据成员通过类的构造函数进行初始化
1.2 、成员函数
类的成员函数被声明在类体中,成员函数的声明看起来像是名字空间域中所出现的函数声明,成员函数的定义也可以被放在类体内
成员函数与普通函数的不同:
(1)成员函数被声明在它的类中,这意味着该成员函数名在类域之外是不可见的。我们可以通过“点. 或箭头-> 成员访问操作符”引用成员函数
(2)成员函数拥有访问该类的公有和私有成员的特权,而一般来说,普通函数只能访问类的公有成员
成员函数可以是重载的函数重载函数,但是,一个成员函数只能重载自己类的其他成员函数,一个类的成员函数与在其他类或名字空间中声明的函数无关,因此也不能重载它们
1.3 、成员访问
信息隐藏是为了防止程序的函数直接访问类类型的内部表示而提供的一种形式化机制。类成员的访问限制是通过类体内被标记为public、private以及protected的部分来指定的
公有成员在程序的任何地方都可以被访问。实行信息隐藏的类将其public 成员限制在成员函数上,这种函数定义了可以被一般程序用来操纵该类类型对象的操作
私有成员只能被成员函数和类的友元访问,实行信息隐藏的类把其数据成员声明为private
被保护成员对派生类就像public 成员一样,对其他程序则表现得像private
一个类可以包含多个public、private、protected区。每个区一直有效,直到另一个区标签或类体的结束右括号出现为止。如果没有指定访问限定符,则缺省情况下,在类体的开始左括号后面的区是private 区
1.4 、友元
    在某些情况下,允许某个函数而不是整个程序可以访问类的私有成员,这样做会比较方便。友元机制允许一个类授权其他的函数访问它的非公有成员
友元声明以关键字 friend 开头,它只能出现在类的声明中。由于友元不是授权友谊的类的成员。所以它们不受其在类体中被声明的 public private protected 区的影响
一个友元或许是一个名字空间函数、另一个前面定义的类的一个成员函数,也可能是一个完整的类。在使一个类成为友元时,友元类的所有成员函数都被给予访问“授权友谊的类的非公有成员”的权力
1.5 、类声明和类定义
    一旦到了类体的结尾,即结束右括号,我们就说一个类被定义了一次。一旦定义了一个类,则该类的所有成员就都是已知的,类的大小也是已知的了
我们也可以声明一个类但是并不定义它。例如:
class Screen; // Screen 类的声明
这个声明向程序引入了一个名字Screen,指示Screen 为一个类类型
但是我们只能以有限的方式使用已经被声明但还没有被定义的类类型。如果没有定义类,那么我们就不能定义这类类型的对象,因为类类型的大小不知道,编译器不知道为这种类类型的对象预留多少存储空间
但是,我们可以声明指向该类类型的指针或引用。允许指针和引用是因为它们都有固定的大小,这与它们指向的对象的大小无关。但是,因为该类的大小和类成员都是未知的,所以要等到完全定义了该类,我们才能将解引用操作符* 应用在这样的指针上,或者使用指针或引用来指向某一个类成员
因为只有当一个类的类体已经完整时,它才被视为已经被定义,所以一个类不能有自身类型的数据成员。但是当一个类的类头被看到时,它就被视为已经被声明了,所以一个类可以用指向自身类型的指针或引用作为数据成员
2 、类对象
类的定义不会引起存储区分配,只有当定义一个类的对象时系统才会分配存储区
类类型的对象有一个域,它是由对象定义在程序文本文件中的位置决定的。一个类的对象可能被定义在一个与“类类型被定义的域”不同的域中
一个对象可以被同一类类型的另一个对象初始化或赋值。缺省情况下拷贝一个类对象与拷贝它的全部数据成员等价。例如:
Screen bufScreen = myScreen;
// bufScreen._height = myScreen._height
// bufScreen._width = myScreen._width
// bufScreen._cursor = myScreen._cursor
缺省情况下,当一个类对象被指定为函数实参或函数返回值时,它就被按值传递。我们也可以把一个函数参数或返回类型声明为一个类类型的指针或引用
我们必须用成员访问操作符来访问类对象的数据成员或成员函数,点成员访问操作符.与类对象或引用联用,箭头访问操作符-> 与类对象的指针联用
3 、类成员函数
类的成员函数是一组操作的集合,用户可以在该类的对象上执行这些操作
3.1 、inline 和非inline 成员函数
在类定义中定义的函数被自动作为inline 函数处理
一两行以上的成员函数最好被定义在类体之外。这要求一个特殊的声明语法,来标识一个函数是一个类的成员:成员函数名必须被它的类名限定修饰qualified
成员函数必须先在其类体内被声明,而且类体必须在成员函数被定义之前先出现
通常,在类体外定义的成员函数不是inline 的。但是,这样的函数也可以被声明为inline函数,可以通过显式地在类体中出现的函数声明上使用关键字inline,或者通过在类体外出现的函数定义上显式使用关键字inline,或者两者都用
由于内联函数必须在调用它的每个文本文件中,被定义所以没有在类体中定义的内联成员函数必须被放在类定义出现的头文件中
3.2 、访问类成员
    无论成员函数是在类体内还是外面,我们都说它在类域内。这有两个含义:
(1)成员函数的定义可以引用任何一个类成员,无论该成员是私有的还是公有的,都不会破坏类访问限制
(2)成员函数可以直接访问它所属的类的成员,而无需使用点或箭头成员访问操作符
3.3 、特殊的成员函数
    有一组特殊的成员函数可以管理类对象并处理诸如初始化、赋值、内存管理、类型转换以及析构等活动,这些函数通常由编译器隐式调用
初始化成员函数被称为构造函数,每次定义一个类对象或由new 表达式分配一个类对象时都会调用它。构造函数的名字必须与类名相同
3.4 、const 成员函数
通常,程序中任何试图修改const 对象的动作都会被标记为编译错误
为尊重类对象的常量性,编译器必须区分不安全与安全的成员函数(即区分试图修改类对象与不试图修改类对象的函数)
类的设计者通过把成员函数声明为const,以表明它们不修改类对象。例如:
class Screen {
public:
char get() const { return _screen[_cursor]; }
// ...
}
只有被声明为const 的成员函数才能被一个const 类对象调用。关键字const 被放在成员函数的参数表和函数体之间。对于在类体之外定义的const 成员函数,我们必须在它的定义和声明中同时指定关键字const
const成员函数可以被相同参数表的非const 成员函数重载
构造函数和析构函数是两个例外,即使构造函数和析构函数不是const 成员函数,const类对象也可以调用它们。当构造函数执行结束、类对象已经被初始化时,类对象的常量性就被建立起来了。析构函数一被调用,常量性就消失。所以一个const 类对象从构造完成时刻到析构开始时刻这段时间内被认为是const
4 、隐含的this 指针
每个类的成员函数都含有一个指向被调用对象的指针
要想理解这一点,一种方法是看一看编译器是怎样实现this指针的。为支持this 指针,必须要应用两个转变:
(1)改变类成员函数的定义,用额外的参数;this指针,来定义每个成员函数
(2)改变每个类成员函数的调用,加上一个额外的实参——被调用对象的地址
5 、静态类成员
静态数据成员被当作该类类型的全局对象。对于非静态数据成员,每个类对象都有自己的拷贝,而静态数据成员对每个类类型只有一个拷贝。静态数据成员只有一份,由该类类型的所有对象共享访问
同全局对象相比,使用静态数据成员有两个优势:
(1)静态数据成员没有进入程序的全局名字空,间因此不存在与程序中其他全局名字冲突的可能性
(2)可以实现信息隐藏,静态成员可以是private 成员,而全局对象不能
一般地,静态数据成员在该类定义之外被初始化。如同一个成员函数被定义在类定义之外一样,在这种定义中的静态成员的名字必须被其类名限定修饰。例如,下面是interestRate的初始化
// 静态类成员的显式初始化
#include "account.h"
double Account::_interestRate = 0.0589;
与全局对象一样,对于静态数据成员,在程序中也只能提供一个定义。这意味着, 静态数据成员的初始化不应该被放在头文件中,而应该放在含有类的非 inline 函数定义的文件中
静态数据成员的惟一性本质(独立于类的任何对象而存在的惟一实例),使它能够以独特的方式被使用,这些方式对于非static 数据成员来说是非法的
(1)静态数据成员的类型可以是其所属类,而非static 数据成员只能被声明为该类的对象的指针或引用例
(2)静态数据成员可以被作为类成员函数的缺省实参,而非static 成员不能
5.1 、静态成员函数
     静态成员函数的声明除了在类体中的函数声明前加上关键字 static ,以及不能声明为 const volatile 之外,与非静态成员函数相同,出现在类体外的函数定义不能指定关键字 static
静态成员函数没有 this 指针,因此在静态成员函数中隐式或显式地引用这个指针都将导致编译时刻错误。试图访问隐式引用 this 指针的非静态数据成员也会导致编译时刻错误
我们可以用成员访问操作符点. 和箭头-> 为一个类对象或指向类对象的指针调用静态成员函数,也可以用限定修饰名直接访问或调用静态成员函数,而无需声明类对象
6 、指向类成员的指针
6.1 、类成员的类型
函数指针不能被赋值为成员函数的地址,即使返回类型和参数表完全匹配
成员函数有一个非成员函数不具有的属性——它的类。指向成员函数的指针必须与向其赋值的函数类型匹配,不是两个而是三个方面都要匹配:
1 )参数的类型和个数
2 )返回类型
3 )它所属的类类型
函数指针存储函数的地址,可以被用来直接调用那个函数。
成员函数指针首先必须被绑定在一个对象或者一个指针上,才能得到被调用对象的this 指针,然后才调用指针所指的成员函数。虽然普通函数指针和成员函数指针都被称作指针,但是它们是不同的事物
成员函数指针的声明要求扩展的语法,它要考虑类的类型。对指向类数据成员的指针也是这样
在数据成员指针和普通指针之间的不匹配也是由于这两种指针的表示上的区别。普通指针含有引用一个对象所需的全部信息。数据成员指针在被用来访问数据成员之前,必须先被绑定到一个对象或指针
定义一个成员函数指针需要指定函数返回类型、参数表和类。例如指向Screen 成员函数并且能够引用成员函数height()和width()的指针类型如下:
int (Screen::*) ()
这种类型指定了一个指向类Screen 的成员函数的指针,它没有参数,返回值类型为int
6.2 、使用指向类成员的指针
类成员的指针必须总是通过特定的对象或指向该类类型的对象的指针来访问。我们通过使用两个指向成员操作符的指针(针对类对象和引用的 .* ,以及针对指向类对象的指针的 ->* 来做到这一点)
Screen myScreen, *bufScreen;
// 直接调用成员函数
if ( myScreen.height() == bufScreen->height() )
bufScreen->copy( myScreen );
// 通过成员指针的等价调用
if ( (myScreen.*pmfi)() == (bufScreen->*pmfi)() )
(bufScreen->*pmfS)( myScreen );
类似地,指向数据成员的指针可以按下列方式被访问:
typedef short Screen::*ps_Screen;
Screen myScreen, *tmpScreen = new Screen( 10, 10 );
ps_Screen pH = &Screen::_height;
ps_Screen pW = &Screen::_width;
tmpScreen->*pH = myScreen.*pH;
tmpScreen->*pW = myScreen.*pW;
6.3 、静态类成员的指针
在非静态类成员的指针和静态类成员的指针之间有一个区别。指向类成员的指针语法不能被用来引用类的静态成员。静态类成员是属于该类的全局对象和函数。它们的指针是普通指针(请记住静态成员函数没有this 指针。)
指向静态类成员的指针的声明看起来与非类成员的指针相同。解引用该指针不需要类对象
7 、嵌套类
    一个类可以在另一个类中定义,这样的类被称为嵌套类。嵌套类是其外围类的一个成员。嵌套类的定义可以出现在其外围类的公有、私有或保护区中
嵌套类的名字在其外围类域中是可见的,但是在其他类域或名字空间中是不可见的。这意味着,嵌套类的名字不会与外围域中声明的相同名字冲突
与非嵌套类一样,嵌套类可以有与自身同样类型的成员
私有成员是指这样的成员,它只能在该类的成员或友元定义中被访问。除非外围类被声明为嵌套类的友元,否则它没有权利访问嵌套类的私有成员
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值