[문과 코린이의 IT 기록장] C,C++ - static (static이란?, static 맴버 변수, static 맴버변수의 또 다른 접근방법, static 맴버함수, const static 맴버)
[문과 코린이의 IT 기록장] C,C++ - static
(static이란?, static 맴버 변수, static 맴버변수의 또 다른 접근방법, static 맴버함수, const static 맴버)
1. static이란?
: static변수는 지역변수와 전역변수의 성질을 둘 다 가지고 있는 변수이다. 즉, 지역변수처럼 static이 선언된 함수 내에서만 사용이 가능하며, 한번만 초기화를 할 뿐 전역 변수처럼 프로그램이 종료될 때까지 메모리공간에 존재하게 된다. (함수가 반환되도 소멸 X)
[ C언어에서 공부한 함수의 static 개념 ] 1) 전역변수에서 선언된 static의 의미 : 선언된 파일 내에서만 참조를 허용하겠다는 의미. 2) 함수 내에 선언된 static의 의미 : 한번만 초기화되고, 지역변수와 달리 함수를 빠져나가도 소멸되지 않는다. |
ex )
static int cnt;
- static변수는, 전역변수와 마찬가지로, 초기화하지 않으면 0으로 초기화된다.
- 이 문장은 딱 한번만 실행된다. 즉, cnt는 Counter함수가 호출될 때마다 새롭게 할당되는 지역변수가 아니다.
2. static 맴버 변수(클래스 변수)
a. SoSimple Static 맴버변수(클래스 변수) 관련 부분
class SoSimple{
private:
static int simObjCnt;
// static 맴버변수, 클래스 변수 : SoSimple 객체가 생성될 때 마다 함께 생성되어 객체별로 유지되는 변수가 아님. 객체를 생성하건, 생성하지 않건, 메모리 공간에 딱 하나만 할당되어서, 공유되는 변수이다.
public:
SoSimple(){
simObjCnt++;
cout<<simObjCnt<<"번째 SoSimple 객체"<<endl;
}
};
int SoSimple::simObjCnt = 0;
// static 맴버 변수의 초기화
b. SoComplex Static 맴버변수(클래스 변수)관련 부분
class SoComplex{
Private:
static int cmxObjCnt;
// SoComplex 내에 선언된 static변수. SoComplex 객체에 의해 공유된다.
Public:
SoComplex(){
cmxObjCnt++;
cout<<cmxObjCnt<<"번째 SoComplex 객체"<<endl;
}
SoComplex(SoComplex & copy){
cmxObjCnt++;
cout<<cmxObjCnt<<"번재 SoComplex 객체"<<endl;
}
// SoComplex 내부에서는, cmxObjCnt가 마치 맴버변수인 것처럼 접근이 가능하다.
};
int SoComplex::cmxObjCnt = 0;
// static 맴버 변수의 초기화
c. main함수 부분
int main(){
SoSimple sim1;
SoSimple sim2;
SoComplex cmx1;
SoComplex cmx2 = cmx1;
SoComplex();
return 0;
}
(1) static 맴버변수 공유 관계 이 경우, sim1, sim2 객체가 static변수 simObjCnt를 공유하고, cmx1, cmx2, 임시객체가 cmxObjCnt를 공유하는 형태를 보인다. 그러나, 이 변수는 각 객체 내부에 존재하는 것은 아니다. 이 변수는 객체 외부에 있으며, 단지 객체에게 맴버변수처럼 접근할 수 있는 권한을 줬을 뿐이다. 즉, SoSimple클래스 안에 선언된 static변수는, 모든 SoSimple객체가 공유하는 구조이며, 생성 및 소멸의 시점도 전역변수와 동일하다. (2) static 맴버변수를 생성자에서 초기화하면 안 되는 이유 SoSimple(){ simObjCnt = 0; simObjCnt++; cout<<simObjCnt<<"번째 SoSimple 객체"<<endl; } 만약 SoSimple의 생성자를, 이와 같이 정의한다면, 객체가 생성될 때마다 변수 simObjCnt는 0으로 초기화된다. 그러나 simObjCnt변수는 객체가 생성될 때 동시에 생성되는 변수가 아니고, 이미 메모리 공간에 할당이 이뤄진 변수를 의미하기 때문에, 이런 코드가 실행되는 것은 옳지 않다. 따라서 'static 맴버변수의 초기화 문법'은 이와 같이 별도로 지정되어야 한다. int SoSimple::simObjCnt = 0; // SoSimple 클래스의 static 맴버변수 simObjCnt가, 메모리 공간에 저장될 때 0으로 초기화해라. |
3. static 맴버변수의 또 다른 접근방법
사실 static 맴버변수는 어디서든 접근이 가능한 변수이다.
- 만약, static 맴버가 'private'로 선언되면, 해당 클래스의 객체들만 접근이 가능하지만, public으로 선언되면, 클래스의 이름 또는 객체의 이름을 통해 어디서든 접근이 가능하다. * static 맴버변수는 객체 내에 존재하지 않기 때문에
a. Static 맴버변수 부분
Class SoSimple{
public:
static int simObjCnt; // static 맴버변수가 public으로 선언된 것을 볼 수 있다.
...
};
b. 맴버변수 접근 부분 (main)
Int main(){
cout<<SoSimple::simObjCnt<<"번째 SoSimple 객체"<<endl;
// 객체를 하나도 생성하지 않은 상태. 클래스의 이름을 이용해서, simObjCnt에 접근하는 형태를 볼 수 있다. 즉, public으로 선언한 static 변수는 어디서든 접근이 가능하다.
SoSimple sim1;
SoSimple sim2;
cout<<SoSimple::simObjCnt<<"번째 SoSimple 객체"<<endl;
cout<<sim1.simObjCnt<<"번째 SoSimple 객체"<<endl;
cout<<sim2.simOjbCnt<<"번째 SoSimple 객체"<<endl;
// 마치 sim1객체에서 접근하는 것과, sim2객체에서 접근하는 것이 다른 결과를 보이는 것 같지만, 객체 내부에 simObjCnt 맴버변수가 존재하는 것이 아니기 때문에 같은 결과를 보인다.
// 하지만 이런 접근은 맴버변수에 접근하는 것과 같은 오해가 가능하기 때문에 추천하지 않는다. 따라서 public static 맴버에 접근할 때에는 'SoSimple::simObjCnt'과 같이 클래스의 이름을 이용해 접근하는 것이 좋다.
return 0;
}
4. static 맴버함수
: static 맴버함수 또한, 그 특성이 static 맴버변수와 동일하다.
[ static 맴버함수의 특징 ] 1) 선언된 클래스의 모든 객체가 공유한다. 2) public으로 선언이 되면, 클래스의 이름을 이용해 호출이 가능하다. 3) 객체의 맴버로 존재하는 것이 아니다. |
ex )
Class SoSimple{
Private:
int num1;
static int num2;
Public:
SoSimple(int n):num1(n){ }
static void Adder(int n){
num1 += n; // 컴파일 에러 발생
num2 += n;
}
};
int SoSimple::num2 = 0;
- static 맴버함수인 Adder에서, 맴버변수인 num1에 접근하는 것은 컴파일 에러를 발생시킨다. 즉, static 맴버함수 내에서는, static 맴버변수와 static 맴버함수만 호출 가능하다. (Static은 객체의 맴버가 아니기 때문, 객체 생성 이전에도 호출 가능)
5. const static 맴버
- const static으로 선언되는 맴버변수(상수)는 선언과 동시에 초기화가 가능하다.
a. Const Static 부분
class CountryArea{
Public:
const static int RUSSIA = 1707540;
// const static으로 선언되는 맴버변수(상수)는, 클래스 내부에서 선언과 동시에 초기화가 가능하다.
....
};
- const static 상수는 하나의 클래스에 둘 이상 모이는 것이 보통이다.
- 이와 같이 const static 맴버변수는, 클래스가 정의될 때 지정된 값이 유지되는 상수이기 때문에, 초기화가 가능하도록 문법으로 정의하고 있다.
b. Main부분
Int main(){
cout<<"러시아 면적 : "<< CountryArea::RUSSIA<<"km2"<<endl;
// CountryArea클래스에 정의된 상수에 접근하기 위해, 굳이 객체를 생성할 필요는 없다. (public이기 때문에, 클래스의 이름을 통해 접근 가능하기 때문).
...
}
* 유의사항 - 아직 공부하고 있는 문과생 코린이가, 정리해서 남겨놓은 정리 및 필기노트입니다. - 정확하지 않거나, 틀린 점이 있을 수 있으니, 유의해서 봐주시면 감사하겠습니다. - 혹시 잘못된 점을 발견하셨다면, 댓글로 친절하게 남겨주시면 감사하겠습니다 :) |