C plus plus prime-类

类定义

类头、类体、类域

数据成员

数据成员声明在类体中,可以是任何类型,eg. 指针,类

分为:非静态(nonstatic),静态(static).

成员函数

成员函数在类域之外是不可见的。通过 "." 或 "->" 来引用。

注意区分全局域/全局函数,类域/成员函数。

成员访问

信息隐藏(information hiding):类成员的访问限制,通过访问限定符来实现的。

  • public 公有成员,提供给用户使用的
  • private 只能被成员函数和友元访问
  • protected 对派生类表现的像 public, 对其他程序表现的像 private.

友元

关键字 find, 友元可以是一个名字空间函数、一个前面定义的类的一个成员函数、也可以是一个完整的类。

允许一个类授权其他的函数访问他的非公有成员,通常声明放在类头之后。

类声明和定义

类声明,是只有类头,没有类体。无法确定类类型的大小,类成员也是未知的。但是可以声明指向该类类型的指针或引用。

类定义,是具有完整的类体。

什么时候用类声明?什么时候用类定义?

类对象

类定义不会分配存储区,只有定义了一个类的对象,才会分配。

类类型,即定义的一个类。通过它定义的对象是有生命期的,生命期根据它在哪个域中被声明的。

类对象可以被另一个对象初始化或赋值,拷贝一个类对象与拷贝它的成员函数等价。

当一个类对象被指定为函数实参或函数返回值时,它就被按值传递。我们可以把一个函数参数或返回值声明为一个类类型的指针或引用。(7.3节,7.4节)

回顾下指针:

1
2
3
4
int i = 100;
int *p;
int* p = &i;
int *p = &i;

p 是指针变量,用来存储地址。注意数组名是 const 地址常量,代表第一个元素的地址。

指针和引用的区别:

指针:指针是一个变量,只不过这个变量存储的是一个地址,指向内存的一个存储单元;而引用跟原来的变量实质上是同一个东西,只不过是原变量的一个别名而已。如:

1
2
int a=1; int *p=&a;
int a=1; int &b=a;

上面定义了一个整形变量和一个指针变量p,该指针变量指向a的存储单元,即p的值是a存储单元的地址.
而下面2句定义了一个整形变量a和这个整形a的引用b,事实上a和b是同一个东西,在内存占有同一个存储单元。

  • "sizeof引用"得到的是所指向的变量(对象)的大小,而"sizeof指针"得到的是指针本身的大小;
  • 可以有const指针,但是没有const引用;
  • 指针的值可以为空,但是引用的值不能为NULL,并且引用在定义的时候必须初始化;
  • 指针的值在初始化后可以改变,即指向其它的存储单元,而引用在进行初始化后就不会再改变了

继续回到类对象作为函数参数时,用成员访问操作符来访问类对象的数据成员或成员函数。点操作符与类对象或引用连用; 箭头访问操作符与类对象的指针连用。

1
2
3
4
5
6
7
8
# include "Screen.h"
bool isEqual(Screen& s1, Screen *s2)
{
...
s1.height ...
s2->height ...
...
}

isEqual 是非成员函数,如果要直接引用 s1, s2 中的数据成员 height 是不可以的。所以必须借助于 Screen 中的公有成员函数。

1
s2->height 等价于 (*s2).height

类成员函数

一组操作的集合。

inline 和 非 inline 成员函数

内联函数的作用:如果在程序中调用某个函数,不但要拷贝实参,保存机器的寄存器,程序还必须转向一个新的位置,这样会降低效率。

而内联函数就是解决这个问题的,在程序的调用节点上,“内联”的展开函数的操作,从而额外的执行开销被消除了。

类体内定义的成员函数自动的作为内联函数处理。
类体内声明,类体外定的函数,需要显示的加上关键字 inline. 同时类体外的定义需要用类名限定修饰(限定修饰符 :: )

访问类成员

成员函数的定义可以引用任何一个类成员,无论该成员是私有还是公有。

成员函数可以直接访问它所属类的成员,无需访问操作符。这实际上是通过 this 指针实现的。

私有与公有成员函数

公有函数集定义了类的接口。私有成员函数为其他成员函数提供支持。

特殊的成员函数

构造函数:管理类对象并处理初始化、赋值、内存管理、类型转换、析构等活动。每次定义一个类对象或 new 表达式分配一个类对象都会调用它。
构造函数的名字必须与类名相同。

const 和 volatile 成员函数

只有被声明为 const 的成员函数才能被一个 const 对象调用。关键字 const 在函数体和参数表之间.

通常一个类如果想被广泛使用,其中不修改类数据成员的成员函数应该声明为 const 成员函数。

但是即使声明了,这个成员函数依然有可能修改数据成员,如果这个类含有指针。

比如:

1
2
private:
char *_text;

_text 不能被修改,但是它指向的字符却是可以被修改的。所以程序员这个时候就需要注意了。。

构造函数和析构函数即使不是 const 成员函数,也能被 const 对象调用。

volatile 跟 const 用法一样,它用来提示编译器该对象的值可能在编译器未被检测到的情况下被修改。因为,编译器不能武断的对引用这些对象的代码进行优化。

mutable 数据成员

一旦一个对象被声明为 const,它的内容就不能修改。但是其中某些数据成员,比如索引,被修改之后并没有修改对象本身,这个时候就可以将该数据成员(也就是索引)声明为 mutable.

那样,即使 const 对象, const 成员函数修改了该数据成员,也不会有编译错误。

隐含的指针 this

每个类成员函数都含有一个指向被调用对象的指针。但是一般不需要显示的写出来,如果写出来也是可以的。

何时使用指针呢?

当连续使用成员函数时,比如 myVector.find().sort().insert() 时,对应的成员函数返回的值应该是被调用对象本身,也就是 *this.

还有一种情况, copy 函数:

1
2
3
4
5
6
7
void Screen::copy(const Screen& sobj)
{
if (this != &sobj)
{
// 把 sobj 的值拷贝到 * this 中
}
}

这里的 this 指针含有被调用对象的地址。如果 sobj 的地址 &sobj 与 this 相同,那就不需要拷贝了。

注意这里 & 用法:引用和取地址。

静态类成员

静态数据成员

对于非静态成员,每个类都有一个自己的拷贝。而静态成员对每个类类型只有一个拷贝。静态数据成员只有一份,由该类类型的所有对象共同访问。

不同于全局对象,它可以隐藏,并且不会与其他全局名字冲突。

关键字 static. 注意与 const 的区别,没有加 const 意味着是可以更新的。只需要更新一次,所有的类对象对应的值都会更新。

静态类成员的显示初始化,在类定义之外,用类名限定修饰。在静态数据成员的定义中也可以直接使用私有成员,这与在类成员函数中直接引用私有成员是一样的。

对于静态数据成员,除了通过类对象使用成员访问操作符访问之外,还可以直接使用类名加限定修饰符访问

1
Account::_intersetRate

静态成员函数

静态成员函数,只访问静态数据成员,而不访问任何其他数据成员。所以它们与哪个对象来调用这个函数无关。

在类体中声明时需要加关键字 static, 类体外不能指定关键字。并且不能设定为 const 和 volatile.

指向类成员的指针

普通函数指针 7.9节

成员函数指针