본문 바로가기
  • 쓸쓸한 개발자의 공부방
C++

[C++] static member (정적 멤버), static 변수, static 함수

by 심찬 2021. 8. 22.

static 변수는 실무에서 사용에 매우 제한을 두는 코딩이다. 메모리가 할당되어 계속 자리를 차지 하고 있기 때문이다. 사용 방법을 정확히 파악해 꼭 필요한 경우에만 static 변수를 선언해 사용하자.

 

 

static member

 

전역 변수 기본 접근 : 자동차의 총 개수를 파악하기 위한 전역 변수를 아래와 같이 사용이 가능하다.

 

#include <iostream>

int cnt = 0;   // 전역 변수
               // 자동차의 총 개수를 파악하기 위해 cnt 변수 선언

class Car
{
    int speed;
    int color;
public:    
    //int cnt = 0;   // member 로 만들면 각 클래스 마다 각자 정보를 갖고 있음.
    
    Car()  {++cnt;}
    ~Car() {--cnt;}
};

int main()
{
    Car c1;
    Car c2;
    Car c3;
    
    cnt = 0;  // 실수로 값을 초기화 해버릴 수 있는 문제 있음.
    
    std::cout << cnt << std::endl;

}

main 안에서 실수로 값을 초기화 해버리는 문제가 생길 수 있는데, 이를 막을 수 있는 방법이 있을까?

 

정적 멤버 데이터(static member data)

 

 - static이 붙은 멤버 데이터

 - 모든 객체가 공유한다.

 - 클래스 내부에 선언을 만들고 ㅋ클래스 외부에 정의를 만들어야 한다.

 - 모든 객체가 공유한다.

 - 멤버 이므로 접근 지정자를 사용할 수 있다.

 

class 내부에 static으로 변수를 선언한다. 

private로 선언하면 class 내부에서 getter/setter를 통해 해당 변수에 접근이 가능하게 만들 수 있다.

 

#include <iostream>

//int cnt = 0;   // 외부에 선언하는 것이 아닌!

class Car
{
    int speed;
    int color;
    static int cnt;     // static으로 class 내부에 선언 (private 로 선언)
                        // 외부의 잘못된 사용을 막을 수 있다.
    
public: 
    Car()  {++cnt;}
    ~Car() {--cnt;}
    
    int getCount() { return cnt;}   // get을 통해 값을 가져 올 수 있다.
};
int Car::cnt = 0;


int main()
{
    Car c1;
    Car c2;
    Car c3;
    
    //cnt = 0;
    
    std::cout << c1.getCount() << std::endl;
}

class 내부에 static으로 선언된 cnt 값을 c1.getCount()로 호출해 가져올 수 있다.

 

 

 

정적 멤버 데이터와 일반 멤버 데이터

아래 코드에서

 - cnt 는 전역변수

 - Car::cnt는 정적 멤버 변수

c1, c2, c3는 각자 speed와 color를 별도로 갖고 있지만 Car::cnt는 딱 1개만 생성되어 있다.

객체를 만들지 않아도 Car::cnt는 프로그램 시작할 때 미리 생성이 된다.!

#include <iostream>

int cnt = 0;

class Car
{
public:
    int speed;
    int color;
    
    static int cnt; // declaration
    
    Car() {}
};

int Car::cnt = 0;   // definition

int main()
{
    // 정적 멤버 데이타는 객체가 없어도 메모리에 놓인다.
    // 클래스이름으로 접근 가능.
    Car::cnt = 10;
    
    Car c1, c2, c3;
    
}

 

정적 멤버 데이터 생성시 선언부와 정의부 분리해 만드는 경우

// ================= Car.h ===================
class Car
{
public:
    int speed;
    int color;
    static int cnt;
    
public:
    Car();
};

// ================= Car.cpp ===================

#include "Car.h"

int Car::cnt = 0;

Car::Car()
{
}

 

static member 변수에 접근하는 방법은 두 가지가 있는데 클래스 이름을 사용해 접근하는 것을 추천한다.

물론 객체 이름을 사용해 접근하는 것도 가능하지만 

코드를 나중에 봤을 때 static member 변수가 아닌 일반 변수로 오해할 소지가 있기 때문이다.

 

class Car
{
public:
    int speed;
    int color;
    
    static int cnt = 10; 
};

int main()
{
    Car::cnt = 0;   // 클래스 이름을 사용해 접근 가능
    
    Car c;
    c.speed = 0;
    c.cnt = 0;      // 객체 이름을 사용해 접근이 가능하다.
    
    Car::cnt = 0; // 클래스 이름을 사용해 접근하도록 추천 (가독성 향상)
}

 

 

 

static member 데이타 초기화

 

c++11부터 일반 멤버 데이터는 멤버 필드 초기화를 사용 가능하지만,

정적 멤버 데이터는 필드 초기화를 사용할 수 없다.

- 클래스 외부에 정의에서 초기값을 지정해야 한다.

  
class Test
{
public:
    int data = 0; // C++11 부터 지원  (선언 + 정의)

    //static int sdata1 = 0;  // error   : 
    
    static int sdata1;  // 선언 (declaration)
    
    static const int        sdata2 = 0; // ok
    //static const double     sdata3 = 0; // error
    static constexpr int    sdata4 = 0; // ok
    static constexpr double sdata5 = 0; // ok
    
    inline static int sdata6 = 0; // C++17
};
int Test::sdata1 = 0; // 정의 (definition)

int main()
{
    int n1 = Test::sdata1;
    int n2 = Test::sdata2;
    //int n3 = Test::sdata3;
    int n4 = Test::sdata4;
    int n5 = Test::sdata5;
    int n6 = Test::sdata6;
}

 

  외부 정의 멤버 필드 초기화
non static member data X O
static member data O X
static const 정수계열 X O
static const 비정수계열 O X
static constexpr X O
inline static X O

 

정적 멤버 함수 (static member function)

 

 - 함수에도 static을 붙여서 객체가 생성되지 않더라도 호출이 가능한 함수를 만들 수 있다.

 - 객체 없이 호출이 가능하다.

 - 객체 이름으로도 접근이 가능하지만 클래스 이름으로 호출하는 것이 좋다.

 - 일반 멤버(데이터, 함수)에 접근할 수 없다.

 - 정적 멤버(데이터, 함수)에 접근할 수 있다.

#include <iostream>

class Car
{
    int speed;
    static int cnt; 
public:    
    Car()  {++cnt;}
    ~Car() {--cnt;}
    
    static int getCount() { return cnt;}
};
int Car::cnt = 0;   // 

int main()
{
    // 자동차 개수를 알고 싶다.
    // getCount() 호출
    int value = Car::getCount();
    
    Car c1, c2, c3;
    
    int value2 = c1.getCount();
    
    std::cout << value << std::endl;
    std::cout << value2 << std::endl;
}

 

 

- 일반 멤버 함수는 각각의 객체에 대한 개별 연산

- 정적 멤버 함수는 모든 객체의 공통에 대한 연산

 

foo() 함수는 static으로 선언되었기 때문에 일반 변수인 data1, 일반 함수 goo() 사용이 불가하다.

goo() 함수는 일반 함수이기 때문에 모든 변수, 함수에 사용이 가능하다.

 

class Test
{
    int        data1;
    static int data2; 
public:
    static void foo()
    {
        data1 = 0; // error
        data2 = 0; // ok
        goo();     // error
    }
    void goo()
    {
        data1 = 0; // ok
        data2 = 0; // ok
        foo();     // ok
    }    
};
int Test::data2 = 0;
int main()
{
    Test::foo();
    Test t;
    t.goo();
}

 

main.cpp:8:9: error: invalid use of member ‘Test::data1’ in static member function

main.cpp:10:13: error: cannot call member function ‘void Test::goo()’ without object

 

댓글