언어/C&C++ 기본

[C++] Static

차가운오미자 2021. 7. 1. 11:36

Static의 의미

1. class 밖에 선언된 static: means linkage to the symbol would be static. translation unit에서만 visible할 것

2. class 안: 그 변수가 모든 그 class의 instance 들에 의해 공유될 것

 

1. class밖에 선언된 static

static int s_Var (s_는 conventionally 사용되는 static var 표시)

컴파일러는 이 translation unit에서만 static 요소의 정의를 찾으려 할 것이다.

 

예를 들어서 두 개의 소스파일에 각각 s_Var이라는 변수를 static하게 선언하면 문제없이 컴파일된다.

하지만 한 소스파일 (혹은 두 소스파일 모두) s_Var 변수에 static선언을 빼게 되면 컴파일 에러가 발생한다.

왜냐하면, 같은 변수가 두 번 선언되었기 때문이다.

전자의 경우에는, 각 translation unit 고유의 s_Var이었기 떄문에 충돌이 없었지만, static을 빼버리며 그 변수는 모든 translation unit에 선언된 변수라 중복이 발생하는 것이다.

// Main.cpp
int s_Var = 10; 

// Static.cpp
int s_Var = 5; 

 

외부에서 해당 변수의 정의를 찾는 다는 의미로 extern 선언을 해줄 수 있다.

// Main.cpp
extern int s_Var;
	// Static.cpp에서 해당 변수를 찾을 것. 


// Static.cpp
int s_Var = 5; 

이외에도, static은 함수에도 선언할 수 있다. 

 

global할 필요가 없는 것은 static으로 선언하는 것이 안전하다. 마치 private을 굳이 사용하는 것처럼.

static 선언을 하지 않으면 모든 translation unit을 검색해서 찾아내려 하니까..

 

2. class/struct 안에 static 

보통 객체지향 언어들 모두 class안에 static을 선언하는 문법을 갖고 있다. 

class안에 static 변수를 선언한다는 건 그 class로 만든 모든 인스턴스들이 그 변수 하나를 공유한다는 것이다.

static함수는 인스턴스 없이 사용할 수 있게 되며, 대신 그 class로 만들어진 인스턴스들의 변수들을 이용할 수 없다. 

 

struct Entity{
    static int x, y;
    
    void print(){
    	cout << x << ", " << y << endl;
    }
}

// 아래의 선언들이 필요함 - 아니면 unresolved external symbol 오류가 난다. 
// 왜냐면 위 static 변수들을 어딘가에 정의할 필요가 있기 때문에, 그래야 링커가 연결 가능
int Entity::x;
int Entity::y;

int main(){
	
    Entity e;
    e.x = 2; 
    e.y = 3;
    
    Entity e1 = { 5, 8 }; // 에러 뜸, x, y는 인스턴스의 변수가 아니기 떄문
    
    Entity e1;
    e1.x = 4;
    e1.y = 8;
    
    e.print();
    e1.print(); 
    // 둘다 결과는 4, 8 >> 왜냐면 x, y는 하나니까
    
    // 결국 접근은 인스턴스를 통해서 할 필요가 없다. 
    Entity::x = 5;
    Entity::y = 6; 
    
    
}

static 함수를 살펴보자면, 

struct Entity{
    static int x, y;
    
    static void print(){
    	cout << x << ", " << y << endl;
    }
}

int Entity::x;
int Entity::y;

int main(){
	
    Entity e;
    
    Entity::x = 4;
    Entity::y = 8;
    
    Entity::print();
    // 바로 print() 함수를 부를 수 있다. 
    // 대신 이게 가능한 건, x, y가 모두 역시 static하기 때문에
   
    
}

주의할 점은 static method가 static변수에만 접근할 수 있다는 것이다. 

 

만약 static 메소드에서 인스턴스를 사용하고 싶으면 해당 인스턴스를 변수로 넘길 수 있도록 선언해줘야 한다.

struct Entity{
    int x, y;
    
    static void print(){
    	cout << x << ", " << y << endl;
    }
}

static void print(Entity e){
	std::cout << e.x << ", ", e.y << std::endl;
}

int main(){
	
    Entity e;
    e.x = 2; 
    e.y = 3;
    
    Entity::print(e); // parameter로 인스턴스를 넘김
   
    
}

 

3. Local Static

life time of entire program but its scope is limited to that function or if statement or whatever

- life time: how long this variable will last in memory

- scope: where can we actually access the variable

#include <iostream>

using namespace std; 

int j; 
 
void Func(){
	
	int i = 0; 
	i++;
	cout << i << endl;
}

void FuncJ(){
	
	j++;
	cout << j << endl;
}

void FuncK(){
	
	static int k = 0;
	k++;
	cout << k << endl;
}


int main(){
	
	Func();	
	Func();	
	Func();	
	Func();	
	Func();
	// 결과는 1 1 1 1 1, 왜냐면 함수를 부를 때마다 i가 새로 선언되었기 때문
	
	FuncJ();
	FuncJ();
	FuncJ();	
	FuncJ();
	FuncJ();
	// 결과는 1 2 3 4 5
	// 대신 이렇게 하면 j에 어디서나 접근할 수 있어서 안전하지 않아짐
	 
	FuncK();
	FuncK();
	FuncK();
	FuncK();
	FuncK();
	// 결과는 1 2 3 4 5 이면서 함수 외부에서  접근할 수 없다.  

	
}

class에서 적용시켜보자. 

 

만약 내가 어떤 클래스를 선언할 때 이 클래스의 인스턴스가 딱 하나였으면 좋겠다면, 아래와 같이 선언하면 된다

class Singleton{
	
private: 
	static Singleton* s_Instance;
public:
	static Singleton& Get() { return s_Instance; }
	
	void print(){};
	}
};

Singleton* Singleton::s_Instance = nullptr;



int main(void){
	Singleton::Get().print();	
}

혹은 아래와 같이, 함수 내에서 인스턴스를 선언하고 인스턴스를 반환한다.

class Singleton{
	
public:
	static Singleton& Get() { 
		
		static Singleton s_Instance;
        // 만약 여기서 static을 빼버리면 공간이 스택에 생겨서, 이 함수가 끝남과 동시에
        // 저장공간이 없어질 것이다. 참조값으로 반환하고 있기에 문제가 생긴다. 
		return s_Instance; }
	
	void print(){};
	}
};

Singleton* Singleton::s_Instance = nullptr;



int main(void){
	Singleton::Get().print();	
}

Singleton 객체가 static으로 선언되었기 떄문에, Get()을 처음으로 호출하는 때에 메모리가 할당되고, 그 이후부턴 이미 있는 메모리 공간의 참조값을 반환하는 형식으로 실행된다. 

'언어 > C&C++ 기본' 카테고리의 다른 글

[C] Storage Class (extern, static 등)  (0) 2021.08.09
[C++] ENUMS, Constructor, Destructor  (0) 2021.07.02
[C++] Class  (0) 2021.06.25
[C++] Header Files  (0) 2021.06.14
[C++] Variables, Functions, Pointers, References  (0) 2021.06.14