다음을 통해 공유


스토리지 클래스

C++ 변수 선언 컨텍스트의 스토리지 클래스는 개체의 수명, 연결 및 메모리 위치를 제어하는 형식 지정자입니다. 주어진 개체에는 스토리지 클래스가 하나만 있을 수 있습니다. 블록 내에 정의된 변수에는 , 또는 thread_local 지정자를 사용하여 지정하지 않는 한 자동 스토리지가 externstatic있습니다. 자동 개체 및 변수에는 연결이 없습니다. 블록 외부의 코드에는 표시되지 않습니다. 실행이 블록에 들어갈 때 메모리가 자동으로 할당되고 블록이 종료되면 할당이 해제됩니다.

주의

  • 키워드는 mutable 스토리지 클래스 지정자로 간주될 수 있습니다. 그러나 클래스 정의의 멤버 목록에서만 사용할 수 있습니다.

  • Visual Studio 2010 이상: 키워드는 auto 더 이상 C++ 스토리지 클래스 지정자가 아니고 register 키워드는 더 이상 사용되지 않습니다. Visual Studio 2017 버전 15.7 이상: (모드 이상에서 /std:c++17 사용 가능): register 키워드가 C++ 언어에서 제거됩니다. 이 기능을 사용하면 진단 메시지가 발생합니다.

    // c5033.cpp
    // compile by using: cl /c /std:c++17 c5033.cpp
    register int value; // warning C5033: 'register' is no longer a supported storage class
    

static

키워드를 static 사용하여 전역 범위, 네임스페이스 범위 및 클래스 범위에서 변수 및 함수를 선언할 수 있습니다. 정적 변수는 로컬 범위에서 선언할 수도 있습니다.

정적 생존 기간이란 프로그램이 시작될 때 개체 또는 변수가 할당되고 프로그램이 끝날 때 개체 또는 변수가 할당 취소됨을 의미합니다. 외부 연결은 변수의 이름이 변수가 선언된 파일 외부에서 볼 수 있음을 의미합니다. 반대로 내부 링크는 변수가 선언된 파일 외부에 이름이 표시되지 않음을 의미합니다. 기본적으로 전역 네임스페이스에서 정의된 개체 또는 변수에는 정적 지속 기간 및 외부 링크가 있습니다. 키워드는 static 다음과 같은 경우에 사용할 수 있습니다.

  1. 파일 범위(전역 및/또는 네임스페이스 범위) static 에서 변수 또는 함수를 선언하는 경우 키워드는 변수 또는 함수에 내부 링크가 있음을 지정합니다. 변수를 선언할 때 변수가 정적 생존 기간을 가지며, 다른 값을 지정하지 않으면 컴파일러가 0으로 초기화합니다.

  2. 함수에서 변수를 선언할 때 키워드는 해당 함수 static 에 대한 호출 간에 변수의 상태를 유지하도록 지정합니다.

  3. 클래스 선언에서 데이터 멤버를 선언 static 할 때 키워드는 클래스의 모든 인스턴스에서 하나의 멤버 복사본을 공유한다고 지정합니다. 데이터 멤버는 static 파일 범위에서 정의해야 합니다. 이니셜라이저를 가질 수 있는 것으로 const static 선언하는 정수 데이터 멤버입니다.

  4. 클래스 선언에서 멤버 함수를 선언 static 할 때 키워드는 함수가 클래스의 모든 인스턴스에서 공유되도록 지정합니다. 함수에 static 암시적 this 포인터가 없으므로 멤버 함수는 인스턴스 멤버에 액세스할 수 없습니다. 인스턴스 멤버에 액세스하려면 인스턴스 포인터 또는 참조인 매개 변수를 사용하여 함수를 선언합니다.

  5. 의 멤버 union 를 선언 static할 수 없습니다. 그러나 전역적으로 선언된 익명 union 을 명시적으로 선언 static해야 합니다.

이 예제에서는 함수에 선언된 static 변수가 해당 함수에 대한 호출 간에 상태를 유지하는 방법을 보여 줍니다.

// static1.cpp
// compile with: /EHsc
#include <iostream>

using namespace std;
void showstat( int curr ) {
   static int nStatic;    // Value of nStatic is retained
                          // between each function call
   nStatic += curr;
   cout << "nStatic is " << nStatic << endl;
}

int main() {
   for ( int i = 0; i < 5; i++ )
      showstat( i );
}
nStatic is 0
nStatic is 1
nStatic is 3
nStatic is 6
nStatic is 10

이 예제에서는 클래스에서의 static 사용을 보여줍니다.

// static2.cpp
// compile with: /EHsc
#include <iostream>

using namespace std;
class CMyClass {
public:
   static int m_i;
};

int CMyClass::m_i = 0;
CMyClass myObject1;
CMyClass myObject2;

int main() {
   cout << myObject1.m_i << endl;
   cout << myObject2.m_i << endl;

   myObject1.m_i = 1;
   cout << myObject1.m_i << endl;
   cout << myObject2.m_i << endl;

   myObject2.m_i = 2;
   cout << myObject1.m_i << endl;
   cout << myObject2.m_i << endl;

   CMyClass::m_i = 3;
   cout << myObject1.m_i << endl;
   cout << myObject2.m_i << endl;
}
0
0
1
1
2
2
3
3

다음 예제에서는 멤버 함수에 선언된 static 지역 변수를 보여줍니다. 변수는 static 전체 프로그램에서 사용할 수 있습니다. 형식의 모든 인스턴스는 변수의 동일한 복사본을 static 공유합니다.

// static3.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;
struct C {
   void Test(int value) {
      static int var = 0;
      if (var == value)
         cout << "var == value" << endl;
      else
         cout << "var != value" << endl;

      var = value;
   }
};

int main() {
   C c1;
   C c2;
   c1.Test(100);
   c2.Test(100);
}
var != value
var == value

C++11 static 부터는 로컬 변수 초기화가 스레드로부터 안전합니다. 이 기능을 매직 정적이라고 도 합니다. 그러나 다중 스레드 애플리케이션에서는 모든 후속 할당을 동기화해야 합니다. CRT에 대한 종속성을 사용하지 않도록 플래그를 /Zc:threadSafeInit- 사용하여 스레드로부터 안전한 정적 초기화 기능을 사용하지 않도록 설정할 수 있습니다.

extern

선언된 extern 개체 및 변수는 다른 변환 단위 또는 묶은 범위에 외부 링크가 있는 것으로 정의된 개체를 선언합니다. 자세한 내용은 번역 단위 및 연결을 참조 extern 하세요.

thread_local (C++11)

지정자를 사용하여 thread_local 선언된 변수는 생성된 스레드에서만 액세스할 수 있습니다. 이 변수는 스레드를 만들 때 만들어지고 스레드가 제거될 때 제거됩니다. 각 스레드에 변수의 자체 복사본이 있습니다. Windows에서는 thread_local Microsoft 특정 __declspec( thread ) 특성과 기능적으로 동일합니다.

thread_local float f = 42.0; // Global namespace. Not implicitly static.

struct S // cannot be applied to type definition
{
    thread_local int i; // Illegal. The member must be static.
    thread_local static char buf[10]; // OK
};

void DoSomething()
{
    // Apply thread_local to a local variable.
    // Implicitly "thread_local static S my_struct".
    thread_local S my_struct;
}

지정자에 대해 유의 thread_local 해야 할 사항:

  • DLL에서 동적으로 초기화된 스레드-로컬 변수는 모든 호출 스레드에서 올바르게 초기화되지 않을 수 있습니다. 자세한 내용은 thread를 참조하세요.

  • thread_local 지정자를 사용하거나 extern.와 결합 static 할 수 있습니다.

  • 데이터 선언 및 정의 thread_local 에만 적용 thread_local 할 수 있으며 함수 선언 또는 정의에는 사용할 수 없습니다.

  • 전역 데이터 개체(및 static ), 로컬 정적 개체 및 extern클래스의 정적 데이터 멤버를 포함하는 정적 스토리지 기간이 있는 데이터 항목에서만 지정할 thread_local 수 있습니다. 선언된 thread_local 모든 지역 변수는 다른 스토리지 클래스가 제공되지 않은 경우 암시적으로 정적입니다. 즉, 블록 범위 thread_local 에서 해당합니다 thread_local static.

  • 스레드 로컬 개체의 선언과 정의가 같은 파일에서 발생하는지, 아니면 별도의 파일에서 발생하는지와 관계없이 해당 선언과 정의 둘 다에 대해 thread_local을 지정해야 합니다.

  • 와 함께 std::launch::async변수를 사용하지 thread_local 않는 것이 좋습니다. 자세한 내용은 함수를 참조 <future> 하세요.

Windows에서는 thread_local * 형식 정의에 __declspec(thread) 적용할 수 있으며 C 코드에서 유효하다 *__declspec(thread)는 점을 제외하면 기능적으로 동일합니다. 가능하면 C++ 표준의 일부이므로 더 이식성이 높기 때문에 사용 thread_local 하세요.

register

Visual Studio 2017 버전 15.3 이상 (모드 이상에서 /std:c++17 사용 가능): 키워드는 register 더 이상 지원되는 스토리지 클래스가 아닙니다. 해당 용도로 인해 진단이 발생합니다. 키워드는 나중에 사용할 수 있는 표준에 계속 예약되어 있습니다.

   register int val; // warning C5033: 'register' is no longer a supported storage class

예: 자동 및 정적 초기화

로컬 자동 개체나 변수는 컨트롤의 흐름이 정의에 도달할 때마다 초기화됩니다. 로컬 정적 개체 또는 변수는 컨트롤의 흐름이 정의에 처음 도달할 때 초기화됩니다.

다음 예제에서는 개체의 초기화 및 개체의 초기화와 소멸을 기록한 후 I1, I2I3 객체를 정의합니다.

// initialization_of_objects.cpp
// compile with: /EHsc
#include <iostream>
#include <string.h>
using namespace std;

// Define a class that logs initializations and destructions.
class InitDemo {
public:
    InitDemo( const char *szWhat );
    ~InitDemo();

private:
    char *szObjName;
    size_t sizeofObjName;
};

// Constructor for class InitDemo
InitDemo::InitDemo( const char *szWhat ) :
    szObjName(NULL), sizeofObjName(0) {
    if ( szWhat != 0 && strlen( szWhat ) > 0 ) {
        // Allocate storage for szObjName, then copy
        // initializer szWhat into szObjName, using
        // secured CRT functions.
        sizeofObjName = strlen( szWhat ) + 1;

        szObjName = new char[ sizeofObjName ];
        strcpy_s( szObjName, sizeofObjName, szWhat );

        cout << "Initializing: " << szObjName << "\n";
    }
    else {
        szObjName = 0;
    }
}

// Destructor for InitDemo
InitDemo::~InitDemo() {
    if( szObjName != 0 ) {
        cout << "Destroying: " << szObjName << "\n";
        delete szObjName;
    }
}

// Enter main function
int main() {
    InitDemo I1( "Auto I1" ); {
        cout << "In block.\n";
        InitDemo I2( "Auto I2" );
        static InitDemo I3( "Static I3" );
    }
    cout << "Exited block.\n";
}
Initializing: Auto I1
In block.
Initializing: Auto I2
Initializing: Static I3
Destroying: Auto I2
Exited block.
Destroying: Auto I1
Destroying: Static I3

이 예제에서는 개체I1I2가 초기화되는 방법과 시기 및 I3 제거 시기를 보여 줍니다.

프로그램에 대해 주의해야 할 몇 가지 사항이 있습니다.

  • 먼저 제어 I1 I2 흐름이 정의된 블록을 종료하면 자동으로 제거됩니다.

  • 둘째, C++에서는 블록의 시작 부분에 개체 또는 변수를 선언할 필요가 없습니다. 또한 제어 흐름이 정의에 도달해야 이 개체가 초기화됩니다. (I2I3 이러한 정의의 예입니다.) 출력은 초기화되는 시기를 정확하게 표시합니다.

  • 마지막으로, 프로그램이 실행되는 동안 해당 값을 유지하지만 프로그램이 종료되면 소멸되는 정 I3 적 지역 변수입니다.

참고 항목

선언 및 정의