변수에 총 3가지의 주기가 존재한다. 여기서 주기란, 생성과 소멸이 되는 부분들을 말한다.
- 자동 주기 : 일반 지역 변수 등등 생성과 소멸을 사용자 맘대로 할 수 없다.
- 동적 주기 : 동적 할당된 변수(new) 등등 생성과 소멸(delete)을 사용자 맘대로 할 수 있다.
- 정적 주기 : 전역 변수, 정적 변수 등등 프로그램이 시작과 동시에 생성이 되고, 프로그램 종료 시에 소멸이 된다.
여기서 우리가 볼 것은 정적 주기이다. 우리가 앞서 메모리 구조에서 데이터 영역은 자세히 알아보기 않았었다.
데이터 영역은 프로그램 시작 시 생성되고, 종료 시 소멸되는 메모리로, 정적 / 전역 변수들이 저장되는 곳이다.
이러한 데이터 영역의 특징 덕분에 메모리 누수의 걱정없이 사용할 수도 있지만, 프로그램이 종료되기 전까지
메모리를 계속 잡고있어야 한다는 단점이 있다.
static
정적이라고 부르며, 이 static이 어디에서 사용되는지에 따라 의미가 달라진다고 한다.
대부분 멤버 변수, 멤버 함수와 함께 사용을 하는데 C++에서 정적 멤버란 클래스에는 속하지만, 객체 별로 할당되지는
않고 해당 클래스의 모든 객체가 공유하는 멤버를 의미한다.
클래스의 모든 객체가 일반적인 멤버처럼 각각 할당한 것이 아닌, 정적 멤버 변수/함수의 메모리 하나만을 공유하는 것이다. 보통 사용 시에 생성자와 소멸자를 같이 사용하는 것 같다.
정적멤버변수는 클래스 영역 내에서 선언할 수 있지만, 정의는 전역 범위에서 해줘야 한다.
밑의 예시를 보면 된다.
//클래스
class Exam
{
static int a; //선언
};
int Exam::a = 1; //정의
//함수
void Sample
{
static int a = 1; //선언 정의
}
정적 멤버 변수는 모든 객체가 공유하는 멤버 변수이기 때문에 프로그램이 시작할 때 메모리가 바로 할당되어 값이 계속
유지된다. 이는 객체가 생성되지 전에도 이미 메모리가 존재한다는 말이다.
그래서 객체를 생성해주기 전에 반드시 정의/초기화를 해주어야 한다고 한다.
또한 생성과 초기화는 무조건 한번만 이루어져야 한다.
static멤버 함수는 객체의 이름으로도 접근이 가능하긴 하지만, 일반적인 멤버 변수와 구분하기 위해 대부분 클래스의
이름으로 접근하여 가독성을 높인다.
우리가 헤더 파일과 소스파일로 나눠서 코딩할 때, static 멤버 함수의 선언은 헤더 파일에서 해도 되지만, 정의는 반드시
소스 파일에서 해주어야 한다.
헤더 파일에서 정의 시, 다른 곳에서 해당 헤더 파일을 include 할 때마다 여러 번 정의가 돼버리기 때문이다.
앞서 배웠듯이 우리가 평소에 헤더 파일에서 선언만 해주던 이유와 같다.
범위를 벗어나도 static은 소멸하지 않는다.
위 그림처럼 Class의 객체 A, B, C를 만들었다. 그리고 각 객체마다 변수 a의 주소를 출력하면 모두 같은 값이 나올 것이다.
이게 무슨 뜻일까? 결국 객체 A, B, C의 멤버변수 a는 모두 하나를 가리키고 있다는 뜻이다.
따라서 객체 A에서 변수 a의 값을 변경하면, 다른 객체 B, C의 변수 a도 모두 바뀌는 방식인 것이다.
이번에는 Sample() 함수에서 정적 변수를 사용했을 때, main() 함수에서 호출을 총 3번 했다.
보통의 변수라면 함수 종료와 동시에 소멸되는 것이 원칙이지만, 정적 변수는 함수의 범위에서 벗어나도 소멸하지 않는다.
따라서 정적변수 a의 값은 호출할 때마다 +1식 증감되는 것이다.
Class 안에서 const를 통해 선언과 정의를 할 수 있다.
앞서 클래스 영역 안에서 정적 멤버 변수는 선언만 가능하고 정의는 전역 범위에서 해주어야 한다고 했다.
하지만 클래스 영역 안에서도 정의가 가능하도록 하는 방법이 있다.
//외부에서 정의
class Exam1
{
static int a;
};
int Exam1::a = 1;
//내부에서 정의
class Exam2
{
static const int a = 1;
};
바로 static const 멤버 변수를 만드는 것이다. 이는 const라서 한번 초기화 한 이후로 값을 변경하는 것이 불가능하기 때문에 클래스 내부에서 초기화가 가능해진다고 한다.
const 사용 시에 헤더 파일에서 못하던 정의도 가능하다고 한다.
Class에서 private로 접근 제한을 한다면?
static 멤버 변수는 클래스에서 private로 접근 제한을 걸어두어도, 클래스 외부에서 정의가 가능하다.
class Exam
{
private:
static int a;
};
int Exam::a = 5;
int main()
{
//Exam::a = 6; //불가능한 접근!!!
return 0;
};
하지만 private로 접근 제한이 되어있어서 정의는 어디까지나 전역 범위에서만 가능하고,
다른 내부 영역(main()함수 등등)에서는 접근이 불가능하다.
static 멤버 함수의 사용법
class Exam
{
public:
static int number; //정적멤버변수 선언
static int PrintNum(); //정적멤버변수 정의
};
int Exam::number = 0; //정적멤버변수 정의
int Exam::PrintNum() //정적멤버함수 정의
{
return number;
}
static 멤버 함수도 정의 및 호출 시에 객체 이름보다는 클래스의 이름으로 접근하는 것이 일반적이다.
또한 객체를 생성하지 않아도 클래스 이름으로만 호출할 수도 있다.
그리고 static의 특성상 어떤 객체와도 결합돼있지 않아서 정적 멤버 변수밖에 사용할 수 없다.
링크에 대해서
아무리 전역변수, 정적변수여도 하나의 소스파일(.cpp) 안에서만 사용이 가능하다. 그 이유는 바로 링크 때문이다.
링크에는 내부링크 / 외부링크의 종류가 존재하는데, 내부링크는 하나의 소스파일 안에서만 사용이 가능한 것을 말한다.
예시로 정적 전역 변수, 상수 전역 변수, 정적 함수 등이 내부링크이다.
반대로 외부링크는 다른 소스파일에서도 사용이 가능한 것을 말한다. 예시로 일반 함수들이 외부링크이다.
이처럼 static이 함수에 붙거나 없거나의 차이가 내부링크, 외부링크의 변화를 준다.
참고자료
https://ansohxxn.github.io/cpp/chapter8-10/
https://boycoding.tistory.com/169
'C++' 카테고리의 다른 글
C++ 배우기 24(가상함수, 재정의) (0) | 2022.11.25 |
---|---|
C++ 배우기 23(생성자, 소멸자) (0) | 2022.11.24 |
C++ 배우기 21(템플릿) (0) | 2022.11.17 |
C++ 배우기 20(헤더 / 소스 파일 나누기) (0) | 2022.11.16 |
C++ 배우기 19(객체지향프로그래밍, class) (0) | 2022.11.15 |