티스토리 뷰

Computer Language/C & C++

[C++] 정적 멤버

HONGGG 2023. 12. 18. 15:18

정적 멤버

 지역 변수스코프 안에서 생성되고 스코프가 종료되면 해제된다. 멤버 변수의 경우 클래스 객체(인스턴스)가 생성될 때 메모리에 할당되고 해당 객체가 소멸할 때 같이 제거된다. 이말은 즉슨, 지역 변수나 멤버 변수로는 스코프나 객체 사이에 공용 변수나 공용 함수를 사용하기 위해 고정으로 메모리에 할당되어있는 객체가 필요하다는 말이다.

 

class A {
public:
    int X;
    void y() {)
};
int main() {
    A a, b;	
)

 

위와 같이 A라는 클래스의 인스턴스 a, b가 존재하면 둘 객체가 가진 x라는 변수는 전혀 다른 메모리에 할당되어있는 완벽히 다른 변수가 된다. 즉, 멤버 변수는 한 객체에 소속되어 각 다른 객체의 변수에 접근이 불가능하다는 것이다.

 

 


정적 멤버 메서드

정적 멤버 메서드의 필요성

정적 멤버 메서드(static member method)는 객체 지향 프로그래밍에서 클래스 수준에서 정의되고 해당 클래스의 인스턴스를 생성하지 않고도 호출할 수 있는 메서드이다.

 

외부에서 클래스 객체 사용
class Color {
public:
    ColorO : 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 result((a.GetR()+b.GetR())/2, (a.GetG()+b.GetG())/2, (a.GetB()+ b.GetB())/2);
    return result;
}

 

위 코드는 MixColors함수는 두 개의 Color 클래스 인스턴스를 인자로 받아 각 인스턴스의 멤버 함수를 호출하여 새로운 객체를 반환한다.

 

  1. a 객체와 b 객체를 메모리에 할당한다.
  2. a 객체와 b 객체를 Call by value를 통해 복사본을 MixColors 함수에 전달한다.
  3. a 객체와 b 객체의 GetR함수를 호출하여 함수 호출 스택에 쌓는다.
  4. a 객체와 b 객체의 GetG함수를 호출하여 함수 호출 스택에 쌓는다.
  5. a 객체와 b 객체의 GetB함수를 호출하여 함수 호출 스택에 쌓는다.
  6. 새로운 Color 인스턴스를 생성하며 생성자에 넘겨 값을 초기화 한다.
  7. 새로운 Color 인스턴스를 반환한다.

 

위 과정은 MixColors 함수를 호출할 때, 발생하는 이벤트들이다. 딱 보기에도 많은 이벤트가 발생하고 있다. 물론 위와 같이 외부에 클래스를 다루는 함수를 선언하는 경우는 드믈다.

 

멤버 함수를 통한 객체 사용
class Color {
public:
    ColorO : 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;
    )
    Color MixColors(Color b) {
    	return Color((r+b.GetR())/2, (g+b.GetG())/2, (b+b.GetB())/2);
    }
    
private:
    float r;
    float g;
    float b;
};

 

이번에는 위와 같이 MixColors함수를 멤버 함수로 선언하였다. 이 방법은 이전 외부에서 클래스 인스턴스를 주물럭 거리는 것보다는 훨씬 좋지만, Color 타입 인스턴스가 존재해야만 사용이 가능하다. 어떤 "객체를 통해" 함수를 호출해야만 하는 상황은 좋은 방법이 아니다.

Color red = Color(1, 0, 0);
Color blue = Color(0, 0, 1);
Color purple = red.MixColors(blue);

 

 

정적 멤버 함수

정적 멤버 메서드(static member method)는 객체 지향 프로그래밍에서 클래스 수준에서 정의되고 해당 클래스의 인스턴스를 생성하지 않고도 호출할 수 있는 메서드를 나타낸다.

 

정적 멤버 메서드는 static 키워드를 통해 선언이 가능하며 함수이기에 코드 영역에 저장된다. 해당 함수의 정의는 프로그램의 시작과 동시에 메모리에 올라가는 것이 아니라, 해당 함수가 사용되는 시점에 프로그램 메모리에 로드된다.

 

정적 멤버 함수는 인스턴스에 종속되지 않기 때문, 프로그램이 시작되는 시점에 해당 함수를 호출할 필요가 없다. 프로그램 실행 도중 언제든지 해당 클래스의 정적 멤버 함수를 호출할 수 있다.

 

정적 멤버 메서드 선언
#include <iostream>

using namespace std;

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;
    }
    
    Color MixColors(Color a, Color b) {
    	return Color((a.r + b.r) / 2, (a.g + b.g) / 2, (a.b + b.b) / 2);
    }
    static Color StaticMixColors(Color a, Color b) {
    	return Color((a.r + b.r) / 2, (a.g + b.g) / 2, (a.b + b.b) / 2);
    }
    
private :
    float r;
    float g;
    float b;
};

int main() {
    Color red = Color(1, 0, 0);
    Color blue = Color(0, 0, 1);
    Color orange = red.MixColors(red, blue);
    Color purple = Color::StaticMixColors(red, blue);
}

 

위 코드에서 Color 클래스에 정적 멤버 함수 MixColors가 선언되어있고, main에서 purple 인스턴스를 Color 클래스 정적 멤버 함수로 생성하고 초기화한다. 여기서도 정적 멤버 함수는 클래스 객체가 없어도 정적 멤버 함수가 넘겨받은 a와 b 인스턴스의 private 값(r, g, b)에 접근이 가능하다. 이는 정적(static) 멤버 함수가 클래스의 private 멤버 변수에 접근할 수 있는 것은 클래스의 멤버 함수들끼리는 private 멤버에 접근할 수 있다는 객체 지향 프로그래밍의 원칙 때문이다.

 

 


정적 멤버 변수

정적 멤버 변수(static member variable)는 객체 지향 프로그래밍에서 클래스 수준에서 선언되며, 해당 클래스의 모든 인스턴스 간에 공유되는 변수이다. 

 

정적 멤버 변수는 "객체"에 포함되지 않고 새로 생성되거나 객체가 없어도 존재한다.이는 static 키워드로 선언된 멤버 변수는 프로그램 시작과 동시에 데이터 영역에 저장되어 사용되기 때문이다. 즉, 클래스 내/외부에 선언된 정적(static) 변수는 클래스의 객체가 최초로 생성되기 전에도 이미 메모리에 할당되고, 정적 변수는 클래스 수준에서 선언되어 클래스의 인스턴스와 무관하게 존재하며, 클래스의 정의에 속한다.

 

#include <iostream>

class MyClass {
public:
    // 정적 멤버 변수 선언
    static int staticVariable;

    // 일반 멤버 변수
    int regularVariable;

    // 일반 멤버 함수
    void printVariables() {
        std::cout << "Static Variable: " << staticVariable << ", Regular Variable: " << regularVariable << std::endl;
    }
};

// 정적 멤버 변수 정의 및 초기화
int MyClass::staticVariable = 0;

int main() {
    // 객체 생성
    MyClass obj1;
    MyClass obj2;

    // 객체의 정적 멤버 변수와 일반 멤버 변수에 접근
    obj1.staticVariable = 42;
    obj1.regularVariable = 10;

    obj2.regularVariable = 20;

    // 두 객체가 공유하는 정적 멤버 변수와 각 객체의 일반 멤버 변수 출력
    obj1.printVariables();  // Static Variable: 42, Regular Variable: 10
    obj2.printVariables();  // Static Variable: 42, Regular Variable: 20

    return 0;
}

 

위 코드를 확인하면 정적 변수로 선언된 static variable은 서로 다른 두 객체가 선언되어도 동일한 변수가 사용한다. 즉, 두 객체 모두 해당 변수를 사용하는 주소 값이 동일하게 정해져 있다는 것을 의미한다.

 

 

정적 멤버 변수의 초기화

 정적 멤버 변수는 클래스 내부에서 초기화를 할 수 없다. 이는 객체 생성과 무관한 정적 멤버 변수가 클래스 내부 등호(=)로 객체 생성 시 초기화하려는 것이기 때문이다. 만약 클래스 내부 초기화가 가능하다면, 모두가 함께 사용하는 변수를 매번 새로운 객체가 생성될 때마다 자기 멋대로 초기화를 해버리는 것이다.

 

클래스 내부 초기화 시도
class MyClass {
private:
    // 정적 멤버 변수 선언
    // 오류!!!!
    static int staticVariable = 0;
}

 

위 코드에서는 데이터 영역의 전역 변수를 클래스 인스턴스가 생성되는 주기에 초기화를 진행하려고 하고 있다. 이는 이미 존재하는 

 

단, 클래스 외부의 전역 범위에서 클래스 정적 변수를 초기화 하는 것은 가능하다. 우선 초기화는 새로운 변수를 만들고 값을 대입하는 것이기에 단순 대입과 다르다는 것을 인지해야 한다. 그리고 앞서 계속해서 언급한 "전역 값은 객체와 상관 없이 메모리에 할당되어 있다."는 부분을 생각하면 위 상황을 이해하기 편해진다.

초기화와 대입
초기화는 변수를 새로 생성하며 값을 대입하는 것이며, 대입은 이미 만들어진 변수에 값만 새로 대입한다.

 

class MyClass {
private:
    // 정적 멤버 변수 선언
    static int staticVariable;
}

// 가능
int MyClass::staticVariable = 0;

 

위 코드에서 staticVariable 변수는 MyCalss 클래스 내부에 선언되고 클래스 외부 전역 범위에서 "MyClass::" 클래스 참조로 값을 가져와 초기화가 일어나고 있다. 이는 전역 범위의 작업은 정적 멤버 변수와 마찬가지로 데이터 영역에서 동작하기 때문이다. 즉, 프로그램 시작과 같이 초기화를 한번 진행하고 이후에는 값을 초기화 하지 않는다.

 

 위와 같이 정적 멤버는 접근 제어자와 상관 없이 초기화가 가능 하지만 단순 대입은 그렇지 않다. 예로 private으로 선언된 클래스 정적 멤버 변수는 다른 함수나 생성된 객체로 참조할 수 없다. 그렇지만 전역 범위(데이터 영역)에서 다른 전역 변수를 통한 대입은 진행이 가능하다.

 

class MyClass {
private:
    // 정적 멤버 변수 선언
    static int staticVariable;
}

int main(void) {
    // 오류!!! : private 값에 접근 불가능
    MyClass::staticVariable = 0;	
    
    return 0;
}

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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
글 보관함