标识符的作用域与可见性
作用域是一个标识符在程序正文中的有效区域。作用域可以分为以下几类:
- 函数原型作用域:函数原型中的参数,其作用域始于”(“,结束语”)”,如:
1 | double area(double radius) |
局部作用域:
- 函数的形参、在块中声明的标识符
- 其作用域自声明处起,限于块中
类作用域:
- 类的成员具有类作用域,其范围包括类体和非内联成员函数的函数体。
- 如果在类作用域以外访问类的成员,要通过类名(访问静态成员),或者该类的对象名、对象引用、对象指针(访问非静态成员)
- 文件作用域:
- 不在前述各个作用域中出现的声明,就具有文件作用域,这样声明的标识符其作用域开始于声明点,结束语文件尾。
- 可见性:
- 可见性是从对标识符的引用的角度来谈的概念
- 可见性表示从内层作用域向外层作用域“看”时能看见什么
- 如果标识在某处可见,就可以在该处引用此标识符
- 如果某个标识符在外层中声明,且在内层中没有同一标识符的声明,则该标识符在内层可见
- 对于两个嵌套的作用域,如果在内层作用域内声明了与外层作用域中同名的标识符,则外层作用域的标识符在内层不可见。
1 |
|
对象的生存期
静态生存期:
- 静态生存期与程序的运行期间相同;
- 在文件作用于中声明的对象具有这种生存期
- 在函数内部声明静态生存期对象,要冠以关键字
static
动态生存期:
- 开始于程序执行到声明点时,结束语命名该标识符的作用域结束处
- 块作用域中声明的,没有用static修饰的对象时动态生存期的对象(习惯称局部生存期对象)
1 |
|
类的静态成员
静态成员:在定义前面加了static
关键字的成员
1 | class CRectangle |
基本概念
- 普通成员变量每个对象有各自的一份,而静态成员变量一共就一份,为所有对象共享
注意:sizeof
运算符不会计算静态成员变量,如下例中sizeof(Myclass)
等于4
1 | class Myclass{ |
- 普通成员函数必须具体作用于某个对象,而静态成员函数并不具体作用于某个对象。
- 因此静态成员不需要通过对象就能访问
如何访问静态成员:
- 类名::成员名
1 | CRectangle::PrintTotal(); |
- 对象名.成员名(并不意味着静态成员或静态成员函数只作用于该对象,,它们是被所有的CRentangle对象所共享的)
1 | CRectangle r; r.PrintTotal(); |
- 指针->成员名
1 | CRectangle *p = &r; p->PrintTotal(); |
- 引用.成员名
1 | CRectangle &ref = r; int n = ref.nTotalNumber; |
- 静态成员变量本质上是全局变量,哪怕一个对象都不存在,类的静态成员变量也存在。
- 静态成员函数本质上是全局函数
- 设置静态成员这种机制的目的是将和某些类紧密相关的全局变量和函数写到类里面,看上去像一个整体,易于维护和理解。
示例:考虑一个需要随时知道矩阵总数和总面积的图像处理程序,可以用全局变量来记录总数和总面积,同静态成员将这两个变量封装进类中,就更容易理解和维护。
1 |
|
注意事项:
- 在静态成员函数中,不能访问非静态成员变量,也不能调用非静态成员函数(因为其可能访问非静态成员变量),例如下面的定义就是错误的。
1 | void CRectangle::PrintTotal() |
我们回过头来再看之前的CRectangle
类的写法,其实它是有严重缺陷的,那么这个缺陷是如何产生的呢?
问题就出在我们忽略了复制构造函数,在程序需要它时,会调用自动生成的复制构造函数,自然就不会对nTotalNUmber
和nTotalArea
作相应的增加。
在使用
CRectangle
类时,有时会调用复制构造函数生成临时的隐藏的CRectangle
对象- 调用一个以
CRectangle
类对象作为参数的函数时 - 调用一个以
CRectangle
类对象作为返回值的函数时
- 调用一个以
临时对象在消亡时会调用析构函数,减少
nTotalNUmber
和nTotalArea
的值,可是这些临时对象在生成时却没有增加nTotalNUmber
和nTotalArea
的值。
解决办法:为CRectangle
类写一个复制构造函数
1 | CRectangle::CRectangle(CRectangle &r) |
类的友元
- 友元是C++提供的一种破坏数据封装和数据隐藏的机制
- 通过将一个模块声明为另一个模块的友元,一个模块能引用到另一个模块中很是被隐藏的信息
- 可以使用友元函数和友元类
- 为了确保数据的完整性,及数据封装与隐藏的原则,建议尽量不使用或少使用友元
友元函数
- 友元函数是在类声明中由关键字friend修饰说明的非成员函数,在它的函数体重能够通过对象名访问
private
和protected
成员。 - 作用:增加灵活性,时程序员可以在封装和快速性方面做合理的选择。
- 访问对象中的成员必须通过对象名
1 |
|
友元类
- 若一个类为另一个类的友元,则此类的所有成员都能访问对方类的私有成员
- 声明语法:将友元类名在另一个类中使用
friend
修饰说明
1 | class CCar |
主要注意的是:类的友元关系是单向的:
- 如果声明B类是A类的友元,B类的成员函数就可以访问A类的私有和保护数据,但A类的成员函数不能访问B类的私有、保护数据,即友元类的关系不能传递,不能继承。
this指针
在C++刚推出的时候,编译器在编译C++程序课程时先把一段C++程序翻译成C程序,然后再用C的编译去编译。比如说我们把下面的CCar
的类的C++程序翻译成C程序,class
对应C中的struct
结构体,而C中没有成员函数,因此需要借助this
指针来实现SetPrice
的功能。
C++代码:
1 | class CCar { |
C代码:
1 | struct CCar { |
this
指针作用:非静态成员函数中可以直接使用this
来代表指向该函数作用的对象的指针。
1 | class Complex { |
- 注意静态成员函数不能使用this指针,因为静态成员函数并不具体作用于某个对象。
常量const
常量对象:如果不希望某个对象的植被改变,则定义该对象时在其前面加
const
关键字。常量成员函数:在类的成员函数说明后面加
const
关键字- 常量成员函数执行期间不应该修改其作用的对象。因此,在常量成员函数中不能修改成员变量的值(静态成员变量除外),也不能调用同类的非常量成员函数(静态成员函数除外)。
- 若有两个成员函数,名字和参数表都一样,但是一个是
const
,一个不是,算重载,而不是冲突定义。
常引用:引用前加const,不能通过常引用,修改其引用的变量;常引用经常作为函数的参数
- 当传递一个对象的效率较低(因为需要调用复制构造函数生成一个新的对象),又要确保实际参数的值不能在函数内部被修改时,可以将参数的类型声明为常引用
1 | class Rectangle{ |