정적 키워드와 C ++에서의 다양한 용도


196

키워드 static는 C ++에서 몇 가지 의미가있어서 매우 혼란스럽고 실제로 어떻게 작동 해야하는지 내 마음을 구부릴 수는 없습니다.

내가 이해 한 것으로부터 static스토리지 기간 이 있다는 것은 글로벌의 경우 프로그램 수명 동안 지속되지만 로컬에 대해 이야기 할 때 기본적으로 0으로 초기화됨을 의미합니다.

C ++ 표준은 키워드가있는 클래스 데이터 멤버에 대해 static다음 과 같이 말합니다 .

3.7.1 정적 저장 기간 [basic.stc.static]

3 static 키워드는 정적 저장 기간을 갖는 지역 변수를 선언하는 데 사용할 수 있습니다.

4 클래스 정의에서 클래스 데이터 멤버에 적용된 static 키워드는 데이터 멤버에게 정적 저장 기간을 제공합니다.

지역 변수 란 무엇입니까 ? 함수 로컬 변수입니까? 로컬로 함수를 선언 할 때 static한 번만 초기화 된 것으로 선언 하면이 함수에 처음 들어갑니다.

또한 클래스 멤버와 관련하여 스토리지 기간에 대해서만 이야기합니다. 인스턴스가 아닌 특정 속성은 static아니오 의 속성 입니까? 아니면 저장 기간입니까?

이제 static파일 범위와 파일 범위는 어떻습니까? 모든 전역 변수는 기본적으로 정적 저장 기간을 갖는 것으로 간주됩니까? 다음 섹션 (3.7.1 단원)이이를 나타내는 것으로 보입니다.

1 동적 스토리지 기간이없고 스레드 스토리지 기간이 없고 로컬아닌 모든 변수 에는 정적 스토리지 기간 이 없습니다 . 이 기관들의 보관은 프로그램 기간 동안 지속되어야한다 (3.6.2, 3.6.3)

어떻게 static변수의 연결 관계?

이 모든 static키워드는 솔직한 사람은 영어의 다른 용도를 명확히하고 또한 말해 줄 수, 혼동되는 경우 초기화하는 static클래스 멤버?


답변:


147

변수:

static정의 된 번역 단위 의 "수명"에 대한 변수가 존재합니다 .

  • 네임 스페이스 범위 (예 : 함수 및 클래스 외부)에 있으면 다른 번역 단위에서 액세스 할 수 없습니다. 이것을 "내부 연결"또는 "정적 저장 기간"이라고합니다. (을 제외하고는 헤더 에서이 작업을 수행하지 마십시오 constexpr. 다른 모든 것은 번역 단위마다 별도의 변수로 끝나므로 혼란 스럽습니다)
  • 함수 의 변수 인 경우 다른 로컬 변수와 마찬가지로 함수 외부에서 액세스 할 수 없습니다. (이것은 그들이 언급 한 지역입니다)
  • 클래스 멤버는 (으)로 인해 제한된 범위를 갖지 static않지만 클래스뿐만 아니라 인스턴스 (예 :)에서 해결할 수 있습니다 std::string::npos. [참고 : 클래스에서 정적 멤버를 선언 할 수 있지만 일반적으로 여전히 번역 단위 (cpp 파일) 로 정의 되어야하므로 클래스 당 하나만 있습니다.]

코드로 위치 :

static std::string namespaceScope = "Hello";
void foo() {
    static std::string functionScope= "World";
}
struct A {
   static std::string classScope = "!";
};

변환 단위의 함수가 실행되기 전에 ( main실행이 시작된 후 ) 해당 변환 단위의 정적 저장 기간 (네임 스페이스 범위)이있는 변수는 "일정한 초기화"( constexpr가능한 경우 또는 0이 아닌 경우) 로컬은 번역 단위에 정의 된 순서대로 올바르게 "동적으로 초기화"됩니다 ( std::string="HI";그렇지 않은 경우 constexpr). 마지막으로 함수 로컬 스태틱은 처음 실행될 때 선언 된 행에 도달 할 때 초기화됩니다. 모든 static변수는 초기화의 역순으로 모두 파괴됩니다.

이 모든 권리를 얻는 가장 쉬운 방법은 constexpr초기화 되지 않은 모든 정적 변수 를 함수 정적 로컬로 만드는 것입니다. 이렇게하면 정적 / 글로벌을 사용하려고 할 때 모든 정적 / 전역이 올바르게 초기화 되어 정적 초기화가 방지됩니다. 주문 fiasco .

T& get_global() {
    static T global = initial_value();
    return global;
}

사양은 네임 스페이스 범위 변수는 기본적으로 "정적 저장 기간"가 말할 때, 그들은 조금 "번역 장치의 수명을"을 의미하지만은 않기 때문에, 조심 하지 가 파일의 외부 액세스 할 수 없습니다 의미한다.

기능

훨씬 더 간단하고 static종종 클래스 멤버 함수로 사용되며 독립형 함수에는 거의 사용되지 않습니다.

정적 멤버 함수는 클래스의 인스턴스없이 호출 될 수 있다는 점에서 일반 멤버 함수와 다릅니다. 인스턴스가 없으므로 클래스의 비 정적 멤버에 액세스 할 수 없습니다. 정적 변수는 인스턴스 멤버를 절대적으로 참조하지 않는 클래스 또는 static멤버 변수 를 관리하는 함수를 원할 때 유용 합니다.

struct A {
    A() {++A_count;}
    A(const A&) {++A_count;}
    A(A&&) {++A_count;}
    ~A() {--A_count;}

    static int get_count() {return A_count;}
private:
    static int A_count;
}

int main() {
    A var;

    int c0 = var.get_count(); //some compilers give a warning, but it's ok.
    int c1 = A::get_count(); //normal way
}

static자유 함수는 함수가 다른 변환 부에 의해 참조 될 것이며, 따라서, 링커를 완전히 무시할 수 있다는 것을 의미한다. 이것은 몇 가지 목적이 있습니다 :

  • 함수가 다른 파일에서 절대 사용되지 않도록 cpp 파일에서 사용할 수 있습니다.
  • 헤더에 넣을 수 있으며 모든 파일에는 자체 함수 사본이 있습니다. 인라인은 거의 같은 일을하기 때문에 유용하지 않습니다.
  • 작업 감소로 링크 시간 단축
  • 각 번역 단위에 같은 이름의 함수를 넣을 수 있으며 모두 다른 일을 할 수 있습니다. 예를 들어, static void log(const char*) {}각 cpp 파일에를 넣고 각각 다른 방식으로 기록 할 수 있습니다.

1
반원은 어떻습니까? 세 번째 별도 사례가 아닙니까?
Étienne

4
@Etienne-정적 클래스 데이터 멤버는 다른 변환 단위에서 액세스 할 수 있다는 점을 제외하고 정적 전역 변수와 동일하며 모든 액세스 (멤버 함수 제외)는 classname::범위 를 지정해야합니다 . 정적 클래스 멤버 함수는 전역 함수와 비슷하지만 클래스 범위로 지정되거나 일반 멤버와 비슷하지만 this선택 사항은 아닙니다 (이 둘은 동일해야 함).
Steve314

1
@ LuchianGrigore : 당신의 요점을 볼 때, 어떤 단어를 사용 해야할지 모르겠습니다.
Mooing Duck

1
@ Steve314 : 나는 당신이 의미하는 바를 이해하지만 너무 심하게 과부하 된 용어를 static 으로 처리 할 때 우리 모두 조금 더주의를 기울 였기를 바랍니다. 특히 모든 글로벌 (실제로 공간 레벨) 변수 정도로 정적 추가 정적 기간이 정적 글로벌 변수 로서 이해 될 수있다 namespace A { static int x; }즉, 내부 결합을 하고 동작 매우 다른 정적 클래스의 데이터 멤버 .
David Rodríguez-dribeas

1
"네임 스페이스 범위에있는 경우 다른 번역 단위에서 액세스 할 수 없습니다 ..."네임 스페이스 범위에있는 경우 무엇을 의미합니까? 항상 그렇지 않습니까? 예와 반대 예를 들어 주시겠습니까?
AturSams

66

정적 저장 기간은 변수가 프로그램 수명 동안 메모리의 동일한 위치에 상주 함을 의미합니다.

연계는 이것과 직교합니다.

이것이 당신이 만들 수있는 가장 중요한 차이점이라고 생각합니다. @Tony를 직접 다루지는 않지만 앞으로 이것을 읽을 수있는 사람이라면 누구나 쉽게 이해할 수있을뿐만 아니라이 내용과 나머지 내용을 이해해야합니다.

키워드 static는 내부 연결 정적 저장소 를 나타내는 데 사용할 수 있지만 본질적으로 다릅니다.

지역 변수 란 무엇입니까? 함수 로컬 변수입니까?

예. 변수가 초기화 될 때 (함수를 처음 호출 할 때와 실행 경로가 선언 지점에 도달 할 때)에 관계없이 프로그램 수명 동안 메모리의 동일한 위치에 상주합니다. 이 경우 static정적 저장소를 제공합니다.

정적 및 파일 범위의 경우는 어떻습니까? 모든 전역 변수는 기본적으로 정적 저장 기간을 갖는 것으로 간주됩니까?

그렇습니다. 모든 글로벌은 정적 스토리지 기간을 정의합니다 (이제 의미하는 것을 정리했습니다). 그러나 네임 스페이스 범위 변수는 static내부 링크를 제공하므로 번역 단위 당 변수가 있기 때문에로 선언되지 않습니다 .

정적은 변수의 연결과 어떤 관련이 있습니까?

네임 스페이스 범위 변수에 내부 연결을 제공합니다. 멤버 및 로컬 변수에 정적 저장 기간을 제공합니다.

이 모든 것을 확장하자 :

//

static int x; //internal linkage
              //non-static storage - each translation unit will have its own copy of x
              //NOT A TRUE GLOBAL!

int y;        //static storage duration (can be used with extern)
              //actual global
              //external linkage
struct X
{
   static int x;     //static storage duration - shared between class instances 
};

void foo()
{
   static int x;     //static storage duration - shared between calls
}

이 정적 키워드는 완전히 혼란 스럽습니다.

확실히, 익숙하지 않은 이상. :) 언어에 새로운 키워드를 추가하지 않기 위해위원회는이 키워드를 IMO로 재사용하여 혼란에 빠뜨 렸습니다. 그것은 다른 것들을 의미하는 데 사용됩니다 (아마도 반대하는 것 같습니다).


1
static int x네임 스페이스 범위에서 말할 때 비 정적 스토리지 를 제공 한다고 말하고 있습니까?
Michael Hagar

31

질문을 명확히하기 위해 '정적'키워드 사용법을 세 가지 형식으로 분류하려고합니다.

(ㅏ). 변수

(비). 기능

(씨). 클래스의 멤버 변수 / 함수

각 소제목에 대한 설명은 다음과 같습니다.

(A) 변수에 대한 '정적'키워드

이 설명은 조금 까다로울 수 있지만 올바르게 설명하고 이해하면 매우 간단합니다.

이것을 설명하기 위해서는 먼저 변수 의 범위, 지속 시간 및 연결 에 대해 아는 것이 매우 유용 합니다.

1. 범위 : 파일에서 변수에 액세스 할 수있는 위치를 결정합니다. (i) 로컬 또는 블록 범위 의 두 가지 유형이 있습니다 . (ii) 글로벌 범위

2. Duration : 변수가 생성되고 파괴되는시기를 결정합니다. 다시 두 가지 유형이 있습니다. (i) 자동 저장 기간 (로컬 또는 블록 범위가있는 변수의 경우). (ii) 정적 저장 기간 (글로벌 스코프를 갖는 변수 또는 정적 지정자가 있는 로컬 변수 (함수 또는 코드 블록) ).

3. 링크 : 다른 파일에서 변수에 액세스 (또는 링크) 할 수 있는지 여부를 결정합니다. 다시 말하지만 운 좋게도 (i) 내부 연결 (블록 범위 및 전역 범위 / 파일 범위 / 글로벌 네임 스페이스 범위 가있는 변수의 경우 ) (ii) 외부 연결 (전역 범위 / 파일 범위 / 글로벌 네임 스페이스 범위)

일반 전역 및 지역 변수에 대한 이해를 돕기 위해 아래 예를 참조하십시오 (정적 저장 시간을 갖는 지역 변수 없음).

//main file
#include <iostream>

int global_var1; //has global scope
const global_var2(1.618); //has global scope

int main()
{
//these variables are local to the block main.
//they have automatic duration, i.e, they are created when the main() is 
//  executed and destroyed, when main goes out of scope
 int local_var1(23);
 const double local_var2(3.14);

 {
/* this is yet another block, all variables declared within this block are 
 have local scope limited within this block. */
// all variables declared within this block too have automatic duration, i.e, 
/*they are created at the point of definition within this block,
 and destroyed as soon as this block ends */
   char block_char1;
   int local_var1(32) //NOTE: this has been re-declared within the block, 
//it shadows the local_var1 declared outside

 std::cout << local_var1 <<"\n"; //prints 32

  }//end of block
  //local_var1 declared inside goes out of scope

 std::cout << local_var1 << "\n"; //prints 23

 global_var1 = 29; //global_var1 has been declared outside main (global scope)
 std::cout << global_var1 << "\n"; //prints 29
 std::cout << global_var2 << "\n"; //prints 1.618

 return 0;
}  //local_var1, local_var2 go out of scope as main ends
//global_var1, global_var2 go out of scope as the program terminates 
//(in this case program ends with end of main, so both local and global
//variable go out of scope together

이제 Linkage의 개념이 온다. 한 파일에 정의 된 전역 변수를 다른 파일에 사용하려는 경우 변수의 연결이 중요한 역할을합니다.

전역 변수의 연결은 키워드 (i) static 및 (ii) extern으로 지정됩니다.

(이제 설명을 얻습니다)

정적 키워드는 지역 및 전역 범위의 변수에 적용 할 수 있으며 두 경우 모두 서로 다른 의미를 갖습니다. 먼저 전역 범위가있는 변수에서 '정적'키워드 사용법 (키워드 'extern'의 사용법을 명확히 함)과 나중에 로컬 범위가있는 사람들에 대한 설명을 설명합니다.

1. 전역 범위를 가진 변수에 대한 정적 키워드

전역 변수는 정적 지속 시간을 가지며, 이는 사용되는 특정 코드 블록 (예 : main ())이 끝날 때 범위를 벗어나지 않음을 의미합니다. 연결에 따라, 선언 된 파일 (정적 전역 변수의 경우) 또는 선언 된 파일 외부 (외부 유형의 전역 변수) 외부의 파일에만 액세스 할 수 있습니다.

extern 지정자를 갖는 글로벌 변수의 경우,이 변수가 초기화 된 파일 외부에서 액세스되는 경우, 함수가 전달되어야하는 것처럼 사용중인 파일에서 전달 선언되어야합니다. 정의가 사용중인 위치와 다른 파일에있는 경우 선언됩니다.

반대로 전역 변수에 static 키워드가 있으면 선언 된 파일 외부에서는 사용할 수 없습니다.

(설명을 위해 아래 예를 참조하십시오)

예 :

//main2.cpp
 static int global_var3 = 23;  /*static global variable, cannot be                            
                                accessed in anyother file */
 extern double global_var4 = 71; /*can be accessed outside this file                  linked to main2.cpp */
 int main() { return 0; }

main3.cpp

//main3.cpp
#include <iostream>

int main()
{
   extern int gloabl_var4; /*this variable refers to the gloabal_var4
                            defined in the main2.cpp file */
  std::cout << global_var4 << "\n"; //prints 71;

  return 0;
}

이제 c ++의 모든 변수는 const 또는 non-const 일 수 있으며 각 'const-ness'에 대해 두 가지 기본 c ++ 연결이 발생합니다 (아무도 지정되지 않은 경우).

(i) 전역 변수가 Const가 아닌 경우 기본적으로 해당 연결은 extern입니다 . 즉 extern 키워드를 사용하여 선언을 통해 다른 .cpp 파일에서 non-const 전역 변수에 액세스 할 수 있습니다 (즉, const가 아닌 전역 변수에는 외부 링크가 있습니다 (정적 지속 시간 포함). 또한 정의 된 원본 파일에서 extern 키워드 사용은 중복됩니다. 이 경우 비 const 전역 변수를 외부 파일에 액세스 할 수 없게하려면 변수 유형 앞에 지정자 'static'을 사용하십시오 .

(ii) 전역 변수가 const 인 경우 기본적으로 연결은 정적입니다 . 즉, const 전역 변수는 정의 된 위치 이외의 파일에서 액세스 할 수 없습니다 (즉, const 전역 변수에는 내부 연결이 있습니다 (정적 지속 시간 포함) 물론이야)). 또한 정적 키워드를 사용하여 const 전역 변수가 다른 파일에서 액세스되는 것을 방지합니다. 여기서 const 전역 변수에 외부 연결을 만들려면 변수 유형 앞에 지정자 'extern'을 사용하십시오.

다음은 다양한 연계가있는 전역 범위 변수에 대한 요약입니다.

//globalVariables1.cpp 

// defining uninitialized vairbles
int globalVar1; //  uninitialized global variable with external linkage 
static int globalVar2; // uninitialized global variable with internal linkage
const int globalVar3; // error, since const variables must be initialized upon declaration
const int globalVar4 = 23; //correct, but with static linkage (cannot be accessed outside the file where it has been declared*/
extern const double globalVar5 = 1.57; //this const variable ca be accessed outside the file where it has been declared

다음으로 다른 파일에서 액세스 할 때 위의 전역 변수가 어떻게 작동하는지 조사합니다.

//using_globalVariables1.cpp (eg for the usage of global variables above)

// Forward declaration via extern keyword:
 extern int globalVar1; // correct since globalVar1 is not a const or static
 extern int globalVar2; //incorrect since globalVar2 has internal linkage
 extern const int globalVar4; /* incorrect since globalVar4 has no extern 
                         specifier, limited to internal linkage by
                         default (static specifier for const variables) */
 extern const double globalVar5; /*correct since in the previous file, it 
                           has extern specifier, no need to initialize the
                       const variable here, since it has already been
                       legitimately defined perviously */

2. 지역 범위가있는 변수에 대한 정적 키워드

로컬 범위의 변수에 대한 정적 키워드 업데이트 (2019 년 8 월)

이를 두 가지 범주로 세분화 할 수 있습니다.

(i) 함수 블록 내의 변수에 대한 정적 키워드(ii) 명명되지 않은 로컬 블록 내의 변수에 대한 정적 키워드

(i) 펑션 블록 내의 변수에 대한 정적 키워드.

이전, 내가 로컬 범위를 가진 변수는 블록이 입력 될 때 즉, 그들이 존재하는 온, 자동 기간을 언급합니다 (함수 블록 수, 그것을 정상적인 블록 수)와 멸망 할 때 블록의 끝, 긴 이야기의 짧은 변수 로컬 범위에는 자동 지속 시간이 있고 자동 지속 기간 변수 (및 객체)에는 연결이 없으므로 코드 블록 외부에서는 볼 수 없습니다.

경우 정적 지정자 기능 블록 내의 로컬 변수로 도포하고, 그것을 정적 자동에서 변수의 시간 변화 와 수명 시간은 고정 된 메모리 위치를 갖는 수단 프로그램의 전체 기간이고, 그 값은 초기화 cpp 참조에 언급 된대로 프로그램 시작 전에 한 번 (초기화를 할당과 혼동해서는 안 됨)

예를 살펴 보겠습니다.

//localVarDemo1.cpp    
 int localNextID()
{
  int tempID = 1;  //tempID created here
  return tempID++; //copy of tempID returned and tempID incremented to 2
} //tempID destroyed here, hence value of tempID lost

int newNextID()
{
  static int newID = 0;//newID has static duration, with internal linkage
  return newID++; //copy of newID returned and newID incremented by 1
}  //newID doesn't get destroyed here :-)


int main()
{
  int employeeID1 = localNextID();  //employeeID1 = 1
  int employeeID2 = localNextID();  // employeeID2 = 1 again (not desired)
  int employeeID3 = newNextID(); //employeeID3 = 0;
  int employeeID4 = newNextID(); //employeeID4 = 1;
  int employeeID5 = newNextID(); //employeeID5 = 2;
  return 0;
}

정적 지역 변수와 정적 전역 변수에 대한 위의 기준을 살펴보면 그들 사이의 차이점이 무엇인지 묻고 싶을 수도 있습니다. 전역 변수는 코드 내 어느 시점에서나 액세스 가능하지만 ( const- ness 및 extern- ness 에 따라 다른 변환 단위와 동일 하지만) 함수 블록 내에 정의 된 정적 변수는 직접 액세스 할 수 없습니다. 변수는 함수 값 또는 참조에 의해 반환되어야합니다. 이를 예로 들어 설명해 보겠습니다.

//localVarDemo2.cpp 

//static storage duration with global scope 
//note this variable can be accessed from outside the file
//in a different compilation unit by using `extern` specifier
//which might not be desirable for certain use case.
static int globalId = 0;

int newNextID()
{
  static int newID = 0;//newID has static duration, with internal linkage
  return newID++; //copy of newID returned and newID incremented by 1
}  //newID doesn't get destroyed here


int main()
{
    //since globalId is accessible we use it directly
  const int globalEmployee1Id = globalId++; //globalEmployeeId1 = 0;
  const int globalEmployee2Id = globalId++; //globalEmployeeId1 = 1;

  //const int employeeID1 = newID++; //this will lead to compilation error since newID++ is not accessible direcly. 
  int employeeID2 = newNextID(); //employeeID3 = 0;
  int employeeID2 = newNextID(); //employeeID3 = 1;

  return 0;
}

정적 전역 변수와 정적 로컬 변수 선택에 대한 자세한 설명은이 스택 오버 스레드 에서 찾을 수 있습니다

(ii) 명명되지 않은 로컬 블록 내의 변수에 대한 정적 키워드.

로컬 블록이 범위를 벗어나면 로컬 블록 (함수 블록이 아님) 내의 정적 변수에 액세스 할 수 없습니다. 이 규칙에 대한 경고는 없습니다.

    //localVarDemo3.cpp 
    int main()
    {

      {
          const static int static_local_scoped_variable {99};
      }//static_local_scoped_variable goes out of scope

      //the line below causes compilation error
      //do_something is an arbitrary function
      do_something(static_local_scoped_variable);
      return 0;
    }

C ++ 11 constexpr은 컴파일 타임에 표현식의 평가를 보장하고 컴파일러가 코드를 최적화 할 수 있는 키워드 를 도입했습니다 . 스코프 내의 정적 const 변수의 값이 컴파일 타임에 알려진 경우 코드는와 유사한 방식으로 최적화됩니다 constexpr. 여기 작은 예가 있습니다

또한 이 stackoverflow 스레드 에서 변수 의 차이 constexprstatic const변수 를 찾아 보는 것이 좋습니다 . 이것으로 변수에 적용된 정적 키워드에 대한 설명을 마칩니다.

B. 기능에 사용되는 '정적'키워드

함수 측면에서 정적 키워드는 간단한 의미를 갖습니다. 여기서는 함수의 링크를 의미합니다. 일반적으로 cpp 파일 내에 선언 된 모든 함수는 기본적으로 외부 링크를 갖습니다. 즉, 한 파일에 정의 된 함수는 정방향 선언으로 다른 cpp 파일에서 사용할 수 있습니다.

함수 선언 전에 static 키워드를 사용하면 internal 로의 연결이 제한됩니다 . 즉, 정의 외부의 파일에서는 정적 함수를 사용할 수 없습니다.

C. Staitc 키워드는 멤버 변수와 클래스 함수에 사용됩니다.

1. 클래스의 멤버 변수에 대한 '정적'키워드

여기 예제로 직접 시작합니다

#include <iostream>

class DesignNumber
{
  private:

      static int m_designNum;  //design number
      int m_iteration;     // number of iterations performed for the design

  public:
    DesignNumber() {     }  //default constructor

   int  getItrNum() //get the iteration number of design
   {
      m_iteration = m_designNum++;
      return m_iteration;
   }
     static int m_anyNumber;  //public static variable
};
int DesignNumber::m_designNum = 0; // starting with design id = 0
                     // note : no need of static keyword here
                     //causes compiler error if static keyword used
int DesignNumber::m_anyNumber = 99; /* initialization of inclass public 
                                    static member  */
enter code here

int main()
{
   DesignNumber firstDesign, secondDesign, thirdDesign;
   std::cout << firstDesign.getItrNum() << "\n";  //prints 0
   std::cout << secondDesign.getItrNum() << "\n"; //prints 1
   std::cout << thirdDesign.getItrNum() << "\n";  //prints 2

   std::cout << DesignNumber::m_anyNumber++ << "\n";  /* no object
                                        associated with m_anyNumber */
   std::cout << DesignNumber::m_anyNumber++ << "\n"; //prints 100
   std::cout << DesignNumber::m_anyNumber++ << "\n"; //prints 101

   return 0;
}

이 예에서 정적 변수 m_designNum은 값을 유지하고이 단일 개인 멤버 변수 (정적이므로)는 객체 유형 DesignNumber의 모든 변수와 공유됩니다.

또한 다른 멤버 변수와 마찬가지로 클래스의 정적 멤버 변수는 클래스 함수와 관련이 없으며 기본 함수에서 anyNumber를 인쇄하여 시연됩니다.

클래스의 const 대 비 const 정적 멤버 변수

(i) 비 const 클래스 정적 멤버 변수 앞의 예제에서 정적 멤버 (공개 및 개인 모두)는 상수가 아니 었습니다. ISO 표준은 비 const 정적 멤버가 클래스에서 초기화되는 것을 금지합니다. 따라서 이전 예와 같이 정적 키워드를 생략해야한다는 경고와 함께 클래스 정의 후에 초기화해야합니다.

(ⅱ) 급의 CONST 정적 멤버 변수 이 간단하고 다른 CONST 멤버 변수 초기화 협약으로 이동이 클래스 즉 CONST 정적 멤버 변수가 될 수 선언 시점에서 초기화되고 이들은 결국 초기화 될 수있다 클래스 정의 후에 초기화 될 때 키워드 const를 정적 멤버에 추가해야한다는 한 가지 경고가있는 클래스 선언

그러나 선언 시점에서 const 정적 멤버 변수를 초기화하는 것이 좋습니다. 이것은 표준 C ++ 규칙을 따르며 코드를 더 깨끗하게 만듭니다.

클래스의 정적 멤버 변수에 대한 자세한 예는 learncpp.com http://www.learncpp.com/cpp-tutorial/811-static-member-variables/ 에서 다음 링크를 찾으십시오 .

2. 클래스의 멤버 함수를위한 '정적'키워드

클래스의 멤버 변수는 정적 일 수 있으므로 클래스의 멤버 함수도 가능합니다. 클래스의 일반 멤버 함수는 항상 클래스 유형의 객체와 연결됩니다. 반대로, 클래스의 정적 멤버 함수는 클래스의 어떤 객체와도 관련이 없습니다. 즉,이 포인터가 없습니다.

둘째, 클래스의 정적 멤버 함수에는 * this 포인터가 없으므로 기본 함수 (ClassName :: functionName ();)에서 클래스 이름 및 범위 분석 연산자를 사용하여 호출 할 수 있습니다.

셋째, 클래스의 정적 멤버 함수는 클래스의 정적 멤버 변수에만 액세스 할 수 있습니다. 클래스의 비 정적 멤버 변수는 클래스 객체에 속해야하기 때문입니다.

클래스의 정적 멤버 함수에 대한 자세한 예는 learncpp.com에서 다음 링크를 찾으십시오.

http://www.learncpp.com/cpp-tutorial/812-static-member-functions/


1
1) c ++ 17 이전에는 정적 정적 const 멤버 변수 만 클래스에서 초기화 할 수 있습니다 (예 struct Foo{static const std::string name = "cpp";};: 오류) name. 클래스 외부에서 정의해야합니다. c ++ 17에 도입 된 인라인 변수를 사용하면 다음과 같은 코드를 작성할 수 있습니다. struct Foo{static inline const std::string name = "cpp";};2) 범위 분석 연산자를 사용하는 클래스 이름과 도트 연산자가있는 인스턴스 (예 : instance.some_static_method ())
oz1

"m_anyVariable"이 "m_anyNumber"가되어서는 안됩니까? 마지막 코드 예제에서?
gebbissimo

답변의 완전성과 정확성을 판단 할 수는 없지만 실제로는 포괄적이며 이해하기 쉽습니다. 고마워요! 당신이 그것을 향상시키고 싶다면, 처음에 짧은 요약은 매우 긴 텍스트이기 때문에 주요 내용은 "내부 / 외부와 같은 용어를 알고있는 사람들을위한 중첩 된 목록 또는 트리 다이어그램으로 쉽게 시각화 될 수 있기 때문에 유익 할 수 있습니다 linkage "
gebbissimo

18

실제로는 매우 간단합니다. 함수 범위에서 변수를 정적으로 선언하면 해당 함수에 대한 연속 호출간에 값이 유지됩니다. 그래서:

int myFun()
{
static int i=5;
i++;
return i;
}
int main()
{
printf("%d", myFun());
printf("%d", myFun());
printf("%d", myFun());
}

증가 된 값을 기억하기 때문에 678대신에 표시 됩니다 666.

정적 멤버는 클래스의 인스턴스에서 값을 유지합니다. 따라서 다음 코드는

struct A
{
static int a;
};
int main()
{
A first;
A second;
first.a = 3;
second.a = 4;
printf("%d", first.a);
}

first.a와 second.a는 본질적으로 동일한 변수이기 때문에 4를 인쇄합니다. 초기화에 대해서는 이 질문을 참조하십시오 .


네임 스페이스 범위 변수는 다루지 않습니다.
Michael Hagar

10

static파일 범위에서 변수를 선언하면 해당 변수는 해당 특정 파일 (기술적으로 * 번역 단위 )에서만 사용할 수 있지만 너무 복잡하지는 않습니다. 예를 들면 다음과 같습니다.

a.cpp

static int x = 7;

void printax()
{
    cout << "from a.cpp: x=" << x << endl;
}

b.cpp

static int x = 9;

void printbx()
{
    cout << "from b.cpp: x=" << x << endl;
}

main.cpp :

int main(int, char **)
{
    printax(); // Will print 7
    printbx(); // Will print 9

    return 0;
}

A에 대한 로컬 변수 static수단은, 변수는 0으로 초기화 될 것으로 하고 호출간에 그 값을 유지 :

unsigned int powersoftwo()
{
    static unsigned lastpow;

    if(lastpow == 0)
        lastpow = 1;
    else
        lastpow *= 2;

    return lastpow;
}

int main(int, char **)
{
    for(int i = 0; i != 10; i++)
        cout << "2^" << i << " = " << powersoftwo() << endl;
}

들어 클래스 변수, 그 클래스의 모든 구성원간에 공유되는 변수의 단일 인스턴스가 있음을 의미합니다. 권한에 따라 정규화 된 이름을 사용하여 클래스 외부에서 변수에 액세스 할 수 있습니다.

class Test
{
private:
    static char *xxx;

public:
    static int yyy;

public:
    Test()
    {        
        cout << this << "The static class variable xxx is at address "
             << static_cast<void *>(xxx) << endl;
        cout << this << "The static class variable yyy is at address "
             << static_cast<void *>(&y) << endl;
    }
};

// Necessary for static class variables.
char *Test::xxx = "I'm Triple X!";
int Test::yyy = 0;

int main(int, char **)
{
    Test t1;
    Test t2;

    Test::yyy = 666;

    Test t3;
};

비 클래스 함수를 표시 static하면 해당 파일에서만 액세스 할 수 있고 다른 파일에서는 액세스 할 수 없습니다.

a.cpp

static void printfilename()
{ // this is the printfilename from a.cpp - 
  // it can't be accessed from any other file
    cout << "this is a.cpp" << endl;
}

b.cpp

static void printfilename()
{ // this is the printfilename from b.cpp - 
  // it can't be accessed from any other file
    cout << "this is b.cpp" << endl;
}

클래스 멤버 함수의 경우, 함수로 표시 static하면 개체의 특정 인스턴스에서 함수를 호출 할 필요가 없습니다 (예 : this포인터 가 없음 ).

class Test
{
private:
    static int count;

public:
    static int GetTestCount()
    {
        return count;
    };

    Test()
    {
        cout << this << "Created an instance of Test" << endl;
        count++;
    }

    ~Test()
    {
        cout << this << "Destroyed an instance of Test" << endl;
        count--;
    }
};

int Test::count = 0;

int main(int, char **)
{
    Test *arr[10] = { NULL };

    for(int i = 0; i != 10; i++)
        arr[i] = new Test();

    cout << "There are " << Test::GetTestCount() << " instances of the Test class!" << endl;

    // now, delete them all except the first and last!
    for(int i = 1; i != 9; i++)
        delete arr[i];        

    cout << "There are " << Test::GetTestCount() << " instances of the Test class!" << endl;

    delete arr[0];

    cout << "There are " << Test::GetTestCount() << " instances of the Test class!" << endl;

    delete arr[9];

    cout << "There are " << Test::GetTestCount() << " instances of the Test class!" << endl;

    return 0;
}

8

정적 변수는 클래스마다 고유 한 변수를 갖지 않고 클래스의 모든 인스턴스간에 공유됩니다.

class MyClass
{
    public:
    int myVar; 
    static int myStaticVar;
};

//Static member variables must be initialized. Unless you're using C++11, or it's an integer type,
//they have to be defined and initialized outside of the class like this:
MyClass::myStaticVar = 0;

MyClass classA;
MyClass classB;

'MyClass'의 각 인스턴스에는 자체 'myVar'가 있지만 동일한 'myStaticVar'을 공유합니다. 실제로 'myStaticVar'에 액세스하기 위해 MyClass 인스턴스가 필요하지 않으며 다음과 같이 클래스 외부에서 액세스 할 수 있습니다.

MyClass::myStaticVar //Assuming it's publicly accessible.

함수 내부에서 클래스 멤버 변수가 아닌 로컬 변수로 사용될 때 정적 키워드는 다른 작업을 수행합니다. 전역 범위를 제공하지 않고 지속적 변수를 작성할 수 있습니다.

int myFunc()
{
   int myVar = 0; //Each time the code reaches here, a new variable called 'myVar' is initialized.
   myVar++;

   //Given the above code, this will *always* print '1'.
   std::cout << myVar << std::endl;

   //The first time the code reaches here, 'myStaticVar' is initialized. But ONLY the first time.
   static int myStaticVar = 0;

   //Each time the code reaches here, myStaticVar is incremented.
   myStaticVar++;

   //This will print a continuously incrementing number,
   //each time the function is called. '1', '2', '3', etc...
   std::cout << myStaticVar << std::endl;
}

지속성 측면에서 전역 변수이지만 범위 / 접근성 측면에서 전역 적이 지 않습니다.

정적 멤버 함수를 가질 수도 있습니다. 정적 함수는 기본적으로 멤버가 아닌 함수이지만 클래스 이름의 네임 스페이스 내에 있으며 클래스 멤버에 대한 개인 액세스 권한이 있습니다.

class MyClass
{
    public:
    int Func()
    {
        //...do something...
    }

    static int StaticFunc()
    {
        //...do something...
    }
};

int main()
{
   MyClass myClassA;
   myClassA.Func(); //Calls 'Func'.
   myClassA.StaticFunc(); //Calls 'StaticFunc'.

   MyClass::StaticFunc(); //Calls 'StaticFunc'.
   MyClass::Func(); //Error: You can't call a non-static member-function without a class instance!

   return 0;
}

멤버 함수를 호출하면 'this'라는 숨겨진 매개 변수가 있으며 이는 함수를 호출하는 클래스의 인스턴스에 대한 포인터입니다. 정적 멤버 함수 에는 숨겨진 매개 변수 가 없습니다 ... 클래스 인스턴스 없이는 호출 가능하지만 클래스의 비 정적 멤버 변수에는 액세스 할 수없는 'this'포인터가 없으므로 액세스 할 수 없습니다. 특정 클래스 인스턴스에서 호출되지 않습니다.


1
"공개적으로 접근 할 수 있다고 가정합니다." -아니에요
Luchian Grigore

2
myStaticVar또한 정의되어야합니다. static키워드 의 의미에 대한 질문에 대답 할 때 생각하지 않습니까?
Praetorian

@Praetorian : 고마워요.
Jamin Grey

1
@JaminGrey "정적 독립형"이란 정적 비 멤버 함수를 의미하며, 현재 CPP 파일에만 새로운 기능이 필요할 때마다 링커가 추가 기호를 처리하지 않기를 원합니다.
VR

1
@VR 홀수! 기능이 존재 한다는 것을 결코 알지 못했습니다 . 내 지식을 넓혀 주셔서 감사합니다!
Jamin Grey

1

나는 C 프로그래머가 아니기 때문에 C 프로그램에서 static의 사용법에 대한 정보를 올바르게 줄 수는 없지만 Object Oriented programming에 관해서는 static은 기본적으로 변수 또는 함수 또는 클래스를 동일하게 선언합니다 프로그램 수명 동안 예를 들어 보자.

class A
{
public:
    A();
    ~A();
    void somePublicMethod();
private:
    void somePrivateMethod();
};

Main에서이 클래스를 인스턴스화하면 이와 같은 작업을 수행합니다.

int main()
{
   A a1;
   //do something on a1
   A a2;
   //do something on a2
}

이 두 클래스 인스턴스는 서로 완전히 다르며 서로 독립적으로 작동합니다. 그러나 이런 식으로 클래스 A를 다시 만들려면

class A
{
public:
    A();
    ~A();
    void somePublicMethod();
    static int x;
private:
    void somePrivateMethod();
};

다시 메인으로 돌아 갑시다.

int main()
{
   A a1;
   a1.x = 1;
   //do something on a1
   A a2;
   a2.x++;
   //do something on a2
}

그러면 a1과 a2는 동일한 int x 복사본을 공유하므로 a1의 x에 대한 모든 작업은 a2의 x에 직접 영향을 미칩니다. 내가 이걸하면

int main()
{
   A a1;
   a1.x = 1;
   //do something on a1
   cout << a1.x << endl; //this would be 1
   A a2;
   a2.x++;
   cout << a2.x << endl; //this would be 2 
   //do something on a2
}

클래스 A의 두 인스턴스는 정적 변수와 함수를 공유합니다. 이것이 귀하의 질문에 대답하기를 바랍니다. C에 대한 나의 제한된 지식은 함수 또는 변수를 정적으로 정의한다는 것은 함수 또는 변수가 정적으로 정의 된 파일에서만 볼 수 있음을 의미합니다. 그러나 이것은 C 사람이 더 잘 대답 할 것입니다. C ++을 사용하면 C와 완전히 호환되는 C 및 C ++ 방식으로 변수를 정적으로 선언 할 수 있습니다.


1

지역 변수 란 무엇입니까? 함수 로컬 변수입니까?

예-함수 로컬 변수와 같은 비전 역입니다.

또한 local 함수를 static으로 선언하면 함수가 한 번만 초기화된다고 선언 할 때이 함수에 처음 들어갑니다.

권리.

또한 클래스 멤버와 관련하여 스토리지 지속 시간에 대해서만 이야기합니다. 인스턴스에 국한되지 않고 정적 no의 속성입니까? 아니면 저장 기간입니까?

class R { static int a; }; // << static lives for the duration of the program

말을하는 것입니다, 모든 인스턴스 R공유가 int R::a- int R::a복사되지 않습니다.

정적 및 파일 범위의 경우는 어떻습니까?

효과적으로 전역적인 생성자 / 소멸자를 가진 전역-초기화가 접근 할 때까지 지연되지 않습니다.

정적은 변수의 연결과 어떤 관련이 있습니까?

로컬 함수의 경우 외부입니다. 액세스 : 함수에 액세스 할 수 있습니다 (물론 반환하지 않는 한).

수업의 경우 외부입니다. 액세스 : 표준 액세스 지정자가 적용됩니다 (공개, 보호, 개인).

static 선언 된 위치 (파일 / 네임 스페이스)에 따라 내부 연결을 지정할 수도 있습니다.

이 정적 키워드는 완전히 혼란 스럽습니다.

C ++에서 너무 많은 목적을 가지고 있습니다.

누군가 영어의 다른 용도를 명확히하고 정적 클래스 멤버를 언제 초기화 할 수 있습니까?

main로드되고 생성자가 있으면 자동으로 초기화됩니다 . 그것은 좋은 것처럼 들리지만 초기화 순서는 대부분 제어 할 수 없으므로 복잡한 초기화는 유지 관리가 매우 어려워지고 최소화해야합니다. 정적이 있어야하는 경우 라이브러리 및 프로젝트. 정적 저장 기간이있는 데이터의 경우 특히 변경 가능한 경우 (글로벌 변수)이 디자인을 최소화해야합니다. 초기화 '시간'도 여러 가지 이유로 달라집니다. 로더와 커널에는 문제의 데이터에 따라 메모리 풋 프린트를 최소화하고 초기화를 연기하는 몇 가지 트릭이 있습니다.


1

정적 객체 : 정적 키워드를 사용하여 클래스 멤버를 정적으로 정의 할 수 있습니다. 클래스의 멤버를 정적으로 선언하면 클래스의 오브젝트 수에 관계없이 정적 멤버의 사본이 하나만 있음을 의미합니다.

정적 멤버는 클래스의 모든 객체에서 공유됩니다. 다른 초기화가 없으면 첫 번째 객체를 만들 때 모든 정적 데이터가 0으로 초기화됩니다. 클래스 정의에 넣을 수는 없지만 다음 예제에서와 같이 범위 확인 연산자 ::를 사용하여 정적 변수를 다시 선언하여 클래스 외부에 초기화하여 클래스가 속하는 클래스를 식별 할 수 있습니다.

정적 데이터 멤버의 개념을 이해하기 위해 다음 예제를 시도해 보겠습니다.

#include <iostream>

using namespace std;

class Box
{
   public:
      static int objectCount;
      // Constructor definition
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
         // Increase every time object is created
         objectCount++;
      }
      double Volume()
      {
         return length * breadth * height;
      }
   private:
      double length;     // Length of a box
      double breadth;    // Breadth of a box
      double height;     // Height of a box
};

// Initialize static member of class Box
int Box::objectCount = 0;

int main(void)
{
   Box Box1(3.3, 1.2, 1.5);    // Declare box1
   Box Box2(8.5, 6.0, 2.0);    // Declare box2

   // Print total number of objects.
   cout << "Total objects: " << Box::objectCount << endl;

   return 0;
}

위 코드를 컴파일하고 실행하면 다음과 같은 결과가 나타납니다.

Constructor called.
Constructor called.
Total objects: 2

정적 함수 멤버 : 함수 멤버를 정적로 선언하여 클래스의 특정 객체와 독립적으로 만듭니다. 클래스의 개체가없고 클래스 이름과 범위 확인 연산자 :: 만 사용하여 정적 함수에 액세스하더라도 정적 멤버 함수를 호출 할 수 있습니다.

정적 멤버 함수는 정적 데이터 멤버, 기타 정적 멤버 함수 및 클래스 외부의 다른 함수에만 액세스 할 수 있습니다.

정적 멤버 함수에는 클래스 범위가 있으며 클래스의 this 포인터에 액세스 할 수 없습니다. 정적 멤버 함수를 사용하여 클래스의 일부 오브젝트가 작성되었는지 여부를 판별 할 수 있습니다.

정적 함수 멤버의 개념을 이해하기 위해 다음 예제를 시도해 보겠습니다.

#include <iostream>

using namespace std;

class Box
{
   public:
      static int objectCount;
      // Constructor definition
      Box(double l=2.0, double b=2.0, double h=2.0)
      {
         cout <<"Constructor called." << endl;
         length = l;
         breadth = b;
         height = h;
         // Increase every time object is created
         objectCount++;
      }
      double Volume()
      {
         return length * breadth * height;
      }
      static int getCount()
      {
         return objectCount;
      }
   private:
      double length;     // Length of a box
      double breadth;    // Breadth of a box
      double height;     // Height of a box
};

// Initialize static member of class Box
int Box::objectCount = 0;

int main(void)
{

   // Print total number of objects before creating object.
   cout << "Inital Stage Count: " << Box::getCount() << endl;

   Box Box1(3.3, 1.2, 1.5);    // Declare box1
   Box Box2(8.5, 6.0, 2.0);    // Declare box2

   // Print total number of objects after creating object.
   cout << "Final Stage Count: " << Box::getCount() << endl;

   return 0;
}

위 코드를 컴파일하고 실행하면 다음과 같은 결과가 나타납니다.

Inital Stage Count: 0
Constructor called.
Constructor called.
Final Stage Count: 2

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.