Hoon222y

[1219-2] 정적 멤버 - 정적 멤버 변수/ 선언과 정의 본문

코딩/교육

[1219-2] 정적 멤버 - 정적 멤버 변수/ 선언과 정의

hoon222y 2018. 12. 19. 20:46

정적 멤버 변수 :  클래스 바깥에 선언되지만 클래스에 소속되며, 객체별로 할당되지 않고 모든 객체가 공유한다. 정적 멤버 변수는 객체와 논리적으로 연결되어 있지만 객체 내부에 포함되지 않으며, 클래스에 소속된다. (공유 멤버라고 이해하는것이 좀 더 직관적이다. )

예를들어 count 변수를 전역변수로 두어서 객체가 생성될 때 마다 count를 증가시키면서 객체의 개수를 확인한다고 가정하자. 이러한 경우에는 객체의 개수를 세는데는 문제가 없지만, 캡슐화 위반, 은폐가 불가능 등의 이유로 위험하다. 따라서 클래스 안에 해당 변수를 넣어주어야 한다. 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <iostream>
using namespace std;
//int cnt;          //데이터 영역
 
class Clazz{
public:
    //int cnt;            // -> 객체를 생성해야만 사용가능
                        // 정적 멤버 데이터 또는 정적 멤버 변수라고 한다.
                        // 클래스 안에 존재하는 전역변수이다.
                        // c++에서는 클래스도 small namespace라고 합니다.
    static int cnt;               //데이터 영역
    //Clazz() { cnt++; }
};
 
int Clazz::cnt =0;
 
int main(){
    Clazz c1;
    Clazz c2;
    Clazz c3;
                        //클래스 안의 전역 변수에 접근하려면 클래스의 이름과 범위 지정연산자를 사용해야 한다.
                        //이는 클래스도 일종의 작은 namespace 이기 떄문이다.
    cout << Clazz::cnt << endl;
}
cs


 해당 코드를 보면서 이해해보자. 일단 public 상에서 int cnt를 선언하게 된다면 무조건 Clazz 객체를 생성해야 만 해당 변수를 사용할 수 있다. 그런데 위에서 말한것 처럼 cnt 변수를 통하여 객체의 개수를 확인하고 싶은것이 목표이다. 하지만 그냥 int cnt라고 선언을 한다면 객체가 3개가 생성이 되어도 cnt 값을 찾게되면 1로 밖에 나오지 않는다. 이러한 문제를 해결하는 방법이 static 으로 선언하는 것이다. 

 변수 앞에 static 키워드를 붙여 정적 멤버임을 선언한다. 이 때, Clazz 클래스 소속이라는것만 알릴 뿐 메모리에는 할당하지 않는다. 이렇게 선언을 한 뒤에는 15번째줄 처럼 클래스 외부에 :: 연산자와 함께 소속을 밝히고 별로로 정의하고 초기화 해준다. 

 주의해야 할 점은 보통 헤더 파일에는 선언만 들어가므로 정적 멤버에 대한 정의는 cpp 파일에 한다. 헤더 파일에 정의하면 이중으로 정의되어 에러가 발생한다.

여기서 또 생각할 점은 예를들어 class 안에 변수가 int a; static int b; 가 있다고 가정해보자. 그러다면 해당 객체를 생성하고 sizeof i 를 하면 크기는 8이 아닌 4가 나온다. 이유는 정적 멤버는 모든 객체가 공유하므로 개별 객체의 크기에는 포함이 되지 않기 때문이다. 



이번에는 선언과 정의에 대해서 알아보자. 

 선언: 실제 기계어 코드가 생성되지 않으며, 단순히 컴파일러에게 알려주기 위한 정보일 뿐이다. 즉, 선언은 여러번 해도 상관이 없다. 

 정의: 실제 기계어 코드가 생성되기 때문에, 동일한 개념을 2번 이상 정의할 수 없다. 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Clazz{
public:
    //int cnt;                      //-> 객체를 생성해야만 사용가능
    //static int cnt;               //데이터 영역
    //Clazz() { cnt++; }
};
 
//선언은 실제 기계어 코드가 생성되지 않으며 단순히 컴파일러에게 알려주기 위한 정보일 뿐다. 즉 선언을 여러번 해도 문제 없다.
void foo();
void foo();
 
//extern을 통한 선언은 정보일 뿐이기 때문에 여러번 선언해도 상관 없다.
extern int cnt;
extern int cnt;
 
//정의는 실제 기계어 코드가 생성되기 떄문에 동일한 개념을 2번 이상 정의 할  수 없다.
int poo(){};
int poo(){};            //이 경우에는 에러가 발생한다. 정의를 두번 했기 때문이다. {}가 들어가서.
 
//이 경우에도 정의라서 에러가 발생
int cnt = 0;
int cnt = 0;            //에러 발생
cs


 해당 코드에서 보면 알 수 있듯 함수의 경우 {}로 정의를 두번 이상 하는경우 에러가 발생하고(18번 줄), 변수의 경우에도 = 을 통하여 두번이상 정의하는 경우 에러가 발생하는 것 을 볼 수 있다. 변수의 경우 앞에 extern을 사용하여 선언으로 바꿀 수 있는 방법이 있다. 하지만 이 때

1
2
extern int cnt=10;
extern int cnt=120;
cs

처럼 extern 하고 그냥 두는 것이 아니라 각각 10,120 으로 선언하게 되면 에러가 발생한다. 만약에 

1
2
extern int cnt=10;
extern int cnt;
cs

 이런식으로 선언하면 main 문에서 cnt 를 출력하는 경우 10이라는 값이 출력된다. 




 아래 코드의 경우 C와 C++에서의 선언과 정의를 하는 경우인데 

 설명하고자 하는 바가 C에서는 선언과 정의를 extern으로 하는데 C++ class내에서의 static 변수 선언과 정의는 11번째 줄과 15번째 줄로 한다는것을 설명하고자 하는것인가?  질문할것


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
using namespace std;
 
//in C
extern int global; //선언
int global;        //정의
 
//in C++
class Clazz{
public:
    static int cnt;             //선언 , extern int cnt;
    Clazz() { cnt++; }
};
 
int Clazz::cnt = 0;             //여기서 정의를 해주니까 첫번째 cout 이 실행이 되는것이다.
                                //따라서 객체가 공유해야 할 데이터가 있다면 이는 정적 멤버 변수로 선언하면 된다.
int main(){
    cout << Clazz::cnt << endl;
    Clazz c1;
    Clazz c2;
    Clazz c3;
    cout << Clazz::cnt << endl;
}
cs


Comments