C++

static Keyword

gbleem 2025. 3. 27. 01:09

1. class 외부에서의 static의 의미


개념)

  • 정의된 단위에서만 볼 수 있게 한다.
  • static으로 선언한 변수가 존재하는 그 파일 내부에서만 그 변수를 찾을 수 있다는 의미

 

예시 1)

statickeyword.cpp 파일

static int s_Variable = 5;

 

staticmain.cpp 파일

#include <iostream>

int s_Variable = 10;

int main()
{
	std::cout << s_Variable << "\n"; //10 출력
	std::cin.get();
}

 

이 경우 10을 출력하게 된다.

 

예시 2)

만약 위의 statickeyword.cpp에서 s_Variable 변수의 static 을 빼면 아래와 같이 이미 정의되어있다는 오류를 출력한다.

 

예시 3) 

그렇다면 이 에러를 없애주고, static 변수를 쓰기 위해서는 staticmain.cpp의 변수를 extern으로 선언하는 것이다.

  • extern 키워드를 통해서 외부와 링킹을 하게 된다.

statickeyword.cpp

int s_Variable = 5;

 

staticmain.cpp

#include <iostream>

extern int s_Variable;

int main()
{
	std::cout << s_Variable << "\n";
	std::cin.get();
}

 

이 경우 5를 출력해준다.

 

예시4) 

만약 바로 위 코드에서 statickeyword.cpp의 s_Variable을 static으로 선언하면 어떻게 될까?

static int s_Variable = 5;

 

정답은 staticmain에서 아래와 같은 오류가 발생하게 된다. 즉 외부에서 s_Variable을 찾지 못한다는 의미이다. 

class외부에 선언된 변수에 static을 붙이게 되면 private 지정자를 붙인 것과 같은 동작을 한다.

 

다른 예시를 들어보면, 아래와 같은 이름의 함수를 두고, static을 붙인 경우와 붙이지 않은 경우를 확인해 보면

  • 아래처럼 붙이지 않은 경우 당연히 여러번 정의되었다는 링크 에러가 뜰 것이다.

//statickeyword.cpp
void func()
{

}

//staticmain.cpp
void func()
{

}

int main()
{
	std::cout << s_Variable << "\n";
	std::cin.get();
}
  • 그러나 아래처럼 static으로 함수를 선언한 경우 문제없이 컴파일 된다.
    • 그 이유는 역시 class 외부에서 static으로 선언되면 다른 파일에서는 그 함수를 찾을 수 없기 때문이다.
//statickeyword.cpp
static void func()
{

}

//staticmain.cpp
void func()
{

}

int main()
{
	std::cout << s_Variable << "\n";
	std::cin.get();
}

 

이렇게 동작하는 이유는 아마도 전역 변수를 만들 때 static을 붙이지 않게 된다면, 정말 모든 파일에서 사용할 수 있는 전역변수가 되어버리기 때문에 문제가 될 수 있기 때문인 것 같다.

그래서 해당 파일 내부에서만 쓸 수 있는 전역 변수를 만들기 위해 static을 붙인다고 이해했다.

 

 

2. class 내부에서의 static의 의미


개념)

모든 클래스와 인스턴스 메모리를 공유

 

예시 1)

일반적인 경우의 멤버 변수의 동작을 보면 아래와 같다.

  • 당연히 예상되는 대로 2, 3 / 5, 8 이 출력될 것이다.
  • 여기서 struct로 한 이유는 기본으로 public 지정자 이기 때문이다. (클래스로 해도 무방함)
#include <iostream>

struct Entity
{
	int x, y;

	void Print()
	{
		std::cout << x << " " << y << "\n";
	}
};

int main()
{
	Entity e;
	e.x = 2;
	e.y = 3;

	Entity e1 = { 5,8 };

	e.Print(); //2 3 
	e1.Print(); //5 8

	std::cin.get();
}

 

예시 2)

만약 멤버 변수인 x, y를 static으로 바꾸면 이니셜라이저를 사용한 부분에 있어서 오류가 뜨게 된다.

  • 위의 오류를 수정해서 아래와 같이 구성하면, 또 한번 오류가 발생한다.
  • 이 오류의 원인은 우리가 static 변수를 정의하지 않았기 때문이다. 
#include <iostream>

struct Entity
{
	static int x, y;

	void Print()
	{
		std::cout << x << " " << y << "\n";
	}
};

int main()
{
	Entity e;
	e.x = 2;
	e.y = 3;

	Entity e1;
	e1.x = 5;
	e1.y = 8;

	e.Print();
	e1.Print();

	std::cin.get();
}

 

 

예시 3)

위에서 발생한 오류들을 모두 수정하면(정의해주기) 아래와 같이 구성할 수 있다.

#include <iostream>

struct Entity
{
	static int x, y;

	void Print()
	{
		std::cout << x << " " << y << "\n";
	}
};

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

int main()
{
	Entity e;
	e.x = 2;
	e.y = 3;

	Entity e1;
	e1.x = 5;
	e1.y = 8;

	e.Print();
	e1.Print();

	std::cin.get();
}

 

그럼 위 코드의 실행 결과는 무엇일까

 

정답은 두 경우 모두 5, 8이 출력된다. 두개의 Entity 인스턴스가 존재하지만, static 변수는 모든 인스턴스와 공유하기 때문에 최종적으로 대입한 값이 뜨게 되는 것이다.

 

사실 위에 처럼 값을 대입하기 보다는 아래의 코드처럼 값을 대입하는 것이 더 좋은 방식이다.

Entity::x = 5;
Entity::y = 8;

 

예시4) 

멤버 함수 또한 static으로 만들고 아래와 같이 사용할 수 있게 된다.

또한 사실상 Entity라는 인스턴스를 만들 필요도 없어지기 때문에 아래와 같은 코드로 바꿀 수 있다.

#include <iostream>

struct Entity
{
	static int x, y;

	static void Print()
	{
		std::cout << x << " " << y << "\n";
	}
};

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

int main()
{
	Entity::x = 2;
	Entity::y = 3;

	Entity::x = 5;
	Entity::y = 8;

	Entity::Print();
	Entity::Print();

	std::cin.get();
}

 

예시5)

static 멤버 함수를 쓰는 이유는 아래와 같이 볼 수 있다.

아래 처럼 코드를 구성하면(x, y변수를 non static으로 수정), 아래 사진에서 보이는 에러가 발생한다.

#include <iostream>

struct Entity
{
	int x, y;

	static void Print()
	{
		std::cout << x << " " << y << "\n";
	}
};

int main()
{
	Entity e;
	e.x = 2;
	e.y = 3;

	Entity e1;
	e1.x = 5;
	e1.y = 8;

	Entity::Print();
	Entity::Print();

	std::cin.get();
}

 

(설명) 정확하지 않을 수 있다.

  • 클래스 내부의 non static 메서드는 현재 클래스의 인스턴스를 매개변수로 가져오게 된다. (보이지는 않지만...)
  • 그러나 static 메서드는 클래스의 인스턴스를 가져올 수 없고, 위의 사진과 같은 오류가 발생하는 것이다.
  • 코드로 추가 설명하면
    • Print1 함수에서 오류가 발생하는 원인이
      • static 멤버 함수의 경우 해당 클래스의 인스턴스를 가져올 수 없어서 인데,
      • 예를 들면, Print2 함수 처럼 선언하면 당연히 오류가 발생하는 것과 같은 의미이다.
      • Print2 함수는 x랑 y가 뭔지 모르니까
    • 그러나 Print3 함수는 오류가 사라지게 되는데,
      • Entity 라는 인스턴스를 매개변수로 받았기 때문이다.
      • 이 모습이 non-static 한 메서드의 컴파일 시 동작 모습이다. 
struct Entity
{
	int x, y;
	
    //에러 발생
	static void Print1()
	{
		std::cout << x << " " << y << "\n";
	}
};
//위의 Print 함수랑 같은 의미 (에러 발생)
static void Print2()
{
	std::cout << x << " " << y << "\n";
}
//에러 사라짐
static void Print3(Entity e)
{
	std::cout << e.x << " " << e.y << "\n";
}

 

 

참고)

https://www.youtube.com/watch?v=f3FVU-iwNuA&t=130s

https://www.youtube.com/watch?v=V-BFlMrBtqQ