网站建设流程域名dns web海外推广营销系统
tags: C++ OOP
引子: 类为什么需要静态成员
有时候类需要与它的一些成员与类本身直接相关, 而不是与类的各个对象都保持关联, 这就减少了成员与每一个类的实例对象的联系, 从而降低资源占用. 另一方面, 如果每次都需要重新更新该成员, 使得对象使用新的值, 这时候只需要修改一份该成员.
声明静态成员
要点
-
在成员声明之前加上关键字
static
使得其与类关联在一起 -
静态成员可以是public或private的, 类型可以是常量, 引用, 指针, 类等
-
类的静态成员存在于任何对象之外, 对象中不包含任何与静态数据成员有关的数据(这也是为什么需要类内声明, 类外初始化的原因)
-
类的静态成员函数也不与任何实例对象绑定在一起, 不包含this指针.
-
类的静态成员函数不能声明为const的, 也不能在static函数体内使用this指针.
正因为没有this指针, 所以不能加cv-修饰符
error: static member function 'static void P::f()' cannot have cv-qualifier
当将const限定符应用于非静态成员函数时,它会影响this指针。对于类C的const限定成员函数,this指针的类型为
C const*
,而对于非const限定的成员函数,this指针的类型为C*
。静态成员函数没有this指针(此类函数不会在类的特定实例上调用),因此静态成员函数的const限定没有任何意义。 -
在类外可以直接通过作用域运算符直接访问静态成员, 即使直接通过类名访问, 也不会出错.
-
成员函数可以不通过作用域运算符直接使用静态变量;
-
在类的外部定义静态成员时, 不能重复使用static关键字, 该关键字只能出现在类内部的声明语句中.
-
因为静态数据成员不属于类的任何一个对象, 所以它们并不是在创建类的对象时被定义的. 这意外着它们不是由类的构造函数初始化的.
-
一般来说, 不能在类的内部初始化静态成员, 而必须在类的外部定义和初始化每一个静态成员.
经过测试, 静态常量数据成员可以在类内初始化, 静态成员函数可以在类内定义.
-
一个静态数据成员只能定义一次.
-
类似于全局变量, 静态数据成员定义在任何函数之外, 所以静态数据成员一旦被定义, 就将一直存在于程序的整个生命周期中.
-
可以为静态成员提供const整数类型的类内初始值, 不过要求静态成员必须是字面值常量类型的constexpr. 初始值必须是常量表达式, 因为这些成员本身就是常量表达式, 所以能用在所有适合于常量表达式的地方.
-
如果某个静态成员的应用场景仅限于编译器可替换其值的情况, 则一个初始化的const或者constexpr static不需要分别定义; 反之, 如果将其用于值不能替换的情况, 则该静态成员必须有一条定义语句.
例子: 如果某一成员的作用就是定义类内另一成员的size, 就不需要专门在类外定义该静态常量了.
// 在类内 static constexpr int size = 10; double array[size];
-
如果在类内提供了一个初始值, 则成员的定义不能再指定一个初始值了.
例子:
// 当需要把下面的值传给一个接受`const int&`的函数时, 必须定义`size` static constexpr int P::size = 10; void test(const int &a) { cout << a << endl; } void t2() { test(P::MAX_length); }
-
即使一个常量静态成员在类内被初始化了, 通常情况下也应该在类外部定义(我认为应该翻译为生命)一下该成员.
// 类内: static constexpr int MAX_length = 10; // 类外: constexpr int P::MAX_length;
参考:
- Definitions and ODR (One Definition Rule) - cppreference.com;
- c++ - In-class initialization of static data members - Stack Overflow;
- ★\bigstar★c++ - Static data member initialization in the class definition? - Stack Overflow
有点奇怪.
例子: 访问静态成员
注释掉的就是错误的用法.
#include <iostream>
using namespace std;
class P {
public:P() = default;~P() = default;/* static void f()const{} */static void g() {/* this->MAX_age = 1; */cout << "g()\n";}static void h();static const int MAX_age = 1;static constexpr int MAX_length = 1;/* static int MAX_account = 1; */static int MAX_account;
};
int P::MAX_account = 10;
/* static void P::h() { cout << "h()\n"; } */
void P::h() { cout << "h()\n"; }void t1() {int b = P::MAX_age;cout << b << endl;P::MAX_account = 12;cout << P::MAX_account << endl; // 12
}int main(int argc, char *argv[]) {t1();return 0;
}
与非静态成员的区别
要点
-
静态成员可以是不完全类型(可以不是指针或引用, 但是非静态成员不行)
不完全类型:
class P; // 前向声明
在声明之后, 定义之前,
P
是不完全类型. 即: 只知道P是一个类类型, 但是不清楚到底包含哪些成员. -
静态成员可以作为默认实参. 非静态数据成员不能作为默认实参, 因为其值属于对象的一部分, 如果作为默认实参, 将无法真正提供一个对象以便从中获取成员的值, 导致错误.