Part2::Ch 01. 클래스 - 08. 정적 멤버

2025. 4. 29. 18:05Programming Language/C++

반응형

C++에서 정적 멤버(static member)는 클래스의 모든 인스턴스가 공유하는 변수 또는 함수를 의미한다.

 

정적 멤버 변수

클래스의 모든 객체가 공통으로 사용하는 하나의 변수로 하나의 클래스로 여러개의 객체를 만들 수 있는데 이 모든 객체가 동일하게 하나의 정적 멤버 변수의 메모리 위치를 바라보고 사용하게 된다.

#pragma once
#include <iostream>

class Car
{
private:
    static int carCount;  // 정적 멤버 변수 선언
public:
    Car() { carCount++; } // 객체가 생성될 때 마다 증가
    void print() {
        std::cout << this->carCount << std::endl;
    }
};

 

여기서 주의해야할 점은 이렇게만 사용하면 main에서는 사용이 불가능하다.

#include <iostream>
#include "Car.h"

int main() {
	Car c;
	Car c1;

	c.print();
	c1.print();
}

이는 정적 멤버변수가 초기화되지 않았기 때문인데 이 정적 멤버변수를 초기화 하기 위해서는 클래스의 외부에서 초기화가 한번은 무조건 필요하다.

이 정적 멤버 변수를 초기화하는 방법은 

타입 클래스명::정적멤버변수명 = 초기화값;

과 같이 사용하고 

int Car::carCount = 0;

이런식으로 클래스의 외부에 작성해줘야 한다

 

근데 만약 헤더파일에 정적 멤버변수의 초기화 코드를 넣게 되면

#pragma once
#include <iostream>

class Car
{
private:
    static int carCount;  // 정적 멤버 변수 선언
public:
    Car() { carCount++; } // 객체가 생성될 때 마다 증가
    void print() {
        std::cout << this->carCount << std::endl;
    }
};

int Car::carCount = 0;

헤더 파일을 include할때 마다 정적 멤버변수를 초기화하려고 할 것이기에 에러가 발생할 것이다.

그래서 조금 더 보면 정의가 이미 되어 있다고 하는데 사실 이 정적 멤버 변수의 경우는 클래스 내부에 작성된건 그냥 단순한 선언문이다.

class Car
{
private:
    static int carCount;  // 단순한 정적 멤버 변수의 "선언"
public:
    Car() { carCount++; } // 객체가 생성될 때 마다 증가
    void print() {
        std::cout << this->carCount << std::endl;
    }
};

int Car::carCount = 0; // 실제 정적 멤버 변수의 "정의"

 

클래스의 경우는 결국 객체를 만드는 틀이고 생성되었을때는 형체를 갖고 있지 않는다.

그러나 정적 멤버 변수의 경우는 " 정적 "이기 때문에 컴파일을 하면서 메모리 공간을 할당해야하기에 클래스에 넣어주는것(선언하는것)과는 별개로 외부에서 정적 멤버 변수의 공간을 할당할 "정의"를 필요로 하게 된다.

그렇기에 외부에 정적 멤버 변수의 정의를 해야하는 것이고 이 또한 여러번 정의 되지 않도록 헤더에 선언하기 보다는 cpp파일로 따로 빼서 include 하는 방식을 사용해야만 한다.

// Car.h 파일
#pragma once
#include <iostream>

class Car
{
private:
    static int carCount;
public:
    Car();
    void print();
};

=========================================================

//Car.cpp파일
#include "Car.h"

Car::Car() { carCount++; } // 객체가 생성될 때 마다 증가

void Car::print() {
    std::cout << this->carCount << std::endl;
}

int Car::carCount = 0;

 

또한 이렇게 객체가 만들어지기 이전에 정의되고 초기화 되기 때문에 이 멤버 변수가 public으로 접근 제한이 걸린 상태라면 그냥 클래스 상태에서도 멤버 변수에 접근도 가능하다.

// Car.h
#pragma once
#include <iostream>

class Car
{
public:
    static int carCount;
public:
    Car();
    void print();
};

============================
// main.cpp
#include <iostream>
#include "Car.h"

int main() {
	std::cout << Car::carCount << std::endl; // 객체를 만들지 않고 클래스 상태에서 접근도 가능
}

 

추가로 const로 정적 멤버 변수가 선언되는 경우에는 인라인에서 초기화가 가능하다.

// Car.h 파일
#pragma once
#include <iostream>

class Car
{
private:
    static const int carCount = 0;
public:
    Car();
    void print();
};

 

다만 이때는 

//Car.cpp파일
#include "Car.h"

Car::Car() { carCount++; } // 객체가 생성될 때 마다 증가

void Car::print() {
    std::cout << this->carCount << std::endl;
}

const int Car::carCount = 0;

이렇게 cpp파일에서 초기화를 다시하면 두번 초기화 됐다고 에러를 발생시킨다.

이렇게 인라인에서 초기화를 한다면 cpp파일에서 초기화가 불필요하니 사용할 필요가 없다.

// Car.h 파일
#pragma once
#include <iostream>

class Car
{
private:
    static const int carCount = 0;
public:
    Car();
    void print();
};

=========================================================

//Car.cpp파일
#include "Car.h"

Car::Car() { carCount++; } // 객체가 생성될 때 마다 증가

void Car::print() {
    std::cout << this->carCount << std::endl;
}

 

그리고 이렇게 const선언 없이도 inline으로 초기화하는 방법이 하나 더 있는데 이때는 inline이라는 키워드를 맨 앞에 사용해주면 된다.

// Car.h 파일
#pragma once
#include <iostream>

class Car
{
private:
    inline static int carCount = 0;
public:
    Car();
    void print();
};

이렇게 사용하는 경우엔 컴파일러가 c++ 17부터 가능하니 이점 참고 해서 사용하길 바란다

그리고 이렇게 초기화 할때에는 만약 정적 멤버 변수가 클래스 타입일 경우엔 아래와 같이

// Car.h 파일
#pragma once
#include <iostream>

class Company;
class Car
{
private:
    inline static Company comp = Company();
public:
    Car();
    void print();
};

전방선언은 불가능하다.

왜냐면 저렇게 사용하려면 Company의 모양을 알아야하기 때문에 이런 사용방식도 불가능하다.

 

정적 멤버 함수

객체와 무관하게 호출 가능한 클래스 소속 함수로

class Car {
private:
	int speed;
    static int carCount;
public:
    static void showCount() {
        std::cout << "Count = " << carCount << std::endl;
    }
};

 이렇게 구현되어 있을때 showCount라는 함수는 객체가 생성되기 이전에 호출이 가능하게 된다.

int main(){
    Car::showCount();
}

그렇기 때문에 이 정적 멤버 함수의 경우는 this를 가질 수 없다.

 

왜냐면 this라는 것은 객체자신을 바라보게 되어 있는데 객체가 생성되기 이전에 해당 함수를 사용할 수 있기에 this를 만들수 없기 때문이다.

그와 동시에 this를 사용할 수 없기에 비 정적 멤버 변수 및 함수에는 접근이 불가능하다.

class Car {
private:
    int speed;
    static int carCount;
public:
    void print(){
        std::cout << "print()함수 호출됨" << std::endl;
    }
    static void showCount() {
        std::cout << "Speed = " << speed << std::endl;
        // ㄴ 여기서 speed는 this -> speed로 컴파일러가 해석함
        print();
        // ㄴ 여기서 print()는 this -> print()로 컴파일러가 해석함
    }
};

보다 싶이 클래스의 함수의 내부에서는 멤버 변수에 접근할때 컴파일러가 this를 활용한것으로 인지하기 때문에 이렇게 비 정적 멤버 변수 및 함수에는 접근이 불가능해진다.

 

 

반응형