본문 바로가기
Programming/C++

C++ 언어 기초 (8) - static 데이터 멤버와 static 멤버함수

by 롱일스 2020. 8. 28.
반응형

▣ 객체의 메모리 공간

 ●  객체를 저장하기 위한 메모리 공간

 

  ▷ 일반적으로 객체마다 개별적으로 데이터 멤버가 메모리 공간을 할당받아 각각 존재한다. 멤버함수는 재호출이 가능한 구조이기 때문에 객체마다 개별적으로 멤버함수를 가질 필요 없이 공유해서 사용한다.

  ▷ 때에 따라 클래스 전체에 속하는 데이터 멤버를 사용하고 싶을 때도 있다. --> static 데이터 멤버

 

 ●  static 데이터 멤버

  ▷ 클래스에 속하는 모든 객체들이 공유하는 데이터 멤버

  ▷ 객체 생성과 관계 없이 프로그램이 시작되면 static 데이터 멤버를 위한 메모리 공간이 할당된다.

  ▷ 일반 데이터 멤버와는 달리, static 데이터 멤버는 클래스 선언문 내에서는 선언만 하고 클래스 외부에서 별도로 정의해야 한다.

  ▷ static 데이터 멤버는 클래스 전체에 공유되는 메모리로 obj1, obj2, obj3가 똑같은 데이터를 공유해서 사용할 수 있다.

  ▷ static 데이터 멤버는 객체가 없는 상태에서도 사용할 수 있다.

 

 ●  static 멤버함수

  ▷ 특정 객체에 대한 처리를 하는 것이 아니라, 소속 클래스 단위의 작업을 수행하는 함수

  ▷ static 멤버함수는 객체가 정의되지 않아도 사용할 수 있다.

  ▷ static 멤버함수 안에서는 일반 멤버를 사용할 수 없으며, static 멤버만 사용할 수 있다.

 

 

▣ 예제!! 

 ●  NamedObj 클래스

  ▷ 이름을 갖는 객체를 만들 수 있는 클래스를 정의하고자 한다.

  ▷ 객체가 생성될 때 고유번호를 가지게 되는데, 이 번호는 NamedObj 객체가 생성됨에 따라 1번부터 시작해서 차례로 부여되는 일련번호다.

  ▷객체는 자기 자신의 일련번호와 이름을 출력할 수 있고, 현재 존재하는 NamedObj 클래스의 객체 수를 구할 수 있다.

 

메소드 비고
NamedObj(const char *s) 생성자 - 이름을 s로 초기화 한다.
~NamedObj ( ) 소멸자
void display( ) ID와 이름을 출력한다.
static int nobj( ) 현재 존재하는 객체의 수를 구한다.

  ▷ nobj( )함수는 객체가 하나도 없어도 0이라는 값을 가질 수 있어야 하기 때문에 static으로 선언한다.

속성 비고
char *name 이름을 저장한다.
int id ID 번호를 저장한다.
static int nConstr 생성된 객체의 수
static int nDestr 소멸된 객체의 수

  ▷ 생성된 객체의 수와 소멸된 객체의 수도 마찬가지로 생성되지 않고 소멸되지 않아도 0의 값을 가질 수 있도록 static으로 선언한다.

 

 ●  NamedObj 클래스 - NamedObj.h 

class NamedObj {
    char *name;
    int id;
    
    //static 데이터 멤버
    static int nConstr;   // 생성된 객체의 수
    static int nDestr;    // 소멸된 객체의 수
    
public:
    NamedObj(const char *s);  // 생성자
    ~NamedObj();			  // 소멸자
    void display() const {   // 객체의 속성 출력
      cout << "ID : " << id << " --> 이름 : " << name << endl;
    }
    static int nObj(){       //존재하는 객체 수 반환
      return nConstr - nDestr;
    }
};
  • static 데이터 멤버는 클래스 선언문 내에서 선언만 하고, 멤버 정의는 별도로 해야 한다.
  • 생성자와 소멸자의 원형만 선언했다. body 정의는 클래스 선언문 외부에서 정의한다.
  • 만들어진 객체수(nConstr)에서 소멸된 객체수(nDestr)을 뺀 값이 현재 존재하는 객체의 수다.
  • static 멤버함수인 nObj에서는 static 데이터 멤버인 nConstr와 nDestr만 사용한다.

 

 ●  NamedObj 클래스 - NamedObj.cpp

#include <cstring>
#include "NamedObj.h"

NamedObj::NamedObj(const char *s)
{
    name = new char[strlen(s)+1]; //문자열을 복사할 공간을 할당한다.
    strcpy(name, s);
    id = ++nConstr;  // 생성된 객체의 수를 증가시키고 이를 ID로 부여한다.
}

NamedObj::~NamedObj()
{
    ++nDestr;   // 소멸된 객체의 수를 증가시킨다.
    delete [] name;
}

// static 데이터 멤버의 정의 및 초기화
int NamedObj::nConstr = 0;
int NamedObj::nDestr = 0;

void f()
{
    NamedObj x("Third"); // 세 번째 객체의 생성
    x.display();         // 함수 반환 후 x는 소멸된다.
}

int main()
{
    cout << "NamedObj ---- : " << NamedObj::nObj() << endl;
    NamedObj a("First")  // 첫 번째 객체 생성
    NamedObj b("Second"); // 두 번째 객체 생성
   
    f();
    NamedObj c("Fourth");  //네 번째 객체 생성
    c.display();
    cout << "NamedObj num : " << NamedObj::nObj() << endl;
    return 0;
}
  • 클래스 선언문이 바깥(NamedObj.h)에 있기 때문에 "NamedObj::"를 통해 NamedObj의 멤버라는 것을 표기해줘야 한다.
  • 문자열 s의 길이에 1을 더한 크기만큼 new로 메모리 공간을 할당한다. (문자열의 끝에는 문자열의 끝을 알리는 null 문자가 들어가야 하기 때문에 실제 문자열의 개수에 1을 더해준다.)
  • strcpy로 s에 저장된 문자열을 name에 복사한다.
  • nConstr을 처음엔 0으로 설정하고 객체가 생성될 때마다 1씩 증가하도록 id에 ++nConstr를 대입한다. 
  • 아까 헤더파일에서 선언만 했던 2개의 static 데이터 멤버를 정의하고 초기화해준다.
  • static 멤버함수인 nObj()는 객체가 없어도 호출할 수 있어서 객체 없이 호출하기 위해 소속을 NamedObj라고 NamedObj::nObj()라고 표기한다. 아직 객체가 아무것도 생성안됐기 때문에 "생성자수-소멸자수 = 0-0=0"을 출력한다.
  • NamedObj a(...)를 통해 id = 1이 되고 NamedObj b(...)로 id=2가 된다.
  • f( ); 를 통해 id=3이 되고, 함수가 끝나면서 소멸자가 동작하여 소멸자 수가 1개 늘어난다. nDestr = 1.
  • NamedObj c(...)를 통해 4번째 객체가 생성된다. nConstr = 4, id = 4.
  • nObj( )로 nConstr - nDestr = 4 - 1 = 3을 출력한다. 
  • 특정 객체를 위한 데이터 멤버나 함수가 아니라 클래스 전체를 위해 사용되는 함수와 데이터를 선언할 땐 static으로 선언해야 한다.

 

728x90
반응형