티스토리 뷰

주의 사항!

  • 이 글은 제가 직접 공부하는 중에 작성되고 있습니다.
  • 따라서 제가 이해하는 그대로의 내용이 포함됩니다.
  • 따라서 이 글은 사실과는 다른 내용이 포함될 수 있습니다.

 

C++에서는 멤버 변수와 멤버 함수에 static 선언을 추가할 수 있습니다. 이를 소개하기에 앞서 C언어에서 배웠던 static의 개념을 정리해보겠습니다.

  • 전역 변수에 선언된 static의 의미
    • 선언된 파일 내에서만 참조를 허용하겠다는 의미
  • 함수 내에 선언된 static의 의미
    • 한 번만 초기화되고, 지역변수와 달리 함수를 빠져나와도 소멸되지 않음

 

C++에서의 static이 지니는 의미를 이야기하기에 앞서, 간단한 예제 하나를 소개하겠습니다. 이 예제는 객체가 생성될 때마다 "n번째 객체가 생성되었습니다."라는 메시지를 출력하도록 임의의 클래스를 디자인했습니다.

#include <iostream>
using namespace std;

int simObjCnt = 0;
int cmxObjCnt = 0;

class Simple
{
public:
	Simple()
	{
		simObjCnt++;
		cout << simObjCnt << "번째 Simple 객체" << endl;
	}
};

class Complex
{
public:
	Complex()
	{
		cmxObjCnt++;
		cout << cmxObjCnt << "번째 Complex 객체" << endl;
	}

	Complex(Complex& copy)
	{
		cmxObjCnt++;
		cout << cmxObjCnt << "번째 Complex 객체" << endl;
	}
};

int main(void)
{
	Simple sim1;
	Simple sim2;

	Complex com1;
	Complex com2(com1);
	Complex();

	return 0;
}

/*
실행결과

1번째 Simple 객체
2번째 Simple 객체
1번째 Complex 객체
2번째 Complex 객체
3번째 Complex 객체

*/

 

simObjCnt는 Simple 객체들이 공유하는 변수이고, cmxObjCnt는 Complex 객체들이 공유하는 변수입니다. 그런데 이 둘은 전역 변수이기 때문에 문제를 일으킬 소지가 매우 높습니다. 정보은닉도 불가능하고, Simple 객체들이 공유하는 변수이지만 다른 객체에서도 쉽게 접근할 수 있기 때문입니다.

 

그러나 simObjCnt를 Simple 클래스의 static 멤버로, cmxObjCnt를 Complex 클래스의 static 멤버로 선언하면 이런 문제의 소지를 없앨 수 있습니다.

 

클래스 변수

static 멤버 변수는 '클래스 변수'라고도 합니다. 일반적인 멤버 변수와 달리 클래스당 하나씩만 생성되기 때문입니다. 때문에 같은 클래스의 객체들은 이 클래스 변수를 공유할 수 있습니다.

 

클래스 변수를 이용하여 위 예제를 수정해 보겠습니다.

#include <iostream>
using namespace std;

class Simple
{
private:
	static int simObjCnt;

public:
	Simple()
	{
		simObjCnt++;
		cout << simObjCnt << "번째 Simple 객체" << endl;
	}
};

int Simple::simObjCnt = 0;

class Complex
{
private:
	static int cmxObjCnt;
public:
	Complex()
	{
		cmxObjCnt++;
		cout << cmxObjCnt << "번째 Complex 객체" << endl;
	}

	Complex(Complex& copy)
	{
		cmxObjCnt++;
		cout << cmxObjCnt << "번째 Complex 객체" << endl;
	}
};

int Complex::cmxObjCnt = 0;

int main(void)
{
	Simple sim1;
	Simple sim2;

	Complex com1;
	Complex com2(com1);
	Complex();

	return 0;
}

/*
실행결과

1번째 Simple 객체
2번째 Simple 객체
1번째 Complex 객체
2번째 Complex 객체
3번째 Complex 객체

*/

 

static 멤버 변수로 simObjCnt를 선언할 때 초기화는 클래스 내에서 바로 못합니다. 그렇다고 생성자에서 초기화할 수도 없습니다. 왜냐하면 생성자에서는 이 변수를 1씩 올려야 하기 때문입니다. 그래서 클래스 외부에서 0으로 초기화를 해주는 모습입니다.

 

이는 다른 멤버 변수에게는 통하지 않는 방법인 것 같습니다. 책에서는 이러한 초기화 방법을 static 멤버 변수의 초기화 문법으로 별도로 정의되어 있다고 말합니다.

 

클래스 변수 접근

사실 static 멤버 변수는 어디서든 접근이 가능한 변수입니다. static 멤버가 private으로 선언되면 해당 클래스의 객체들만 접근이 가능하지만 public으로 선언되면 클래스의 이름 또는 객체의 이름을 통해서 어디서든 접근이 가능합니다. 다음 예제를 보겠습니다.

#include <iostream>
using namespace std;

class Simple
{
public:
	static int simObjCnt;

public:    //불필요하지만 멤버변수와 멤버함수를 구분하기 위해서 삽입하기도 함
	Simple()
	{
		simObjCnt++;
	}
};

int Simple::simObjCnt = 0;

int main(void)
{
	cout << Simple::simObjCnt << "번째 Simple 객체" << endl;
	Simple sim1;
	Simple sim2;

	cout << Simple::simObjCnt << "번째 Simple 객체" << endl;
	cout << sim1.simObjCnt << "번째 Simple 객체" << endl;
	cout << sim2.simObjCnt << "번째 Simple 객체" << endl;

	return 0;
}

/*
실행결과

0번째 Simple 객체
2번째 Simple 객체
2번째 Simple 객체
2번째 Simple 객체

*/

 

main 함수를 보면 클래스 이름을 통해서 static 멤버 변수인 simObjCnt에 접근하기도 하고, 객체 이름을 통해서 접근하기도 합니다. 그런데 객체 이름을 통해 접근하였다고 해서 해당 static 멤버 변수가 객체 내에 존재하는 변수는 아닙니다. static 멤버 변수는 별도의 메모리 공간을 가지고, 같은 클래스의 객체들이 이를 공유합니다.

 

따라서 static 멤버 변수에 접근할 때는 객체 이름을 이용하기보다 클래스 이름을 통해 접근하는 것이 좋습니다.

 

static 멤버 함수

static 멤버 함수 역시 그 특성이 static 멤버 변수와 동일합니다.

  • 객체 내에 멤버로서 존재하지 않습니다.
  • 별도의 메모리 공간을 가지며 같은 클래스의 객체들이 이를 공유합니다.
  • public으로 선언되면 클래스 이름을 통해 접근이 가능합니다.

 

static 멤버 함수는 객체 내에 멤버로서 존재하는 것이 아니기 때문에 다음과 같은 코드는 컴파일 에러를 일으킵니다.

class Simple
{
private:
	int num1;
	static int num2;

public:
	Simple(int n) : num1(n) {}
	static void Adder(int n)
	{
		num1 += n;    //컴파일 에러 발생
		num2 += n;    //컴파일 에러 발생
	}
};

int Simple::num2 = 0;

Adder 함수는 static 멤버 함수입니다. 따라서 별도의 메모리 공간을 가지며 이를 같은 클래스의 다른 객체들도 공유합니다. 따라서 이 함수 안의 num1과 num2가 어떤 객체의 것인지를 알 수가 없게 됩니다.

 

따라서 static 멤버 함수 내에서는 static 멤버 함수와 static 멤버 변수에만 접근할 수 있습니다. 그리고 이러한 특성의 static 멤버 변수와 static 멤버 함수를 잘 활용하면 대부분의 경우에 있어 전역 변수와 전역 함수를 대체할 수 있습니다.

 

const static

클래스 내에 선언된 const 멤버 변수의 초기화는 생성자에서 멤버 이니셜 라이저를 통해야 합니다. 그러나 const static으로 선언되는 멤버 변수는 다음과 같이 선언과 동시에 초기화가 가능합니다.

#include <iostream>
using namespace std;

class CountryArea
{
public:
	const static int RUSSIA = 1707540;
	const static int CANADA = 998467;
	const static int CHINA = 952790;
	const static int SOUTH_KOREA = 9922;
};

int main(void)
{
	cout << "러시아 면적 : " << CountryArea::RUSSIA << "km^2" << endl;
	cout << "캐나다 면적 : " << CountryArea::CANADA<< "km^2" << endl;
	cout << "중국 면적 : " << CountryArea::CHINA << "km^2" << endl;
	cout << "한국 면적 : " << CountryArea::SOUTH_KOREA << "km^2" << endl;

	return 0;
}

/*
실행결과

러시아 면적 : 1707540km^2
캐나다 면적 : 998467km^2
중국 면적 : 952790km^2
한국 면적 : 9922km^2

*/

 

const static 멤버 변수는, 클래스가 정의될 때 지정된 값이 유지되는 상수이기 때문에 위의 예제에서 보이는 바와 같이 초기화가 가능하도록 문법으로 정의하고 있습니다.

'공부 일지 > CPP 공부 일지' 카테고리의 다른 글

C++ | 객체 포인터와 다형성  (0) 2021.08.02
C++ | 상속  (0) 2021.08.02
C++ | 클래스와 함수에 대한 friend 선언  (0) 2021.08.02
C++ | const와 객체  (0) 2021.08.02
C++ | 복사 생성자와 explicit 키워드  (0) 2021.08.02
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함