特殊的数据成员
常量成员
1 |
|
当数据成员用 const 关键字进行修饰以后,就成为常量成员。
只有当我们在构造函数中初始化 const 成员之后,这个成员才具有“只读属性”,在程序中无法对其值修改。所以赋值操作是无法完成的。
事实上,在构造函数体内对 const 数据成员赋值是非法的,const数据成员需在初始化列表中进行初始化(C++11之后也允许在声明时就初始化)。
引用成员
引用成员也是同样,虽然从定义上说必须被初始化,不允许没有指向任何东西。但是,只有在构造函数中初始化之后,才让这个引用成员得以绑定对象,也可以说引用成员的引用特性真实存在了。
但是我们要注意引用成员赋值的注意点,引用成员需要绑定一个已经存在的、且在这个引用成员的生命周期内始终有效的变量(对象)。
1 |
|
见上面这份代码,copy_name 初始化用通过初始化列表已初始化的方式来初始化,保证 m_name 的生命周期,因为它们是在一个类中的成员,保证 copy_name 的初始化数据的生命周期和自己保持一致。
所以下面这种方式是不恰当的,即 用传递进来的数据初始化引用成员变量。
1 |
|
那如果我们的形参 name 就是引用类型呢?那是可以的,但是你需要保证形参 name 的生命周期。
可以看到,引用成员的使用必须注意生命周期问题,否则你不知道什么时候出错。
类对象成员
有时候,一个类对象会作为另一个类对象的数据成员被使用。我们需要在初始化列表初始化对象。
注意:
- 初始化列表中写的是需要被初始化的对象成员的名称,而不是对象成员的类名。
- 不能在声明对象成员时直接使用有参构造去创建。
如果我们的类对象成员是指针,那就用 new 初始化。
如果我们的类对象成员不是指针,那就用调用构造函数的方式初始化。如果你没有进行初始化,就会调用该类对象的无参构造,如果你的类对象没有无参构造就会报错,这就是为什么我们建议即便你不需要无参构造,也实现一个的缘故。反过来说,如果不想用Point的无参构造,那么必须在Line类的初始化列表中对Point类的对象成员进行初始化。
详细见下:
1 |
|
这里有个问题值得讨论,那就是 Student 和 Demo 对应的构造函数谁先调用?
Student 先调用构造函数,否则根本不可能初始化 Demo 对应的类对象,但是必然是 Demo 对应的类对象 先完成对象的构建,才让 Student构造完成。
所以,你看到的现象是 Demo 对应的类对象先构造,其次才是 Student 构造,这个现象没有问题,但是是 Student 类对象先执行构造函数,等到 Demo 对应的类对象调用构造函数完成初始化之后,Student 类对象才构造完成。
静态成员
C++ 允许使用 static (静态存储)修饰数据成员,这样的成员在创建对象之前被创建并初始化的。且其实例只有一个,被所有该类的对象共享。静态数据成员存储在全局/静态区,并不占据对象的存储空间。
你可以认为,静态成员属于类,但不属于任何一个对象,但所有对象都可以访问静态成员。
1 |
|
静态成员规则:
- private的静态数据成员无法在类之外直接访问(显然)
- 对于静态数据成员的初始化,必须放在类外(一般紧接着类的定义,这是规则1的特殊情况)
- 静态数据成员初始化时不能在数据类型前面加 static,在数据成员名前面要加上类名+作用域限定符
- 如果有多条静态数据成员,那么它们的初始化顺序需要与声明顺序一致(规范)
- 静态成员在访问时可以通过对象访问,也可以直接通过类名::成员名的形式(更常用)
特殊的成员函数
静态成员函数
在某一个成员函数的前面加上static关键字,这个函数就是静态成员函数。静态成员函数具有以下特点:
(1)静态成员函数不依赖于某一个对象。
(2)静态成员函数可以通过对象调用,但更常见的方式是通过类名加上作用域限定符调用。
(3)静态成员函数没有this指针。
(4)静态成员函数中无法直接用成员的名字访问非静态的成员(数据成员、成员函数),只能访问静态数据成员或调用静态成员函数(因为没有this指针)。
构造函数、拷贝构造函数、赋值运算符函数、析构函数比较特殊,可以在静态成员函数中调用。
1 |
|
注:但是非静态的成员函数可以访问静态成员。
静态成员函数不能是构造函数/析构函数/赋值运算符函数/拷贝构造(因为这四个函数都会访问所有的数据成员,而 static 成员函数没有 this 指针)
const 成员函数
1 |
|
特点:
- const 成员函数中,无法修改类的非
mutable
成员变量。 - 当编译器发现该函数是 const 成员函数时,会自动将 this 指针设置为双重 const 限定的指针。
- 只能调用该类的其他
const
成员函数。 - 对于指针和引用类型的成员变量,
const
成员函数只能确保指针或引用的地址(即指向的对象)不变,不能防止指向对象的内容被修改。