본문 바로가기
C++/기초(두들낙서)

[C++기초] static

by Meaning_ 2022. 1. 14.
728x90
반응형

정적멤버 메서드

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(001);
    Color red(100);
 
    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(001);
    Color red(100);
 
    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++ Chapter 8.10 : static 정적 멤버 변수

인프런에 있는 홍정모 교수님의 홍정모의 따라 하며 배우는 C++ 강의를 듣고 정리한 필기입니다. 😀 🌜 [홍정모의 따라 하며 배우는 C++]강의 들으러 가기!

ansohxxn.github.io

 

728x90
반응형

댓글