1. 静态成员变量和函数并不依赖于类的任何对象,它们是类的属性,因此不需要通过类的对象访问,可以通过类名直接访问。
2. 静态成员变量和函数是公有的,可被所有对象访问,但是无法访问类的非静态成员。
3. 静态成员变量和函数不能被声明为const、volatile或者constexpr。
4. 静态成员变量必须在类外进行定义,且只能定义一次。
5. 静态成员函数不能直接访问类的非静态成员,静态成员函数及其参数中不允许使用this指针,因为this指针只有实例化后才会产生。
6. 静态成员函数不能被声明为虚函数,因为虚函数实现需要运行时的信息,而静态成员函数不依赖于类的对象,无法确定运行时的信息。
7. 任何需要依靠对象的成员和成员函数都不能定义为静态类型。
C++中的static关键字可以用于声明静态成员变量(或数据成员)和静态成员函数。它们与类相关联,而不是与类的对象关联,因此可以被所有类的对象共享。通过使用关键字`static`在类中声明静态数据成员,可以将数据成员与类的对象解耦。它们只有一个实例,并且可以在类的任何成员函数中修改,从而可以用来记录该类的所有对象的统一信息。
// 静态数据成员
#include <iostream>class player {
public:player() {++count;}// 通过使用关键字`static`在类中声明静态数据成员,可以将数据成员与类的对象解耦。// 它们只有一个实例,并且可以在类的任何成员函数中修改,从而可以用来记录该类的所有对象的统一信息。static int count;
private:int id;
};
// 类的静态成员变量需要在类外进行定义
int player::count = 0;int main()
{player p1, p2, p3;std::cout << "total players: " << player::count << std::endl;return 0;
}
// 静态成员函数
// 静态成员函数可以使用与静态数据成员相同的语法进行声明:将`static`关键字放在函数的声明之前。
// 与非静态成员函数相比,它们没有this指针,并且只能对静态数据成员进行操作。
#include <iostream>class car {
public:car() {++count;}static int getCount() {return count;}
private:static int count;
};int car::count = 0;int main()
{car c1, c2, c3;std::cout << "number of cars : " << car::getCount() << std::endl;return 0;
}
// 使用静态成员变量实现单例模式
#include <iostream>class Singleton {
public:// 静态成员函数`getInstance()`返回`Singleton`类的单例实例。// getInstance()成员函数是一个静态成员函数,它只能访问静态成员变量,因此可以在类内定义,也可以在类外定义。static Singleton &getInstance() {// instance成员变量是一个静态局部变量,局部静态变量在第一次调用时被初始化,并一直存在直到程序结束,因此不需要在类外进行定义。static Singleton instance;// 返回一个静态局部变量instance的引用,从而实现了全局唯一的实例。// 由于instance是静态局部变量,它只会被初始化一次,并一直存在直到程序结束,// 因此Singleton类的使用者可以多次调用getInstance()来获得同一个实例,并且该实例的生命周期与程序的生命周期相同。return instance;}void foo() {std::cout << "hello world!" << std::endl;}
private:// 类中声明了一个私有的无参构造函数,使得该类不能被直接实例化。Singleton() {}~Singleton() {}
};int main()
{Singleton &s1 = Singleton::getInstance();Singleton &s2 = Singleton::getInstance();std::cout << (&s1 == &s2) << std::endl; // true// 通过对`s1`和`s2`取地址后,对返回的指针进行解引用,并调用其中的`foo()`函数。// 在C++中,对指针进行解引用可以使用`*`或者`->`符号。// `*`符号用在对指针变量本身进行解引用时,而`->`符号则用在对指向对象的指针进行解引用时。// 因此,`(&s1)->foo()`和`s1.foo()`实际上是等价的。// 对`s1`和`s2`取地址后,返回的是指向`Singleton`对象的指针。// 因为`foo()`函数是`public`成员函数,可以通过对象的指针调用。// 因此,在这里可以对指向`Singleton`对象的指针进行解引用,并调用其中的`foo()`函数。(&s1)->foo();(&s2)->foo();// 打印`s1`和`s2`的指针地址,通过输出结果可以发现,// `s1`和`s2`的指针地址是相同的,这说明它们指向了同一个`Singleton`类的实例。std::cout << std::hex << &s1 << std::endl;std::cout << std::hex << &s2 << std::endl;return 0;
}
// static成员变量和static成员函数的生命周期和共享特性
// static成员变量和static成员函数都是属于类的,而不是属于类的任何一个对象。
// 其生命周期与程序的生命周期相同,即它们在程序中只会被创建一次,不管有多少个类的对象被创建,它们都只有一份。
// static成员变量是在程序运行时分配的内存空间,而不是在类的对象创建时分配的,因此它们可以在多个对象之间共享数据。
// static成员函数没有this指针,所以它们不能访问非静态成员变量或函数,只能访问静态成员。
// 与普通成员变量和函数的生命周期不同,普通成员变量和函数的生命周期与对象相关,即每个对象都有自己的成员变量和成员函数。
// 与类的生命周期不同,类只是一个抽象概念,没有实际的内存空间,因此类不存在生命周期的概念。
// 与对象的生命周期不同,对象是类的一个实例,有自己的内存空间,当对象被销毁时,它的成员变量和成员函数也随之销毁。#include <iostream>class Person {
public:Person(std::string name, int age) : name_(name), age_(age) {count_++;}static int count_;static void countPrint() {std::cout << "total number of person: " << count_ << std::endl;}private:std::string name_;int age_;
};// 定义并初始化静态成员变量
int Person::count_ = 0;int main()
{Person p1("Alice", 25);Person p2("Bob", 30);Person p3("Tom", 35);// 由于count_是静态成员变量,所有Person对象共享该变量,因此在每次创建新对象时递增count_可以统计所有已创建的对象数量。// 输出总共创建的Person对象的数量为3,即使我们创建了多个对象,它们都只是count_的共享者而不是它们自己的副本。Person::countPrint(); // 输出3return 0;
}
// static成员变量的申明定义和初始化
// 在C++中,类的静态成员变量必须在类外进行定义和初始化。
// 这是因为静态成员变量不是对象的成员,而是整个类所共享的成员,因此需要在类外进行定义和初始化,以便被所有对象所共享。
// 虽然C++中的静态成员变量可以在类外定义和初始化,但是在使用该成员变量时,仍需要在类内进行声明。
// 因此,即使在类外定义和初始化了静态成员变量,仍需要在类内声明该成员变量的类型和名称。
#include <iostream>class Person {
public:Person(std::string name, int age) : name_(name), age_(age) {}void infoPrint() const {std::cout << "person " << total_++ << " : name : " << name_ << ", age : " << age_ << std::endl;}// 声明静态成员变量static int total_;
private:std::string name_;int age_;
};// 定义并初始化静态成员变量
int Person::total_ = 0;int main()
{Person p1("Alice", 28);Person p2("tom", 22);p1.infoPrint();p2.infoPrint();return 0;
}
// c++类的static成员变量声明定义和初始化
// 在C++中,类的静态成员变量可以在类外进行初始化,也可以在类内进行初始化。
// 如果在类内进行初始化,则需要在声明时进行初始化,
// 值得注意的是,如果静态变量在类内进行初始化,则必须将其定义为const或constexpr类型
#include <iostream>class MyClass {
public:// constexptr静态常量// 这个静态常量是在类内部定义和初始化的,因为它是一个const类型的变量。// 通过将静态常量定义为constexpr类型,可以将其作为编译时常量使用。// 如果希望在类中定义静态非常量,还是应该在类外进行初始化。// 这里需要注意的是,静态成员的定义和初始化应该放在同一文件中,否则编译器会出现多个定义的错误。// 如果不用constexpr修饰将报错:error: ISO C++ forbids in-class initialization of non-const static member ‘MyClass::MAX_NUM’// 如果不进行初始化将报错:error: constexpr static data member ‘MAX_NUM’ must have an initializerstatic constexpr int MAX_NUM = 100; // 已在编译期确定的常量// `constexpr`是C++11引入的关键字,用来指定在编译期能够计算出结果的表达式。// 在编写程序时使用`constexpr`可以提高代码的效率和可读性,在编译器进行编译时就已经完成了一些运算,提高运算速度,// 并且编译器在进行代码检查时也会对`constexpr`表达式进行检查,帮助程序员排错。// 在C++11之前,常量只能由`#define`和`const`定义。// 使用`#define`定义的常量是一种预处理器宏,不检查变量类型,也不进行类型安全检查,// 而使用`const`定义的常量需要在运行期进行计算,并且不方便在数组、结构体、枚举等数据类型中使用。// C++标准规定,constexpr函数应该满足以下要求:// 1. 必须有且只有一个return语句;// 2. 函数中不能有任何形式的循环;// 3. 函数中不能使用任何C++运行时库函数。static int factorial(int n) {if (n <= 1){return 1;} else {return n * factorial(n-1);}}static void print() {static constexpr int num = 5;int arr[MyClass::factorial(num)];std::cout << "the size of the array is " << sizeof(arr) / sizeof(int) << std::endl;}
};// 非静态成员变量不能再类外进行定义和初始化
// 否则报错:error: ‘int MyClass::num’ is not a static data member of ‘class MyClass’
// int MyClass::num = 0;// 不允许在定义(而不是声明)一个静态数据成员时使用`static`关键字。
// 否则报错:error: ‘static’ may not be used when defining (as opposed to declaring) a static data member [-fpermissive]
// 所以类的static成员变量只能再类里边声明,不能再类外边声明
// static int MyClass::num = 0;int main()
{std::cout << MyClass::MAX_NUM << std::endl;MyClass::print();return 0;
}
// 类的静态成员不占用类的内存空间
#include <iostream>class MyClass0 {
public:MyClass0() {}~MyClass0() {}};class MyClass1 {
public:MyClass1() {}~MyClass1() {}public:static std::string name;static int num;
};
std::string MyClass1::name = "Tom";
int MyClass1::num = 0;class MyClass2 {
public:MyClass2() {}~MyClass2() {}public:std::string name;int num;
};int main()
{std::cout << sizeof(MyClass0) << std::endl;MyClass0 obj0;std::cout << sizeof(obj0) << std::endl;std::cout << sizeof(MyClass1) << std::endl;MyClass1 obj1;std::cout << sizeof(obj1) << std::endl;std::cout << sizeof(MyClass2) << std::endl;MyClass2 obj2;std::cout << sizeof(obj2) << std::endl;return 0;
}