정적멤버 메서드
static은 정적멤버이다. 정적멤버의 반대는 동적멤버도 있다.
클래스는 정적멤버와 동적멤버로 구성되어있다.
동적멤버는 객체에 속해있다고 봐도 된다. 클래스 하나 당 정적멤버는 하나여야 하지만,
동적멤버는 붕어빵 찍어내듯이 여러개여도 상관 없다.
r,g,b색상을 입력받고, 색깔을 섞어주는 프로그램을 만들어보자.
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
32
33
34
35
36
37
38
39
40
41
42
43
|
#include <iostream>
using namespace std;
//0부터 1까지 float의 값으로 R,G,B저장
class Color {
public:
Color() :r(0), g(0), b(0) {}
Color(float r, float g,float b):r(r),g(g),b(b){}
//색깔을 바꿔야하는 경우
float GetR() { return r; }
float GetG() { return g; }
float GetB() { return b; }
private:
float r;
float g;
float b;
};
//색깔 섞은거 리턴해주는 함수
Color MixColors(Color a, Color b) {
Color res = Color((a.GetR() + b.GetR())/2, (a.GetG() + b.GetG())/2, (a.GetB() + b.GetB())/2);
return res;
}
int main() {
Color blue(0, 0, 1);
Color red(1, 0, 0);
Color purple = MixColors(blue, red);
cout << "r= " << purple.GetR() << "g= " << purple.GetG() << "b= " << purple.GetB() << endl;
}
|
cs |
전역에 있었던 MixColors함수를 Color 클래스 안에 넣으면
에러가 뜬다. 왜냐면 MixColors는 이제 멤버변수이기 때문에 어디에 속해있는 애인지 밝혀줘야 하기 때문이다.
그렇다고 purple.MixColors라 하기도 애매하다.
그래서 이런경우에 쓸 수 있는게 static이다.
static을 붙여주고
main함수에서는
Color::MixColors로 해주면 된다.
static의 가장 큰 장점은 private 필드까지 접근이 가능하다는 것이다!
굳이 get함수를 쓰지 않아도 a.r b.r 같이 private 필드에 있는 r 변수에 접근이 가능하다!
뿐만 아니라 정적메서드임에도 불구하고 blue.MixColors 같이 어떤 객체에 포함되어있는 것 처럼 쓸 수 있기도
하다.
정적 멤버 변수
정적멤버 변수는 모든 객체가 한 메모리를 공유하는 멤버변수이다. 객체 별로 각각 할당되는 멤버가 아닌 모든 객체가 공유하는 멤버이다.
정적멤버 변수의 특징은 특정한 객체에 포함된게 아니라 클래스에 포함되었다. 객체를 아무리 많이 찍어내도 static 변수는 하나만 있다. 이유는 위의 코드 예시를 보면 class가 Color하나만 있기 때문이다!
개념을 예제를 통해 살펴보자. Color 객체를 만들어주고 만들때 마다 고유의 번호를 생성해주도록 해보자
첫번째로 만들어지면 1번, 두번째로 만들어지면 2번 이런식으로..
고유의 번호를 생성해주는 전역변수의 idCounter가 1 증가하면 private 필드의 id값도 1증가하도록 만들어봤다.
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
|
#include <iostream>
using namespace std;
int idCounter = 1;
class Color {
public:
Color() :r(0), g(0), b(0),id(idCounter++) {
}
Color(float r, float g,float b):r(r),g(g),b(b),id(idCounter++) {}
//색깔을 바꿔야하는 경우
float GetR() { return r; }
float GetG() { return g; }
float GetB() { return b; }
int GetId() {
return id;
}
static Color MixColors(Color a, Color b) {
Color res = Color((a.r + b.r) / 2, (a.g + b.g) / 2, (a.b + b.b) / 2);
return res;
}
private:
float r;
float g;
float b;
int id;
};
int main() {
Color blue(0, 0, 1);
Color red(1, 0, 0);
Color purple = Color::MixColors(blue, red);
cout << "r= " << purple.GetR() << " ,g= " << purple.GetG() << " ,b= " << purple.GetB() << endl;
cout << "blue.GetId()= " << blue.GetId() << endl;
cout << "red.GetId()= " << red.GetId() << endl;
cout << "purple.GetId()= " << purple.GetId() << endl;
}
|
cs |
id(idCounter++)은
Color() :r(0), g(0), b(0) {
id=idCounter;
idCounter++;
}
과 같은 의미이다.
또한 주의할 점은 id값은 생성자의 매개변수로 받아주지 않고 필드에만 있다는점! (이거 때문에 나는 살짝 헷갈려서 써놨다)
그런데 전역에 선언된 idCounter변수와 Color 클래스는 밀접한 관련이 있다. 그래서 idCounter변수를 클래스의 멤버변수로 집어넣어주려 한다. 이렇게 전역으로 선언된 표현을 클래스 안으로 집어넣고 싶을 때 static이 사용된다.
뿐만 아니라 idCounter는 모든 객체가 공유하는 메모리이다. blue객체, red객체, purple객체가 이 idCounter를 공유하며 고유번호가 1씩 증가한다.
idCounter를 Color 클래스 안에 넣어주고 1로 초기화 시켜줬는데 에러가 떴다.
왜냐하면 초기값을 클래스 안에 집어넣을 수 없기 때문이다.
그래서 이것을 해결하기 위해
static int idCounter로 선언만 해주고 1로 초기화하는건 생성자 필드에 정의해 주었다.
하지만 이 방법이 에러는 뜨지 않을 수 있어도 정적멤버인 idCounter가 생성자에 들어가는건
이상하다.
왜냐하면 idCounter는 객체와 관련있는게 아닌 클래스와 관련이 있기 때문이다.
객체가 생성될때 idCounter가 초기화되는건 이상하기에 초기값을 주기 위해 클래스 밖으로 나갈 것이디ㅏ.
Color 클래스 밖에 int Color::idCounter=1;이라고 초기화해 주었다.
Color가 네임스페이스 같은 역할을 하는 것으로, 정적으로 선언된 멤버변수를 바깥에서 접근하고
싶을 때 클래스를 네임스페이스 취급해준다.
선언을 위에서 하고, 아래에서 정의하는 방식은 프로토타입과 네임스페이스를 공부하면서 배웠던 내용인데 정적변수에서도 선언과 정의를 분리해준다.
고유번호가 잘 나오는 것을 확인할 수 있다.
이때 static을 써줬을 때의 장점은
int idCounter; 만 전역으로 선언했을 때 idCounter를 쓰는 클래스가 Color말고도 엄청 많았다면
프로그램이 난잡해질거다.
예를 들어 colorIdCounter; studentIdCounter; 이런식으로 전역변수가 너무 많아지면 코드를 볼 때 난잡해질 것이다.
클래스랑 관련이 있는 변수나 함수는 정적으로 선언해서 클래스 안으로 넣어야 한다!(최대한 전역변수를 줄여줘야 한다..!)
좀 더 보면 좋은 자료들
https://ansohxxn.github.io/cpp/chapter8-10/
'C++ > 기초(두들낙서)' 카테고리의 다른 글
[C++ 기초] 멤버 메서드 활용 (0) | 2022.01.14 |
---|---|
[C++기초] 상수형 매개변수와 상수형 메서드 (0) | 2022.01.14 |
[C++기초] this 포인터 ,객체의 생성과 소멸,생성자와 소멸자 (0) | 2022.01.12 |
[C++기초] 클래스 개념소개,접근제어 지시자,구조체와 클래스의 차이점 (0) | 2022.01.12 |
[C++기초] 네임스페이스 (0) | 2022.01.12 |
댓글