서론
이 장은 C++ 클래스내의 Static 맴버 변수에 특성에 대해 설명하고 선언과 정의에 대해 구분한다.
목차
- Static 멤버 변수
- 선언과 정의의 구분
- 정의 및 초기화는 헤더 파일 내에서는 불가능하다
- Static const 멤버 변수
- Private인 static 멤버 변수 초기화
- Static 멤버 함수
- Static 멤버 함수를 사용하는 이유
- Static 멤버 함수는 this포인터를 사용할 수 없다.
- 멤버 함수 포인터
Static 멤버 변수
모든 객체가 공유하는 멤버 변수이기 때문에 메모리는 프로그램 시작때부터 차지하고 있으며 메모리에 내내 값이 유지된다.
- 객체 생성 전에도 메모리 존재
- 객체와는 독립적으로 존재

객체 이름으로도 접근이 가능하지만 클래스 이름으로도 접근이 가능하다.
int Something::s_value = 1;
선언과 정의의 구분
선언(Declaration)
컴파일러에게 변수의 정보만을 주며 실제 메모리를 사용하지 않는다.
아래 코드는 static멤버 변수 m_value의 존재를 컴파일러에게 알려주는 것일 뿐이다.
class Something
{
public:
static int m_value; // 선언. 메모리를 차지 하지 않는다.
}
정의
실제 메모리가 할당되며 초기화 하는 것도 정의해주는 과정이다.
아래 코드는 static멤버 변수 m_value의 메모리를 할당해주고 값을 1을 넣는 과정이다.
int Something::m_value = 1; // 정의. 전역 범위의 메모리를 할당 받는다.
static 멤버 변수는 모든 객체가 공유해야 하므로 프로그램 전체 영역에서 메모리가 유지되야 한다.
반드시 전역 범위에서 정의 및 초기화를 해줘야 한다.
따라서 static 멤버 변수는 main함수는 물론이고 생성자 안에서도 초기화 할 수 없다.
#include <iostream>
using namespace std;
class Something
{
public:
static int m_value; // 선언. .
};
int Something::m_value = 1; // 👈👈 전역 범위에서 초기화(정의)
int main()
{
// int Something::m_value = 1; 👈👈 에러!!! 전역 범위에서만 가능.
cout << Something::m_value << endl; // 1 출력 👈👈 객체 생성 전에도 사용 가능
cout << &Something::m_value << endl; // 0x601070 출력 👈👈 객체 생성 전에도 사용 가능
Something st1;
Something st2;
st1.m_value = 2;
cout << st1.m_value << endl; // 2 출력
cout << &st1.m_value << endl; // 0x601070 출력
cout << st2.m_value << endl; // 2 출력
cout << &st2.m_value << endl; // 0x601070 출력
st2.m_value = 1024;
cout << Something::m_value << endl; // 1024 출력
return 0;
}
정의 및 초기화는 헤더 파일 내에서는 불가능하다
헤더 파일과 .cpp파일로 분리할 때, static멤버 변수는 반드시 .cpp파일에서 초기화 한다.
- static 멤버 변수의 초기화는 헤더파일에선 불가능하다.
- 여러 곳에서 헤더 파일을 include할 때 마다 static 멤버 변수를 여러번 정의 및 초기화 하는게 됨으로.
📜 Something.h
class Something
{
public:
static int m_value; // 클래스 내에선 초기화 불가능
};
int Something::m_value = 1; // 💥에러!💥 헤더 파일 내에선 초기화 불가능
📜 Something.cpp
int Something::m_value = 1;
Static const 멤버 변수
static const멤버 변수는 클래스 내에서 초기화 하는 것이 가능하다.
static const int m_value;
- 모든 객체가 공유하고
const특성상 초기화 이후에값을 변경하는 것이불가능 - 클래스 내부에서 초기화가 가능하다.
- 헤더파일 내에서도 초기화가 가능하다.
#include <iostream>
using namespace std;
class Something
{
public:
static const int m_value; // 가능! 🙆♀️
};
// int Something::m_value = 1; 👈 const라 기존 값 변경이 불가능
int main()
{
Something st;
st.m_value = 1024; // 👈 💥에러💥 const라서 값 변경 불가
return 0;
}
Private인 static 멤버 변수 초기화
static 멤버 변수는 클래스 내부에선 초기화가 불가능하므로 private하더라도 클래스 외부에서 정의가 가능하다.
#include <iostream>
using namespace std;
class A
{
private: // ⭐⭐⭐
static int s_value;
};
int A::s_value = 1; // 👈 private이더라도 전역범위에서 정의 및 초기화 가능
int main()
{
A a;
cout << a.s_value << endl;
A::s_value = 1; // 👈 💥에러!💥 private이므로 s_value 에접근이 불가능하다.
return 0;
}
Static 멤버 함수
객체와독립적이고객체생성과상관없다.- 따라서
멤버 변수는객체가생성되야메모리를할당받기 때문에static 멤버함수내에서는멤버 변수를사용할 수 없다. - 미리 전역에서 메모리가 할당되는
static멤버 변수는사용이 가능하다.
class Something
{
private:
int noramal_value = 99; // 일반 멤버 변수
static int static_value; // static 멤버 변수
public:
static void Func() // ⭐ static 멤버 함수
{
int a = 1024; // Func()내의 일반 지역 변수
cout << a << endl;
// cout << noramal_value << endl; 👈👈 💥에러!!!💥 일반 멤버 변수는 사용할 수 없다.
cout << static_value << endl; // static 멤버 변수는 사용 가능
}
};
int Something::static_value = 777;
int main()
{
Something::Func(); // 객체 생성 없이 바로 클래스 이름으로 호출 가능
/* 👆 1024와 777을 출력한다. */
return 0;
}
Static 멤버 함수를 사용하는 이유
객체 생성 여부와상관없이클래스 이름으로접근하고 싶을 때 사용private한 static멤버 변수에접근하려 할 때 많이 사용static멤버 변수는모든 객체들이사용하고공유해야 하는데private면외부에서 직접 접근하고사용할 수 없다.priavte 멤버 변수들은멤버 함수들에서만접근이 가능하다는 특징이 있다.static 멤버 함수를 통해서private한 static멤버 변수에간접 접근할 수 있다.(getter, setter 접근함수 처럼)
class Something
{
private:
static int static_value; // private한 static 멤버 변수
public:
static int getValue() // ⭐ static 멤버 함수
{
return static_value;
}
};
int Something::static_value = 777;
int main()
{
// cout << Something::static_value << endl; 👈👈 💥에러!💥 private이므로 직접 접근 불가능
cout << Something::getValue() << endl; // 777 출력. 클래스 이름으로 호출했을 때.
Something s;
cout << s.getValue() << endl; // 777 출력. 객체 이름으로 호출했을 때.
return 0;
}
이처럼 private한 staitc 멤버 변수를 직접 접근할 수는 없지만 static 멤버 함수 getValue() 를 통하여 간접적으로 staitc 멤버 변수를 리턴받을 수 있다.
Static 멤버 함수는 this포인터를 사용할 수 없다.
this포인터는 객체인 자기 자신의 주소를 담고 있기 때문에객체가생성되야사용가능하다.static 멤버 함수는객체들의생성과무관하며언제든 클래스 이름으로 접근가능해야 하기 때문에static멤버 함수내부에서는this포인터를사용할 수 없다.
class Something
{
private:
static int static_value; // static 멤버 변수
public:
static int getValue() // ⭐ static 멤버 함수
{
return this -> static_value; // 💥에러💥
}
int temp() // ⭐ 일반 멤버 함수
{
return this -> static_value; // 문제 없다.
}
};
- static 멤버 함수인 getValue()에선
this를 사용할 수 없다. - 일반 멤버 함수인 temp()에선
this를 사용할 수 있다.
멤버 함수 포인터
일반 함수는 함수 이름에 함수의 주소값이 들어가 있다. 그러나 멤버 함수의 포인터는 조금 다르다.
멤버 함수의 포인터는 &클래스이름 :: 함수이름 으로 접근해야 한다.
class Something
{
public:
int temp() { return 1; }
};
int main()
{
Something s1;
Something s2;
int (Something::*fptr_1)() = s1.temp; // 💥에러💥
int (Something::*fptr_2)() = &Something::temp; // 문제 없다.
}
- int (Something::*fptr_2)() =
&Something::temp;- 멤버 함수의 포인터는 이렇게
& 클래스이름 :: 함수이름으로 으로 접근해야 한다.
- 멤버 함수의 포인터는 이렇게
이유
멤버 변수는 각객체들마다따로 메모리를 가져주소가 다르다멤버 함수는객체마다함수 메모리를따로 갖는 방식이아니다- 멤버 함수는 어딘가
한 군데 저장돼 있고각 객체마다그 공간에동일하게 접근하여 각자의 다른 데이터로 사용하는 방식이다.
- 멤버 함수는 어딘가
- 따라서 일반 함수의 주소와는 다르게
멤버 함수의 주소를 받아와야 한다.- 그럴려면 속해있는 클래스가 어디인지 알려주어야 한다.
&Something::
[reference]
- 그럴려면 속해있는 클래스가 어디인지 알려주어야 한다.
Comment