在函数内部声明的静态变量,即使函数结束,静态局部变量的值也会保留。通常用在需要记住历史信息的情况下。
void count() {
static int counter = 0; // 静态局部变量
counter++;
cout << counter << endl;
}
// 当我们多次调用 count() 函数时,counter 变量的值会累加,而不是每次都从 0 开始。
在函数外部声明的静态变量。它的作用范围局限于声明它的文件内。通常用于在一个文件内共享信息,但防止其他文件访问。
// file1.cpp
static int counter = 0; // 静态全局变量
// 这个 counter 变量只能在 file1.cpp 中访问,其他的 .cpp 文件是无法访问的。
类中的静态成员变量。它们是类的所有对象共有的。静态成员变量在所有对象中有且只有一个副本。通常用在所有对象需要共享同一个变量的时候。
class MyClass {
public:
static int counter; // 静态成员变量
};
// 需要在类定义的外部初始化静态成员变量
int MyClass::counter = 0;
// 所有的 MyClass 对象都会共享同一个 counter 变量。
类中的静态成员函数。它们也是类的所有对象共有的。静态成员函数只能访问静态成员数据、其他静态成员函数和类外部的其他函数。通常用在需要操作静态成员变量,或者不需要访问非静态成员变量和函数的时候。
class MyClass {
public:
static int counter;
static void increaseCounter() { // 静态成员函数
counter++;
}
};
int MyClass::counter = 0;
// 可以通过类名和作用域解析运算符直接调用静态成员函数,不需要创建对象。
MyClass::increaseCounter();
当我们需要在类级别而非对象级别共享数据或者函数时,就必须使用 static。
如果在类的成员函数中需要一个回调函数,而这个回调函数需要被全局访问,那么这个成员函数必须是 static 的。
在C++中,非静态成员函数需要一个对象的上下文(即this指针)来调用,但在某些全局或静态上下文(例如线程创建或排序函数)中,这个对象的上下文可能并不存在。在这种情况下,我们通常会使用静态成员函数作为回调函数。
以下是一个例子,它使用C++11的std::thread类。假设我们有一个类Foo,它有一个成员函数bar,我们希望在新的线程中运行这个函数
#include
#include
class Foo {
public:
void bar() {
std::cout << "Hello, World!\n";
}
void startThread() {
// error: 非静态成员函数需要一个对象的上下文
// std::thread t(bar);
// t.detach();
}
};
上述代码不能正确运行,因为bar是一个非静态成员函数
,而在startThread函数中,我们无法给出这样一个上下文。
如果我们将bar变为静态函数,就可以正确地创建线程:
#include
#include
class Foo {
public:
static void bar() {
std::cout << "Hello, World!\n";
}
void startThread() {
std::thread t(Foo::bar);
t.detach();
}
};
但是注意,静态成员函数无法访问类的非静态成员变量,因为这些变量是与类的实例关联的,而静态成员函数不属于任何具体的类的实例。如果需要在静态成员函数中访问非静态成员,一种常见的方法是将要访问的对象作为参数传递给静态函数。
此处bar必须static的原因
类的成员函数确实可以互相调用,不需要加 static。
然而,这里有个关键的点:在创建 std::thread 的时候,我们需要提供一个全局函数或者静态成员函数作为新线程的入口点。在这种情况下,我们不能直接传递一个非静态成员函数,因为非静态成员函数需要一个隐含的 this 指针,而我们在创建线程的时候不能提供这个 this 指针。
在上述例子中,std::thread t(bar); 这行代码会引发编译错误,因为 bar 是一个非静态成员函数,这就需要一个 Foo 类型的对象来调用它,但在创建线程的语境中我们并未提供这个对象。
如果 bar 是一个非静态成员函数,并且我们需要在一个新的线程中调用它,那么我们应该这样做:
#include
#include
class Foo {
public:
void bar() {
std::cout << "Hello, World!\n";
}
void startThread() {
std::thread t(&Foo::bar, this); // 在新线程中调用非静态成员函数
t.detach();
}
};
在这里,我们使用了 std::thread
的构造函数的另一种形式,它接受一个成员函数指针和一个类的对象,这样新的线程就可以在给定的对象上调用指定的成员函数。这个版本的 std::thread 的构造函数会保存 this 指针,并在新的线程中用它来调用成员函数 bar。
然而,如果在某些情况下我们不能(或者不想)提供一个类的对象(例如,我们正在写一个静态成员函数或全局函数,并且我们需要一个回调函数),那么我们的唯一选择就是使用静态成员函数,因为它们可以在没有对象上下文的情况下被调用。
补充:线程库相关
std::thread t(Foo::bar); 这一行代码的意思是创建一个新的线程并在这个新线程上执行Foo::bar函数。
C++11引入了库来提供对线程的支持。可以使用std::thread类来创建和管理线程。它的构造函数需要一个函数或可调用对象作为参数,这个函数或可调用对象就是新线程开始执行时所调用的函数。在上面的例子中,Foo::bar就是这个函数。
然而,std::thread t(Foo::bar);这样的代码只有在Foo::bar是一个静态成员函数时才是有效的。因为非静态成员函数需要一个对象的上下文(也就是this指针
),而在这个例子中,我们并没有提供这样的上下文。
————————————————
版权声明:本文为CSDN博主「大磕学家ZYX」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:static在 C++ 中的四种用法