들어가며
C++에서 static 키워드는 간단한 사용법을 가지지만 복잡한 의미를 가지고 있는 아이러니한 키워드다. 함수든 변수든 간단하게 static이라는 키워드 여섯글자만 붙여 주면 무엇이든 static 속성을 가지게할 수 있지만 사용되는 각 위치에 따라 적용 되는 의미가 다양하기 때문에 각각의 static에 대한 의미를 잘 숙지하고 사용하여야 한다.
이번 포스트에서는 static 키워드의 기본 속성과 각 사용처에 따라 static 키워드가 가지는 의미, 주의해야 할 점, 프로그램 트릭들을 살펴 보도록 한다.
static 변수
이번 섹션에서는 static 변수에 대해 살펴 보자. C++에서 static 변수란 기본 변수 선언 앞에 static 키워드를 추가하여 선언된 변수다.
static int var; // int 타입으로 선언된 static 변수
static 변수의 가장 큰 특징은 프로세스의 데이터 메모리 영역에 생생되어 그 이후엔 프로그램이 종료 될 때까지 그 생명을 유지한다는 것이다. 또한, 선언 되는 위치에 따라 'static 전역 변수'와 'static 로컬 변수'로 분류 된다. 함수 바깥에 선언된 변수는 static 전역 변수, 함수 내부에 선언된 변수는 static 로컬 변수로써 프로그램 종료 까지 생명을 유지한다는 것은 같지만 각각 다른 특성들을 가지고 있다.
Tip : 프로세스는 크게 code, data, stack, heap 영역 이렇게 네 가지 메모리 영역으로 구성 된다. 로컬 변수들은 stack 영역에, 동적으로 할당 되는 메모리의 경우 heap 영역, 실행되는 코드들에 대해서는 code영역에 저장되며 전역 변수와 static 변수는 data 영역에 생성 되어 프로그램이 종료 될 때 까지 삭제 되지 않고 유지 된다.
static 전역 변수
함수 외부에 선언된 static 변수를 static 전역 변수라 한다. static 전역 변수의 생명 주는 프로그램이 시작 될 때에 생성 및 초기화 되고 프로그램이 종료 될 때 까지 유지된다. 이 속성은 extern을 사용하는 일반 전역 변수와 같지만(여러분이 만일 extern 키워드를 사용하지 않고 그냥 전역 변수를 선언했다고 하더라도 암시적으로 extern 선언이 된다) 전역 변수와는 달리 소스 파일 단위 접근 범위를 가진다.
'static' vs 'extern' 전역 변수
앞에서 설명한 소스 파일 단위의 접근 범위에 대해서 알아 보도록 하자. 변수에는 링크(linkage)라는 속성이 있다. 이는 같은 이름을 가진 경우 같은 식별자를 참조하는지 결정 한다. 링크가 없는 변수는 정의된 제한된 범위에서만 참조할 수 있다. 함수 내에서 생성되는 로컬 변수가 링크 속성이 없는 대표적인 예다. 함수 내에 생성된 로컬 변수의 경우 같은 이름을 같더라도 다른 함수에서 선언되면 각각 독립적인 변수로 취급 된다.
링크(linkage) :
C++ 프로그램을 컴파일하면 C++ 소스파일 별로 독립적인 오브젝트 파일이 생성되고 이 오브젝트 파일들을 하나로 연결(linking)하여 실행 파일을 만들어 낸다. 이 때 함수나 변수들의 이름을 기준으로 함수 호출, 변수 참조 등을 연결하는데 같은 소스 파일 내에서 연결 되는 되는 경우 '내부 링크'라고 하고 외부 소스 파일과도 연결하는 것을 '외부 링크'라고 한다. 일반적으로 함수나 전역 변수는 외부 링킹이 적용 되지만 명시적으로 static을 적용하는 경우 내부 링크만 적용 된다.
이 개념을 좀 더 확장하여 전역으로 선언된 변수의 경우 내부 링크만을 가지거나 외부 링크를 가진 변수로 나눌 수 있다. static으로 선언된 전역 변수의 경우 내부 링크만을 가지며, extern 전역 변수의 경우엔 외부 링크 까지 가진다.
내부 링크만을 가진 static 전역 변수는 소스 파일 단위의 전역 변수로써 파일 어디에서든 접근하여 값을 읽거나 수정할 수 있지만 다른 소스 파일에서는 다른 변수로 취급이 된다. 반면에 extern 전역 변수는 이름 그대로의 전역 변수로써 프로그램 어디어서든 접근 가능하다.
아래 예제는 이런 static 전역 변수와 exntern 전역 변수의 차이를 보여 주고 있다. 예제 소스의 내용이 다소 길긴 하지만 내용 자체는 간단하므로 이해하는데 큰 어려움은 없을 것이라 생각한다.
- Global - static, extern 변수 정의 및 초기화
// --------------------------------------
// Global.h
// --------------------------------------
static int s_var = 0; // static 전역 변수 선언 및 초기화
extern int g_var; // extern 전역 변수 선언
// --------------------------------------
// Global.cpp
// --------------------------------------
#include "Global.h"
int g_var = 0; // extern 전역 변수 초기화
먼저 static 전역 변수와 extern 전역 변수를 선언하고 있다.
※ static 전역 변수의 경우 헤더에서 선언과 초기화 까지 마치고 extern 전역 변수는 cpp에서 초기화를 별도 진행하는 것에 주목하자. static 전역 변수를 extern 처럼 cpp에서 초기화하는 것을 추가하면 같은 변수를 여러번 재정의하고 있다는 컴파일 에러가 발생한다.
- File1, File2 - 개별 cpp에서 static, extern 글로벌 변수 변경
// --------------------------------------
// File1.h
// --------------------------------------
namespace File1
{
void SetVariable(int i);
void IncreaseVariable();
}
// --------------------------------------
// File1.cpp
// --------------------------------------
#include "File1.h"
#include "Global.h"
#include <iostream>
namespace File1
{
void SetVariable(int i)
{
s_var = i;
g_var = i;
std::cout << "s_var=" << s_var << ", g_var=" << g_var << " in File1::SetVariable" << std::endl;
}
void IncreaseVariable()
{
s_var++;
g_var++;
std::cout << "s_var=" << s_var << ", g_var=" << g_var << " in File1::IncreaseVariable" << std::endl;
}
}
// --------------------------------------
// File2.h
// --------------------------------------
namespace File2
{
void SetVariable(int i);
void IncreaseVariable();
}
// --------------------------------------
// File2.cpp
// --------------------------------------
#include "File2.h"
#include "Global.h"
#include <iostream>
namespace File2
{
void SetVariable(int i)
{
s_var = i;
g_var = i;
std::cout << "s_var=" << s_var << ", g_var=" << g_var << " in File2::SetVariable" << std::endl;
}
void IncreaseVariable()
{
s_var++;
g_var++;
std::cout << "s_var=" << s_var << ", g_var=" << g_var << " in File2::IncreaseVariable" << std::endl;
}
}
File1 과 File2는 전역 변수들의 값을 변경하는 간단한 함수들을 구현하고 있다. 동일한 역할을 하는 함수를 두 벌씩 구현한 이유는 각 cpp 파일에서 전역 변수들의 값이 어떻게 영향을 받고 있는지 보여주기 위해서다.
- main - 변경 된 결과 값 확인
// --------------------------------------
// Program.cpp
// --------------------------------------
#include <iostream>
#include "Global.h"
#include "File1.h"
#include "File2.h"
int main()
{
std::cout << "s_var=" << s_var << ", g_var=" << g_var << " in main function line " << __LINE__ << std::endl;
File1::SetVariable(1);
File2::SetVariable(2);
File1::IncreaseVariable();
File2::IncreaseVariable();
std::cout << "s_var=" << s_var << ", g_var=" << g_var << " in main function line " << __LINE__ << std::endl;
}
main 함수 안에서는 File1, File2들을 이용한 변경이 적용 되기 전의 전역 변수 값과 적용 이후 값을 출력하고 있다. 출력 결과는 아래와 같다.
- Output
s_var=0, g_var=0 in main function line 8
s_var=1, g_var=1 in File1::SetVariable
s_var=2, g_var=2 in File2::SetVariable
s_var=2, g_var=3 in File1::IncreaseVariable
s_var=3, g_var=4 in File2::IncreaseVariable
s_var=0, g_var=4 in main function line 13
main 함수 8라인에서 아직 아무러전 변경이 적용되지 않은 static 전역 변수 s_var와 extern 전역 변수 g_var의 값이 0인것을 확인할 수 있다. 그 이후 File1::SetVariable() 에서 각 전역 변수들의 값을 1로 변경하고, 아래에서 File2::SetVariable()를 이용해 2로 변경한다.
지금 부터 중요한 부분이니 집중해서 보도록 하자. 14~15라인의 File1::IncreaseVariable()과 File2::IncreaseVariable()에서는 각 전역 변수들의 값을 1씩 증가 시키고 있다.
File1::IncreaseVariable() 호출 시 extern 전역 변수인 g_var는 최종값인 2에서 1증가하여 3이 되었지만, static 전역 변수인 s_var의 경우 그대로 2로 출력 되는 것을 확인할 수 있다. 이는 소스 파일 단위 접근 범위를 가지는 static 전역 변수의 특징 때문인데, File2::SetVariable에서 2로 설정한 File2.cpp의 s_var은 바로 앞의 File1::SetVariable을 이용해 1로 설정한 File1.cpp의 s_var과는 다른 변수로 취급 되기 때문이다.
File1 네임스페이스의 SetVariable()함수와 IncreaseVariable() 함수는 File1의 s_var을, File2 네임스페이스의 SetVariable()과 IncreaseVariable()는 File2의 s_var를 접근하여 수정한다. 각 increaseVariable() 함수들의 호출 결과는 1에서 증가한 2, 2에서 증가한 3이된다.
마지막으로 main 함수에서 각 전역 변수들의 변경이 끝나고 난뒤 값을 출력 해보면 extern 전역 변수의 경우 적용된 값인 4를 main 함수에서 출력하고 있지만 s_var의 경우 main.cpp는 또 다른 소스 파일이므로 별도의 변수로 취급되어 앞의 변경이 전혀 적용되지 않은 초기값 0을 그대로 출력하고 있다.
요약하면 아래와 같다 :
- 전역 static 변수는 프로그램이 시작되면서 생성되고 초기화 되며 프로그램이 종료 될 때까지 생명을 유지한다.
- 전역 extern 변수는 동일한 이름을 가진다면 프로그램 전체에서 동일한 변수를 가리키지만 전역 static 변수는 소스 파일 단위 접근 범위를 가지며 파일이 다르다면 같은 이름을 가진 변수라고 하더라도 다른 변수로 취급된다.
전역 변수 초기화를 이용한 트릭
이번 섹션에서는 전역 변수의 특성을 이용하여 main 함수 이전에 함수를 호출할 수 있는 트릭을 살펴 보도록 하겠다. 일반적으로 C++에서 함수의 호출은 main 함수로 부터 시작 되어야만 한다고 알려져 있지만 main 함수가 호출 되기 전 다른 함수들을 호출할 수 있는 방법이 존재한다. 바로 전역 변수 초기화를 이용하는 것이다.
프로그램이 시작 되면 가장 먼저 전역 변수 초기화 과정을 거친 후 main 함수가 호출되어 프그램이 실행 된다. 우리가 집중해야 할 분이 바로 이 전역 변수의 초기화 인데, 전역 변수는 단순 값으로도 초기화 되지만, 초기화를 위해 함수를 호출하는 것이 허용 된다. 바로 이를 이용하는 것이다.
특히 소스 파일 단위에서만 변수 접근 가능한 static 전역 변수의 특성이 여기서 빛을 발휘하게 된다. 전역 변수이기에 초기화를 위해 함수 호출을 할 수 있지만, 같은 이름을 가지더라도 다른 소스 파일에서는 다른 변수로 취급 되기에 이름 충돌이나 다른 소스 파일에서 해당 변수를 참조할 일도 없다.
bool Initialize()
{
// 각종 의미 있는 코드들..
std::cout << "call Initialize" << std::endl;
return true;
}
static bool init = Initialize(); // 전역 변수 초기화를 이용한 함수 호출
int main()
{
std::cout << "main function" << std::endl;
}
위 프로그램의 출력은 아래와 같다.
call Initialize
main function
main 함수 호출 이전에 Initialize 함수가 전역 변수 초기화를 위해 먼저 호출 되었음을 확인할 수 있다.
필자의 경우 위 트릭을 이용해 네트워크 패킷 핸들러와 같이 자주 추가 되며 어딘가에 등록 코드를 일일이 작업해 주어야 하는 코드들을 cpp 파일에서 전역 객체에 바로 등록할 수 있도록 하는 도구로 주로 사용한다. 보다 자세한 예제 코드는 [여기] github 레포지토리에서 확인 가능한다.
REMARK : stackoverflow의 'How do I prevent my 'unused' global variables being compiled out of my static library?'와 같은 글을 보면 전역 변수 초기화를 위한 함수 호출이 정상적으로 호출 되지 않는다는 질문이 자주 올라오는 것을 확인할 수 있다.
이유는 static 라이브러리의 import 방식 때문이다. 만일 초기화 되는 전역 변수가 exe(리눅스의 경우 실행 바이너리)를 생성하는 프로젝트나 dll 라이브러리에 있다면 프로그램 시작시 라이브러리의 모든 개체 파일들을 import하기 때문에 당연히 전역 변수들도 모두 초기화 된다. 하지만 static 라이브러리에 있는 전역 변수의 경우 실행 프로젝트로 부터 명시적인 참조가 없는 개체 파일의 경우 import 되지 않기 때문에 해당 개체 파일에 있는 전역 변수의 경우 초기화 조차 되지 않는다.
이를 해결 하기 위해서는 참조와 상관 없이 모든 개체 파일들을 import 하도록 링크 옵션을 지정해 주면 된다.
- Visual Studio : /WHOLEARCHIEV 링크 옵션 설정 (참고 : Microsoft Learn - /WHOLEARCHIVE)
- 프로젝트 속성 페이지 대화 상자를 엽니다. 자세한 정보는 Visual Studio에서 C++ 컴파일러 및 빌드 속성 설정을 참조하세요.
- 구성 속성>링커>명령줄 속성 페이지를 선택합니다.
- /WHOLEARCHIVE추가 옵션 텍스트 상자에 옵션을 추가합니다.
- Linux : --whole-archive
static 로컬 변수
앞에서 살펴 본 전역 변수와 반대로 함수 내부에 선언된 static 변수를 로컬 static 변수라고 한다. 로컬 static 변수의 생성과 초기화 시점은 코드에서 변수 선언이 처음 호출 되는 시점이며, 일단 생성된 로컬 static 변수는 프로그램이 종료 될 때까지 그 생명을 유지한다.
로컬 static 변수의 특이점은 함수가 처음 호출 될 때 단 한번만 초기화 되며 나머지 호출에서는 초기화 과정은 무시 된다는 것이다. 아래 예제 코드에서 demo 함수가 호출 되며 count 변수가 초기화 되며 값이 변경 되는 과정을 살펴 보자.
// 함수 내부에서 static 변수 사용 예제
#include <iostream>
#include <string>
void demo()
{
static int count = 0; // static 변수
std::cout << count << " ";
count++; // 함수가 호출 될 때 마다 값이 업데이트 된다
}
int main()
{
for (int i=0; i<5; i++)
{
demo();
}
return 0;
}
Output
0 1 2 3 4
위 예제 프로그램은 demo라는 함수 안에 count라는 변수를 static으로 선언하고 있다. count 변수는 함수가 최초 호출 될 때 8라인의 초기화 코드를 실행 후, 나머지 호출에서는 더 이상 초기화를 진행하지 않는다. 그리고 함수가 종료 되더라도 그 값을 잃어 버리지 않고 다음 매 호출 시 마다 기존의 값에 1씩 더해주고 있다.
- 초기화는 함수가 호출 될 때 처음 한번만 진행한다.
- 함수가 종료 되더라도 변수가 파괴 되지 않고 프로그램이 종료 될 때 까지 값을 유지한다.
- 로컬 static 변수는 선언된 함수 내에서만 접근 할 수 있다.
static 로컬 변수를 이용한 싱글톤
스캇 마이어스의 Effective C++에서는 함수 호출 시 단 한번만 호출 되는 static 로컬 변수의 특성을 이용해 싱글톤을 구현하는 방법을 소개하고 있다.
class Singleton
{
// ... 싱글톤이 가진 데이터들 ...
};
Singleton* GetInstance()
{
static Singleton instance;
return &instance;
}
위 예제의 GetInstance() 함수는 몇번이 호출 되든 단 한번만 생성 되고 초기화 되는 동일한 instance 객체를 계속 리턴하게 된다.
이런 방식은 만일 여러분이 전역 객체의 생성 순서가 중요한 작업을하는 경우 전역 객체를 직접 호출하는 대신 static 로컬 변수를 리턴하는 함수를 호출함으로써 전역 객체들 간에 생성 순서를 컨트롤할 수 있다.
REMARK : static 로컬 변수를 이용한 싱글톤 방식은 thread safe 하다.
The relevant section 6.7:
such a variable is initialized the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization. [...] If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.
Then there's a footnote:
The implementation must not introduce any deadlock around execution of the initializer.
static 함수
static 키워드는 변수뿐 아니라 함수에도 적용 가능하다.
static <return_type> <function_name>(<arguments>)
{
//code
}
static 함수는 위와 같이 일반 함수 선언 앞에 static 키워드를 추가해주기만 하면 되며, static 전역 변수와 비슷한 링크 속성을 갖는다. 즉, 동일 소스 파일 내에서는 어디에서든 접근과 호출이 가능하지만 다른 소스 파일에서는 접근이 불가하며, 같은 이름을 가지고 있는 함수라 하더라도 다른 소스 파일에 있는 경우 다른 별도의 함수로 취급 된다.
// File1.h ---------------------------------------------------
void ExternalFunction();
static void StaticFunction();
// File1.cpp -------------------------------------------------
#include "File1.h"
#include <iostream>
void ExternalFunction()
{
StaticFunction();
}
void StaticFunction()
{
std::cout << "Static Function in File1 File" << std::endl;
}
// Program.cpp
#include <iostream>
#include "File1.h"
void StaticFunction()
{
std::cout << "Static Function in Program File" << std::endl;
}
int main()
{
ExternalFunction();
StaticFunction();
return 0;
}
위 예제 프로그램에서는 File1.h에 static 함수를 선언하고 File1.cpp, Program.cpp에서 각각 StaticFunction을 정의하고 있다. 30라인과, 31라인을 살펴 보면 ExternalFunction() 함수를 통해 File1.cpp에 정의된 StaticFunction을 호출, Program.cpp에 정의된 StaticFunction은 직접 호출하고 있다. 위 프로그램의 실행 결과는 다음과 같다.
Static Function in File1 File
Static Function in Program File
각 소스 파일에 구현된 함수 정의가 호출 된 것을 확인할 수 있다. 만일 Program.cpp에서 StaticFunction을 별도로 정의하지 않은 상태에서 File1의 StaticFunction을 직접 호출하게 된다면 해당 함수의 정의를 찾을 수 없다는 에러가 발생한다.
이 처럼 static 함수 또한 static 변수 처럼 파일 단위 접근 스코프를 가지며, 동일한 이름을 갖고 있지만 다른 파일에 존재하는 함수의 경우 별도의 함수로 취급한다.
REMARK : static 함수는 소스 파일 내에서만 정의해 사용할 것을 권장한다. 위 예제와 같이 헤더에 static 함수를 개별 정의해 사용하는 방법은 잘 사용되지 않아 익숙치도 않을 뿐더러 함수를 사용하는 측에서 별도의 함수를 꼭 정의해야만 하는 불편함, 같은 이름을 가진 함수임에도 호출 되는 소스 파일의 위치에 따라 다른 동작을 하기 때문에 코드 분석에 많은 어려움을 가져다 준다. 이런게 있다는 정도만 알아 두고 쓰.지.마.라.
클래스에서의 static 키워드
일반 변수뿐만 아니라 클래스에도 static 키워드를 적용할 수 있다. 이번 섹션에서는 클래스에서 static 키워드가 가지는 역할과 의미에 대해 살펴 보도록 하겠다.
static 멤버 변수
클래스 내에 static으로 선언된 멤버 변수는 특정 객체에 종속 되지 않고 해당 클래스의 모든 객체들에게 공유 된다. 그래서 static 멤버 변수는 생성자에서 초기화 될 수 없으며, 클래스 외부에서의 별도 초기화 과정이 필요하다.
아래 프로그램은 static 멤버 변수를 가진 GfG 클래스의 객체를 여러 개 만들고, 각 객체를 통해 static 멤버 변수 i에 접근하고 있다.
#include<iostream>
class GfG
{
public:
static int i;
GfG()
{
// Do nothing
};
};
int main()
{
GfG obj1;
GfG obj2;
obj1.i = 2;
obj2.i = 3;
// prints value of i
std::cout << obj1.i <<" "<< obj2.i << std::endl;
}
Output
LNK2001 확인할 수 없는 외부 기호 "public: static int GfG::i"
LNK1120 1개의 확인할 수 없는 외부 참조입니다.
우선 프로그램을 컴파일하게 되면 위와 같은 오류를 발생 시킨다. 앞에서 이미 언급 했지만, static 멤버 변수는 아래 처럼 명시적으로 클래스 외부에서 클래스 스코프를 이용해 아래 코드 처럼 초기화 되어야 한다.
#include<iostream>
class GfG
{
public:
static int i;
GfG()
{
// Do nothing
};
};
int GfG::i = 0;
int main()
{
GfG obj1;
GfG obj2;
obj1.i = 2;
obj2.i = 3;
// prints value of i
std::cout << obj1.i << " " << obj2.i << std::endl;
}
위 프로그램을 실행하면 20 라인에서 2를 대입했음에 불구하고 변수를 공유하고 있기 때문에 아래 처럼 출력 된다.
3 3
REMARK :static 멤버 변수는 위 코드에서 처럼 클래스 객체에 '.' 연산자를 이용해 접근할 수도 있고 클래스 이름을 이용해 다음과 같이 접근할 수도 있다. 위 예제는 이렇게도 사용가능 하다는 것을 보여주기 위한 예제일 뿐이고, 관례적으로 클래스 이름을 통한 접근 방식을 사용하길 권한다.
GfG::i = 2; // 클래스 이름을 이용해 static 멤버 변수에 접근. 권장 방식
obj2.i = 3; // 클래스 객체를 통해 static 멤버 변수에 접근.
static 클래스 객체
static 변수와 마찬가지로 static 으로 선언된 클래스 객체는 전역인지 로컬인지에 따라 생성되는 시점이 다르며, 일단 생성 된 후엔 프로그램이 종료 될때 까지 그 생명이 유지 된다.
#include<iostream>
class NonStaticClass
{
public:
NonStaticClass()
{
std::cout << "Construct NonStaticClass" << std::endl;
}
~NonStaticClass()
{
std::cout << "Destory NonStaticClass" << std::endl;
}
};
class StaticClass
{
public:
StaticClass()
{
std::cout << "Construct StaticClass" << std::endl;
}
~StaticClass()
{
std::cout << "Destory StaticClass" << std::endl;
}
};
int main()
{
std::cout << "Start of main function" << std::endl;
{
NonStaticClass nonStaticClass;
static StaticClass staticClass;
}
std::cout << "End of main function" << std::endl;
}
위 프로그램을 실행하면 아래와 같은 결과가 출력 된다.
Start of main function
Construct NonStaticClass
Construct StaticClass
Destory NonStaticClass
End of main function
Destory StaticClass
main 함수가 실행 되고 난 뒤 NonStaticClass 객체가 생성되어 "Construct NonStaticClass"가 출력 된 후 static으로 선언된 StaticClass 객체가 생성된 것을 로그를 통해 확인할 수 있다. 주목할 점은 NonStaticClass는 블록이 끝나는 지점에서 소멸 하지만 StaticClass의 객체는 main 함수가 종료 된 후 프로그램이 종료되는 시점에야 소멸된다.
static 클래스 멤버 함수
static 멤버 변수와 마찬가지로 static 멤버 함수 역시 클래스 객체에 종속 되지 않는다. 클래스 멤버 함수 역시 객체에 '.' 연산자를 통해 접근 가능하지만 권장 되는 방법은 아니다. static 멤버 함수에 접근하기 위해서는 클래스 네임과 스코프 연산자(::)를 이용해 호출하는 것을 권장한다.
static 멤버 함수는 클래스 객체에 종속된 것이 아니므로 함수 내부에서 클래스 객체에 종속 되는 일반 멤버 변수 또는 멤버 함수는 접근 불가능하다. 하지만 static 멤버 변수나 static 함수는 접근 가능하다.
REMARK : static 멤버 함수 virtual 함수가 될 수 없으며 const 또는 volatile 한정자를 적용할 수 없다.
class Test {
public:
virtual static void fun1() {}
// error: member ‘fun1’ cannot be declared both virtual and static
// virtual static void fun1()
static void fun2() const {}
// error: static member function ‘static void Test::fun2()’ cannot have cv-qualifier
// static void fun2() const {}
};
마치며
이상 C++에서 static 키워드가 가지는 의미와 사용처에 따른 특성들을 살펴 보았다. static 키워드 외에도 const, mutable, extern 키워드들에 대한 정보를 아래의 링크들을 통해 살펴 보도록 하자.