icon
9장 : 객체 지향 프로그래밍 기초

멤버 함수와 멤버 변수

클래스를 구성하는 가장 기본적인 요소인 멤버 변수(Member Variables)멤버 함수(Member Functions) 에 대해 더 깊이 있게 살펴보겠습니다.

이들은 클래스가 어떤 데이터를 가지고 있고, 어떤 행동을 할 수 있는지를 정의하며, 객체 지향 프로그래밍의 핵심적인 구성 요소입니다.


멤버 변수 (Member Variables)

멤버 변수는 클래스의 속성(Attributes) 또는 상태(State) 를 나타내는 변수입니다.

이들은 일반 변수와 동일하게 데이터 타입을 가지며, 클래스 내부에 선언됩니다.

각 객체는 클래스에 정의된 멤버 변수들을 자신만의 고유한 값으로 가집니다.

특징

  • 객체마다 독립적인 값: 클래스로부터 생성된 각 객체는 자신만의 멤버 변수 복사본을 가지며, 이 값들은 서로 독립적입니다.
  • 스코프: 멤버 변수는 클래스 스코프 내에서 유효하며, 클래스의 모든 멤버 함수에서 직접 접근할 수 있습니다.
  • 접근 지정자: public, private, protected 접근 지정자를 사용하여 외부로부터의 접근을 제어합니다. (일반적으로 private으로 선언하여 캡슐화를 유지)
  • 초기화: 생성자를 통해 초기화하는 것이 일반적이며, 특히 멤버 초기화 리스트를 사용하는 것이 권장됩니다.
예시: Circle 클래스의 멤버 변수
#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;
}

위 예시에서 radiuscolorCircle 클래스의 멤버 변수입니다.

c1c2 두 개의 Circle 객체는 각자 독립적인 radiuscolor 값을 가집니다.

c1radius를 변경해도 c2radius에는 영향을 미치지 않습니다.


멤버 함수 (Member Functions)

멤버 함수는 클래스의 행동(Behavior) 또는 기능(Functionality) 을 나타내는 함수입니다.

이들은 클래스 내부에 선언되며, 해당 클래스의 객체에 속한 멤버 변수에 접근하고 조작할 수 있습니다.

특징

  • 객체의 상태에 접근: 멤버 함수는 해당 객체의 멤버 변수에 직접 접근하여 읽거나 변경할 수 있습니다.
  • this 포인터: 모든 비정적(non-static) 멤버 함수는 호출된 객체 자신을 가리키는 숨겨진 포인터 this를 매개변수로 받습니다. (뒤에서 더 자세히 다룸)
  • 접근 지정자: public, private, protected 접근 지정자를 사용하여 외부로부터의 호출을 제어합니다. (private 멤버 변수에 접근하기 위한 public 게터/세터가 일반적)
  • 선언과 정의: 클래스 내부에 선언(프로토타입)하고, 클래스 외부에서 클래스이름:: 스코프 연산자를 사용하여 정의하는 것이 일반적입니다.
예시: Account 클래스의 멤버 함수
#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 클래스의 멤버 함수입니다.

이 함수들은 ownerNamebalance라는 private 멤버 변수에 직접 접근하여 계좌의 상태를 변경하거나 조회할 수 있습니다.


const 멤버 함수

멤버 함수를 선언할 때 함수 시그니처 뒤에 const 키워드를 붙일 수 있습니다.

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 포인터를 가지지 않으므로 일반(비정적) 멤버 변수나 멤버 함수에 직접 접근할 수 없습니다.

정적 멤버는 객체에 독립적인 정보를 저장하거나, 객체를 생성하지 않고도 특정 유틸리티 기능을 제공하고 싶을 때 유용합니다.

자세한 내용은 이후에 다루겠습니다.