멤버 함수와 멤버 변수
클래스를 구성하는 가장 기본적인 요소인 멤버 변수(Member Variables) 와 멤버 함수(Member Functions) 에 대해 더 깊이 있게 살펴보겠습니다.
이들은 클래스가 어떤 데이터를 가지고 있고, 어떤 행동을 할 수 있는지를 정의하며, 객체 지향 프로그래밍의 핵심적인 구성 요소입니다.
멤버 변수 (Member Variables)
멤버 변수는 클래스의 속성(Attributes) 또는 상태(State) 를 나타내는 변수입니다.
이들은 일반 변수와 동일하게 데이터 타입을 가지며, 클래스 내부에 선언됩니다.
각 객체는 클래스에 정의된 멤버 변수들을 자신만의 고유한 값으로 가집니다.
특징
- 객체마다 독립적인 값: 클래스로부터 생성된 각 객체는 자신만의 멤버 변수 복사본을 가지며, 이 값들은 서로 독립적입니다.
- 스코프: 멤버 변수는 클래스 스코프 내에서 유효하며, 클래스의 모든 멤버 함수에서 직접 접근할 수 있습니다.
- 접근 지정자:
public
,private
,protected
접근 지정자를 사용하여 외부로부터의 접근을 제어합니다. (일반적으로private
으로 선언하여 캡슐화를 유지) - 초기화: 생성자를 통해 초기화하는 것이 일반적이며, 특히 멤버 초기화 리스트를 사용하는 것이 권장됩니다.
#include <iostream>
#include <string>
class Circle {
private: // private 멤버 변수
double radius; // 원의 반지름
std::string color; // 원의 색상
public:
// 생성자 (멤버 초기화 리스트 사용)
Circle(double r, std::string c) : radius(r), color(c) {
std::cout << "Circle 객체 생성: 반지름 " << radius << ", 색상 " << color << std::endl;
}
// 멤버 함수 (게터/세터)
double getRadius() {
return radius;
}
void setRadius(double r) {
if (r >= 0) {
radius = r;
} else {
std::cerr << "오류: 반지름은 음수가 될 수 없습니다." << std::endl;
}
}
std::string getColor() {
return color;
}
void setColor(std::string c) {
color = c;
}
// 원의 넓이를 계산하는 멤버 함수
double calculateArea() {
return 3.14159 * radius * radius;
}
};
int main() {
Circle c1(5.0, "빨강"); // 반지름 5.0, 색상 빨강인 원
std::cout << "c1의 넓이: " << c1.calculateArea() << std::endl; // 출력: c1의 넓이: 78.53975
Circle c2(10.0, "파랑"); // 반지름 10.0, 색상 파랑인 또 다른 원
std::cout << "c2의 넓이: " << c2.calculateArea() << std::endl; // 출력: c2의 넓이: 314.159
c1.setRadius(7.0); // c1의 반지름만 변경
std::cout << "변경된 c1의 넓이: " << c1.calculateArea() << std::endl; // 출력: 변경된 c1의 넓이: 153.93791
return 0;
}
위 예시에서 radius
와 color
는 Circle
클래스의 멤버 변수입니다.
c1
과 c2
두 개의 Circle
객체는 각자 독립적인 radius
와 color
값을 가집니다.
c1
의 radius
를 변경해도 c2
의 radius
에는 영향을 미치지 않습니다.
멤버 함수 (Member Functions)
멤버 함수는 클래스의 행동(Behavior) 또는 기능(Functionality) 을 나타내는 함수입니다.
이들은 클래스 내부에 선언되며, 해당 클래스의 객체에 속한 멤버 변수에 접근하고 조작할 수 있습니다.
특징
- 객체의 상태에 접근: 멤버 함수는 해당 객체의 멤버 변수에 직접 접근하여 읽거나 변경할 수 있습니다.
this
포인터: 모든 비정적(non-static) 멤버 함수는 호출된 객체 자신을 가리키는 숨겨진 포인터this
를 매개변수로 받습니다. (뒤에서 더 자세히 다룸)- 접근 지정자:
public
,private
,protected
접근 지정자를 사용하여 외부로부터의 호출을 제어합니다. (private
멤버 변수에 접근하기 위한public
게터/세터가 일반적) - 선언과 정의: 클래스 내부에 선언(프로토타입)하고, 클래스 외부에서
클래스이름::
스코프 연산자를 사용하여 정의하는 것이 일반적입니다.
#include <iostream>
#include <string>
class Account {
private:
std::string ownerName;
double balance;
public:
Account(std::string name, double initialBalance) : ownerName(name), balance(initialBalance) {
std::cout << "계좌 생성: " << ownerName << "님의 초기 잔액 " << balance << std::endl;
}
// 입금 기능 (멤버 함수)
void deposit(double amount) {
if (amount > 0) {
balance += amount; // 멤버 함수는 private 멤버 변수 balance에 직접 접근 가능
std::cout << amount << "원 입금. 현재 잔액: " << balance << std::endl;
} else {
std::cout << "입금액은 양수여야 합니다." << std::endl;
}
}
// 출금 기능 (멤버 함수)
void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount; // private 멤버 변수 balance에 직접 접근 가능
std::cout << amount << "원 출금. 현재 잔액: " << balance << std::endl;
} else {
std::cout << "출금 실패: 유효하지 않은 금액이거나 잔액 부족. 현재 잔액: " << balance << std::endl;
}
}
// 잔액 조회 기능 (게터 멤버 함수)
double getBalance() {
return balance; // private 멤버 변수 balance 값 반환
}
// 소유자 이름 조회 기능
std::string getOwnerName() {
return ownerName;
}
};
int main() {
Account acc1("김미영", 50000.0);
acc1.deposit(10000.0); // 10000원 입금
acc1.withdraw(5000.0); // 5000원 출금
std::cout << acc1.getOwnerName() << "님의 최종 잔액: " << acc1.getBalance() << std::endl; // 잔액 확인
Account acc2("박지훈", 10000.0);
acc2.withdraw(12000.0); // 잔액 부족으로 출금 실패
std::cout << acc2.getOwnerName() << "님의 최종 잔액: " << acc2.getBalance() << std::endl;
return 0;
}
deposit()
, withdraw()
, getBalance()
는 Account
클래스의 멤버 함수입니다.
이 함수들은 ownerName
과 balance
라는 private
멤버 변수에 직접 접근하여 계좌의 상태를 변경하거나 조회할 수 있습니다.
const
멤버 함수
멤버 함수를 선언할 때 함수 시그니처 뒤에 const
키워드를 붙일 수 있습니다.
반환타입 함수이름(매개변수) const;
const
멤버 함수는 해당 함수 내에서 객체의 멤버 변수 값을 변경하지 않겠다고 선언하는 것입니다. 즉, 객체의 상태를 읽기만 하고 수정하지 않는 함수를 const
멤버 함수로 선언해야 합니다.
특징
const
멤버 함수 내에서는const
가 아닌 멤버 변수를 변경할 수 없습니다. (컴파일 오류 발생)const
객체 또는const
참조자/포인터를 통해 호출할 수 있는 유일한 멤버 함수입니다.const
가 아닌 멤버 함수는const
객체를 통해 호출할 수 없습니다.
장점
- 안전성: 객체의 상태를 변경하지 않는 함수를 명확히 하여 코드의 안정성을 높입니다.
- 유연성:
const
객체를 함수에 인자로 전달할 때, 해당 객체의const
멤버 함수만 호출할 수 있도록 하여 제약을 줍니다.
#include <iostream>
#include <string>
class Point {
private:
int x;
int y;
public:
Point(int _x, int _y) : x(_x), y(_y) {}
// 값만 반환하는 게터 함수는 const 멤버 함수로 선언하는 것이 좋습니다.
int getX() const { // const 멤버 함수
// x = 10; // 컴파일 오류! const 함수 내에서 멤버 변수 변경 불가
return x;
}
int getY() const { // const 멤버 함수
return y;
}
// 객체의 상태를 변경하는 세터 함수는 const가 될 수 없습니다.
void setX(int _x) {
x = _x;
}
// Point 정보를 출력하는 함수 (값을 변경하지 않으므로 const가 적합)
void display() const { // const 멤버 함수
std::cout << "좌표: (" << x << ", " << y << ")" << std::endl;
}
};
// 함수 매개변수로 const 참조자를 사용하는 경우
void printPoint(const Point& p) { // p는 const 객체를 참조
p.display(); // OK: display()는 const 멤버 함수이므로 호출 가능
// p.setX(10); // 컴파일 오류: p는 const 참조자이므로 const가 아닌 setX() 호출 불가
}
int main() {
Point p1(10, 20);
p1.display(); // 출력: 좌표: (10, 20)
p1.setX(15); // OK: p1은 const 객체가 아니므로 setX() 호출 가능
p1.display(); // 출력: 좌표: (15, 20)
const Point p2(30, 40); // const 객체 생성
p2.display(); // OK: display()는 const 멤버 함수이므로 호출 가능
// p2.setX(35); // 컴파일 오류: p2는 const 객체이므로 const가 아닌 setX() 호출 불가
printPoint(p1); // p1은 const 참조자로 전달
printPoint(p2); // p2는 const 참조자로 전달
return 0;
}
const
멤버 함수는 코드의 의도를 명확히 하고, 잠재적인 버그를 줄이는 데 도움을 줍니다.
객체의 상태를 변경하지 않는 모든 멤버 함수에는 const
를 붙이는 습관을 들이는 것이 좋습니다.
정적 멤버 (Static Members) (심화)
클래스의 멤버 변수나 멤버 함수 앞에 static
키워드를 붙이면 정적 멤버(Static Member) 가 됩니다.
정적 멤버는 일반 멤버와 달리 객체에 속하지 않고, 클래스 자체에 속합니다.
- 정적 멤버 변수: 모든 객체가 공유하는 하나의 변수입니다. 객체를 생성하지 않고도
클래스이름::정적변수이름
형식으로 접근할 수 있습니다. 반드시 클래스 외부에서 한 번 정의(메모리 할당)해야 합니다. - 정적 멤버 함수: 객체와 독립적으로 호출되는 함수입니다.
클래스이름::정적함수이름()
형식으로 호출하며,this
포인터를 가지지 않으므로 일반(비정적) 멤버 변수나 멤버 함수에 직접 접근할 수 없습니다.
정적 멤버는 객체에 독립적인 정보를 저장하거나, 객체를 생성하지 않고도 특정 유틸리티 기능을 제공하고 싶을 때 유용합니다.
자세한 내용은 이후에 다루겠습니다.