인스턴스 vs 정적 멤버
Coddy C++ 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 104개 중 27번째.
지금까지 여러분이 생성한 모든 멤버 변수는 특정 객체에 속해 있습니다. 두 개의 Player 객체를 생성하면, 각 객체는 자신만의 name과 health를 가집니다. 이것들을 인스턴스 멤버라고 부르며, 클래스의 각 인스턴스마다 별도로 존재합니다.
하지만 모든 객체에서 공유되는 데이터가 필요하다면 어떻게 해야 할까요? 예를 들어, 전체 플레이어 수가 몇 명인지 추적하는 경우입니다.
이것이 정적 멤버(static members)가 등장하는 이유입니다. 정적 멤버는 특정 객체가 아니라 클래스 자체에 속합니다.
class Player {
std::string name; // 인스턴스 멤버 - 각 플레이어는 자신만의 이름을 가집니다
int health; // 인스턴스 멤버
static int playerCount; // 정적 멤버 - 모든 플레이어가 공유합니다
public:
Player(std::string n) : name(n), health(100) {
playerCount++; // 플레이어가 생성될 때마다 증가합니다
}
~Player() {
playerCount--; // 플레이어가 소멸될 때마다 감소합니다
}
};주요 차이점: 인스턴스 멤버는 객체를 통해 접근하는 반면, 정적 멤버는 클래스 이름 자체를 통해 접근할 수 있습니다. 인스턴스 멤버는 "객체당 하나", 정적 멤버는 "클래스당 하나"라고 생각하면 됩니다.
| 인스턴스 멤버 | 정적 멤버 |
|---|---|
| 각 객체마다 고유함 | 모든 객체 간에 공유됨 |
| 객체가 생성될 때 생성됨 | 객체가 생성되기 전부터 존재함 |
객체를 통해 접근: player.health | 클래스를 통해 접근: Player::playerCount |
정적 멤버는 카운터, 설정 값 또는 개별 객체보다는 논리적으로 클래스에 속하는 모든 데이터에 유용합니다.
챌린지
쉬움인스턴스 멤버와 정적(static) 멤버를 사용하여 개별 제품의 상세 정보와 전체 재고 통계를 추적하는 제품 재고 시스템을 구축해 보겠습니다.
코드를 정리하기 위해 두 개의 파일을 생성합니다:
Product.h: 개별 제품을 관리하면서 재고 전체 데이터를 추적하는Product클래스를 정의합니다. 클래스에는 다음이 포함되어야 합니다:- 인스턴스 멤버:
name(string) 및price(double) — 각 제품마다 고유함 - 정적 멤버
totalProducts: 현재 존재하는 제품의 총 개수를 카운트함 - 정적 멤버
totalValue: 모든 제품의 합산 가격을 추적함 - 생성자: 이름과 가격을 인자로 받아 인스턴스 멤버를 초기화하고, 두 정적 카운터를 업데이트함 (개수 증가, 총 가치에 가격 추가)
- 소멸자: 제품이 제거될 때 정적 카운터를 업데이트함 (개수 감소, 총 가치에서 가격 차감)
getInfo()메서드:"<name>: "<name>: $<price>"lt;price>"형식의 문자열을 반환함- 정적 메서드
getInventoryStats():"Products: <count>, Total Value: "Products: <count>, Total Value: $<value>"lt;value>"형식의 문자열을 반환함
- 인스턴스 멤버:
main.cpp: 인스턴스 멤버가 고유하게 유지되는 동안 정적 멤버가 모든 객체에서 어떻게 공유되는지 보여줍니다. 입력으로부터 두 개의 제품 이름과 가격(name1, price1, name2, price2 — 각각 별도의 줄에 입력됨)을 읽은 후 다음을 수행합니다:- 클래스 이름을 사용하여 초기 재고 통계를 출력합니다:
Product::getInventoryStats() - 중괄호를 사용하여 스코프 블록을 만들고, 그 안에서 첫 번째 이름과 가격으로
Product를 생성합니다. 해당 제품의 정보와 현재 재고 통계를 출력합니다. - 블록이 끝난 후(첫 번째 제품이 소멸됨),
"After first scope:"를 출력하고 이어서 재고 통계를 출력합니다. - 두 번째 이름과 가격으로 또 다른
Product를 생성합니다. 해당 제품의 정보와 재고 통계를 출력합니다. - 이름이
"Bonus"이고 가격이5.00인 세 번째Product를 생성합니다. 최종 재고 통계를 출력합니다.
- 클래스 이름을 사용하여 초기 재고 통계를 출력합니다:
정적 멤버는 헤더 파일의 클래스 정의 외부(클래스 정의 뒤)에서 정의해야 함을 기억하세요. 가격은 <iomanip>의 std::fixed와 std::setprecision(2)를 사용하여 소수점 둘째 자리까지 형식을 맞춥니다.
main.cpp에 #include "Product.h"를 사용하여 헤더 파일을 포함시키세요.
치트 시트
C++는 두 가지 유형의 클래스 멤버를 지원합니다: 인스턴스 멤버와 정적 멤버.
인스턴스 멤버는 각 객체마다 고유합니다 - 모든 인스턴스는 자신만의 복사본을 가집니다:
class Player {
std::string name; // 각 플레이어는 자신만의 이름을 가집니다
int health; // 각 플레이어는 자신만의 체력을 가집니다
};정적 멤버는 클래스의 모든 객체 간에 공유됩니다 - 클래스 자체에 속하는 단 하나의 복사본만 존재합니다:
class Player {
static int playerCount; // 모든 플레이어에 의해 공유됨
public:
Player(std::string n) : name(n), health(100) {
playerCount++; // 공유 카운터 증가
}
~Player() {
playerCount--; // 파괴될 때 감소
}
};주요 차이점:
| 인스턴스 멤버 | 정적 멤버 |
|---|---|
| 각 객체마다 고유함 | 모든 객체 간에 공유됨 |
| 객체가 생성될 때 생성됨 | 객체가 생성되기 전부터 존재함 |
객체를 통해 접근: player.health | 클래스를 통해 접근: Player::playerCount |
정적 메서드는 클래스 이름을 사용하여 호출할 수 있으며 정적 멤버에만 접근할 수 있습니다:
class Player {
static int playerCount;
public:
static int getPlayerCount() {
return playerCount; // 정적 멤버에만 접근 가능
}
};
// 클래스 이름을 사용하여 호출
int count = Player::getPlayerCount();정적 멤버는 클래스 외부에서 정의되어야 합니다 (일반적으로 .cpp 파일이나 헤더의 클래스 정의 뒤에서):
int Player::playerCount = 0;정적 멤버는 카운터, 공유 설정 값 또는 개별 객체보다는 논리적으로 클래스에 속하는 모든 데이터에 유용합니다.
직접 해보기
#include <iostream>
#include <string>
#include "Product.h"
using namespace std;
int main() {
// 입력 읽기
string name1;
double price1;
string name2;
double price2;
getline(cin, name1);
cin >> price1;
cin.ignore();
getline(cin, name2);
cin >> price2;
// TODO: Product::getInventoryStats()를 사용하여 초기 재고 통계 출력
// TODO: 중괄호를 사용하여 스코프 블록 생성
// 블록 내부:
// - name1과 price1으로 Product 생성
// - getInfo()를 사용하여 정보 출력
// - 현재 재고 통계 출력
// TODO: 블록이 끝난 후, "After first scope:" 출력
// 그 다음 재고 통계 출력
// TODO: name2와 price2로 또 다른 Product 생성
// 해당 정보와 재고 통계 출력
// TODO: 이름이 "Bonus"이고 가격이 5.00인 세 번째 Product 생성
// 최종 재고 통계 출력
return 0;
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.