Brise

[C++ 기본 3] 2. 정적 멤버 본문

프로그램/C,C++

[C++ 기본 3] 2. 정적 멤버

naudhizb 2022. 4. 4. 13:49
반응형

C언어에서의 static 변수 또는 static 함수는 정해진 영역 내에서만 고유하게 접근 가능한 변수나 함수를 의미한다.
여기서 정해진 영역이란 파일 또는 중괄호({}) 사이에 위치한 섹션을 의미한다.

C++에서는 클래스의 선언(아마 구조체에도 static 선언이 가능할 것으로 보인다. 구조적으로..) 의 멤버변수를 static으로 선언할 수 있는데, 이 경우 해당 클래스를 이용하는 모든 인스턴스의 변수가 통일되어 사용된다.
또한 해당 정적 멤버의 경우 정적으로 할당되기 때문에 정의 영역이 클래스 밖에 위치하게 되며 인스턴스가 생성되기 이전에도 활용할 수 있다.

// 2_정적멤버1 - 110 page
#include <iostream>

//int cnt = 0;  // 전역변수. 모든 객체가 공유 한다.
              // 어디서나 접근 가능하다.
class Car
{
    int color;
    static int cnt;  // 객체당 한개가 아니라 모든 객체가 공유한다
public:
    //int cnt = 0;    // 객체당 한개씩 생성된다.
    Car()  { ++cnt; }
    ~Car() { --cnt; }
    
    int getCount() { return cnt; }
};
int Car::cnt = 0;

int main()
{
    Car c1;
    Car c2;

    //cnt = 10;

    //std::cout << c1.cnt << std::endl;
    //std::cout << cnt << std::endl; // 2
    std::cout << c1.getCount() << std::endl; // 
}
// 2_정적멤버1 - 110 page
#include <iostream>

// static 멤버 데이타 특징
// 1. 클래스 안에 선언, 클래스 외부에 정의 를 만들어야 한다.
// 2. 객체가 없어도 메모리에 놓이게 된다.
// 3. 객체 생성시 static 멤버 데이타는 모든 객체가 공유하게 된다.
// 4. 접근 방법
//     Car::cnt
//     c1.cnt
//        java, C# 에서는 클래스 이름으로만 사용가능합니다.
//        Car.cnt  => java, C# 스타일 코드

class Car
{
    int color;
public:
    static int cnt; 
    Car() { ++cnt; }
    ~Car() { --cnt; }

    int getCount() { return cnt; }
};
int Car::cnt = 0;

int main()
{
    // 객체가 없어도 static 멤버 데이타에 접근 할수 있다.
    // 단, public 인 경우.
    std::cout << Car::cnt << std::endl;

    Car c1;
    Car c2;

    // 객체의 크기에는 static 멤버 데이타는 포함되지 않는다
    std::cout << sizeof(c1) << std::endl; // 4

    // static 멤버 데이타에 접근하는 방법은 2가지 입니다
    std::cout << Car::cnt << std::endl; // 2
    std::cout << c1.cnt << std::endl;   // 2
}
// 2_정적멤버1 - 110 page
#include <iostream>


class Car
{
    int color;
    static int cnt;
public:    
    Car() { ++cnt; }
    ~Car() { --cnt; }

    // 일반 멤버 함수 : 객체가 있어야만 호출할수 있다.
    //                   c1.getCount();

    // static 멤버 함수 : 객체가 없어도 호출가능한 함수
    //                  Car::getCount();
    //                    c1.getCount();    모두 가능
    static int getCount() { return cnt; }
};
int Car::cnt = 0;

int main()
{
    std::cout << Car::getCount() << std::endl;
    Car c1;
    Car c2;
    std::cout << c1.getCount() << std::endl;
}

정적 변수의 경우 인스턴스와 별개로 클래스에 의하여 접근이 가능하다고 하였다. 그리고 정적 함수 또한 인스턴스가 생성되지 않은 경우에도 접근 가능하여야 한다. 때문에 정적 함수에서는 인스턴스와 관계없이 접근하여 동작 가능하여야 하기 때문에 클래스의 정적 변수에만 접근할 수 있다.

만약 C언어와 같이 다른 파일에서 접근 불가능하게 하고 싶다면 private 키워드를 이용하여 private 함수를 선언하여 사용하여야 한다.

class Car
{
    int color;
    static int cnt;
public:
    // 다음중 에러를 모두 골라 보세요
    void foo()    {
        color = 0;    // 1
        cnt = 0;    // 2
        goo();        // 3
    }
    // 핵심 : static 멤버 함수 안에서는 static 멤버 데이타만 접근할수
    //        있다.
    static void goo()    {
        color = 0;    // 4. error
        cnt = 0;    // 5. ok
        foo();        // 6. error
    }
};
int Car::cnt;  // 초기값을 지정하지 않으면 0(전역변수의 특징과 동일)
int main()
{
    // 힌트 : static 멤버 함수는 객체가 없어도 호출할수 있습니다.
    Car::goo();
}

개인적으로 C++언어에서 정적 변수, 정적 함수를 이해하며 가장 힘들었던 부분 중의 하나는 C언어와 달리 파일과 헤더에서 서로 선언하는 것에 대한 일관성이 없다는 점이었다.

#include <iostream>

// Car.h
class Car
{
    int color;
    static int cnt;
public:
    Car();
    ~Car();
    static int getCount();
};
// Car.cpp
int Car::cnt;

Car::Car() { ++cnt; }
Car::~Car() { --cnt; }
int Car::getCount() { return cnt; }

// 핵심 1. static 멤버 함수는 선언부에만 static 을 표기합니다.
// 2. static 멤버 변수의 외부 정의는 .cpp에 있어야 합니다.

위와 같은 경우에 헤더와 변수에 대하여 다른 것들이 선언된다.
정적 변수의 경우에는 헤더에 선언되며 파일에 정의됩니다.
정적 변수와 정적 함수 모두 헤더에 선언할 때 static 키워드를 사용하여 선언하며, 파일에서 정의할 때 static 키워드 없이 클래스 명(namespace와 비슷한)을 이용하여 정의합니다.

여기까지 클래스에 정적 멤버를 정의하면 하나의 변수만이 지정된다는 것을 알았다. 그렇다면 만약 템플릿을 사용하는 경우는 어떨까? 정답은 템플릿 타입마다 정적 멤버가 정의된다. 단, 당연히 기본 템플릿 문법을 적용하여 사용하여야한다.


//int global = 0; // 전역변수.

int x[1024];
//int y[1024] = { 0 };

template<typename T> 
class Car
{
public:
    static T cnt;
};
template<typename T> T Car<T>::cnt = T();
                    // T() : zero initialization


int main()
{
//    Car c; // error. Car 는 타입(클래스)가 아니라 타입을 만드는
            // 틀(template )
    Car<int> c1; // ok.. Car<int> 는 타입 입니다.

    //Car::cnt = 10; // error
    Car<int>::cnt = 10; // ok.
    Car<double>::cnt = 10; // ok.
    // 결론 : Car => 타입을 만드는 틀
    //          Car<T> =>  타입.(T는 int, double등..)
}

위의 파일을 컴파일하면 아래와 같이 생성된다

아래 L3 섹션을 보면 여러 템플릿 정적 변수가 있는 것을 확인할 수 있다.

반응형

'프로그램 > C,C++' 카테고리의 다른 글

[C++ 기본3] 4. 상수멤버함수  (0) 2022.05.15
C++ Design Pattern  (0) 2022.04.07
[C++ 기본 3] 3. this  (0) 2022.04.04
[C++ 기본 3] 1. 객체복사  (0) 2022.04.04
[C++ 기본 2] 내용 정리  (0) 2022.04.04
[C++ 기본 2] 12. 객체 복사(shallow copy)  (0) 2022.03.03
[C++ 기본 2] 11. 복사 생성자  (0) 2022.03.03
Comments