直到死亡,我们还是会完全或部分谈谈C ++中的静态





你好。在其中一份代码回顾中,我遇到了很多想法以及隐藏自己的想法,而不是我们知道何时使用static关键字会很好在本文中,我想分享有关static关键字的知识和信息 , , ++. /++ . , static ++, . ++, , , , .



static?



静态是一个C ++关键字,用于赋予元素特殊的特征。对于静态元素,内存仅分配一次,并且这些元素存在直到程序终止。所有这些元素都不存储在堆中或堆栈中,而是存储在称为.data.bss的特殊内存段中(取决于是否初始化了静态数据)。下图显示了程序存储器的典型布局。





在哪里使用?



下面是一个程序静态方法的使用方式和位置图







现在,我将尝试详细描述图中显示的所有内容。走!



函数内部的静态变量



静态变量在函数内部使用时,仅初始化一次,然后保留其值。这些静态变量存储在静态存储区(.data或.bss)中,而不是存储在堆栈中,这允许在程序的整个生命周期中存储和使用变量的值。让我们看一下两个几乎相同的程序及其行为。唯一的区别是,一个使用静态变量,而另一个则不使用。



第一个程序:



#include <iostream>

void counter() {
  static int count = 0; //  4
  std::cout << count++;
}

int main() {
  for (int i = 0; i < 10; ++i) {
    counter();
  }
  return 0;
}


程序输出:

0123456789


第二个程序:



#include <iostream>

void counter() {
  int count = 0; //  4
  std::cout << count++;
}

int main() {
  for (int i = 0; i < 10; ++i) {
    counter();
  }
  return 0;
}


程序输出:

000000000


如果第4行不使用static,则每次调用counter()时都会发生内存分配和变量count的初始化,并且每次函数退出时销毁它。但是,如果我们将变量设为静态,则在初始化之后(第一次调用counter()函数),count的作用域将是main()函数的末尾,并且变量将在两次调用counter()的过程中保持其值



静态类对象



类的静态对象具有与上述常规静态变量相同的属性,即 存储在.data或.bss内存段中,在启动时创建并在程序终止时销毁,并且仅初始化一次。该对象照常进行初始化-通过类构造函数。让我们考虑一个带有静态类对象的示例。



#include <iostream>

class Base { //  3
public:
  Base() { //  5
    std::cout << "Constructor" << std::endl;
  }
  ~Base() { //  8
    std::cout << "Destructor" << std::endl; 
  }
};

void foo() { 
  static Base obj; //  14
} //  15

int main() {
  foo(); //  18
  std::cout << "End of main()" << std::endl;
  return 0;
}


程序输出:

构造

的主结束()

析构


3 Base ( 5) ( 8). . 14 obj Base. foo() 18.



- , , foo() 15, , .. . , , .





#include <iostream>

class Base {
public:
  Base() {
    std::cout << "Constructor" << std::endl;
  }
  ~Base() { 
    std::cout << "Destructor" << std::endl; 
  }
};

void foo() { 
  Base obj; 
} //  15

int main() {
  foo();
  std::cout << "End of main()" << std::endl;
  return 0;
}


如果foo()函数中创建变量时删除static,则每次调用该函数时,对象的销毁都会在第15行发生在这种情况下,对于在堆栈上分配了内存的局部变量,程序的输出将是非常期望的:

构造

析构

的主结束()




静态班级成员



与以前的用例相比,类的静态成员更加难以理解。让我们看看为什么。假设我们有以下程序:



#include <iostream>

class A { //  3
public:
  A() { std::cout << "Constructor A" << std::endl; }
  ~A() { std::cout << "Destructor A" << std::endl; }
};

class B { //  9
public:
  B() { std::cout << "Constructor B" << std::endl; }
  ~B() { std::cout << "Destructor B" << std::endl; }

private:
  static A a; //  15 ()
};

int main() {
  B b; //  19
  return 0;
}


在我们的示例中,我们使用静态类成员(第15行)创建了A(第3行)B(第9)。我们假设第19行创建对象b第15行创建对象a如果我们使用该类的非静态成员,就会是这种情况。但是该程序的输出如下:

构造函数B构造函数

B


此行为的原因是,类的静态成员未使用构造函数初始化,因为它们不依赖于对象的初始化。那些。第15行,我们仅声明对象,而不是对其进行定义,因为定义必须使用范围解析运算符(::)在类外部进行让我们定义一个B类成员



#include <iostream>

class A {
public:
  A() { std::cout << "Constructor A" << std::endl; }
  ~A() { std::cout << "Destructor A" << std::endl; }
};

class B {
public:
  B() { std::cout << "Constructor B" << std::endl; }
  ~B() { std::cout << "Destructor B" << std::endl; }

private:
  static A a; //  15 ()
};

A B::a; //  18 ()

int main() {
  B b;
  return 0;
}


现在,在第18行定义了静态类成员之后,我们可以看到以下程序输出:

构造函数

构造乙

析构乙

析构


必须记住,类成员对于类B的所有实例都是相同的,即 如果我们创建了类B的三个对象,则静态类成员的构造函数将仅被调用一次。这是我正在谈论的示例:



#include <iostream>

class A {
public:
  A() { std::cout << "Constructor A" << std::endl; }
  ~A() { std::cout << "Destructor A" << std::endl; }
};

class B {
public:
  B() { std::cout << "Constructor B" << count++ << std::endl; }
  ~B() { std::cout << "Destructor B" << --count << std::endl; }

private:
  static A a; // 
  static int count; // 
};

A B::a; // 
int B::count = 1; // 

int main() {
  B b1, b2, b3;
  return 0;
}


程序输出:

构造函数

构造B1

构造B2

构造B3

析构B3

析构函数B2

析构B1

析构


静态功能



静态函数是C生成的C ++。默认情况下,C中的所有函数都是全局函数,如果要在同一项目的两个不同.c(.cpp)文件中创建两个具有相同名称的函数,则会收到一条错误消息,指出该函数已定义(致命错误LNK1169:找到一个或多个乘以定义的符号)。下面列出了一个程序的三个文件。



// extend_math.cpp
int sum(int a, int b) {
  int some_coefficient = 1;
  return a + b + some_coefficient;
}


// math.cpp
int sum(int a, int b) {
  return a + b;
}


// main.cpp
 int sum(int, int); // declaration

int main() {
  int result = sum(1, 2);
  return 0;
}


为了解决此问题,我们将其中一个函数声明为static。例如这个:



// extend_math.cpp
static int sum(int a, int b) {
  int some_coefficient = 1;
  return a + b + some_coefficient;
}


, , . sum() math.cpp . , static , , , , , (.h).



, inline static, , . (.cpp), #include , . , , .. include .cpp .





- ()



您可以使用静态成员函数,而无需创建类的对象。使用类名和范围解析运算符(::)访问静态函数使用静态成员函数时,存在一些限制,例如:



  1. 在一个函数内,您只能访问静态数据成员,其他静态成员函数以及该类之外的任何其他函数。
  2. 静态成员函数具有它们所驻留的类的范围。
  3. 您无权访问该类的this指针,因为我们没有创建任何对象来调用此函数。


让我们看下面的例子:



#include <iostream>

class A {
public:
  A() { std::cout << "Constructor A" << std::endl; }
  ~A() { std::cout << "Destructor A" << std::endl; }

  static void foo() { //  8
    std::cout << "static foo()" << std::endl;
  }
};

int main() {
  A::foo(); //  14
  return 0;
}


A类,线8,我们有一个静态成员函数foo的() 在第14行,我们使用类名称和范围解析运算符调用该函数,并获得以下程序输出:

静态foo()


从输出中,您可以看到没有对象创建,也没有调用构造函数/析构函数。



如果foo()方法是非静态的,则编译器将在第14行的表达式引发错误,因为 您需要创建一个对象才能访问其非静态方法。





结论



« static , ». , , .



:



  • , . , , , . , , .
  • , , .. , . , , .. .
  • static Singleton, , . , -. Singleton , , .
  • 有时,为了使函数仅运行一次而不将先前的状态存储在对象中的某处,则使用静态变量。您可以在“函数内部的静态变量”部分中查看示例。但这不是一个很好的方法,如果您使用多线程,可能会导致长时间的错误搜寻。
  • 在实践中,C ++程序员经常使用静态成员函数代替不需要创建对象即可执行的常规函数​​。


希望您喜欢我在C ++中有关static关键字的文章我会很高兴收到任何批评和建议。谢谢大家!



All Articles