이제는 만들고 실행하는 것에만 포커스를 맞추는게 아니라 어떻게 하면 유지보수에 용이하게 만들 수 있을까를 생각해야한다. 재사용성이 높은 코드는 수정사항이 발생했을 때 영향을 적게 받는 코드다.
클래스, 객체, 멤버함수, 멤버변수의 관계
1. 클래스 (class) = 붕어빵 틀
- 클래스는 붕어빵을 만드는 틀이다.
- 어떤 붕어빵이 만들어질지 모양, 재료, 그리고 붕어빵이 어떤 동작을 할 수 있는지 등을 정의해 놓은 설계도와 같다.
- 아직 실제로 먹을 수 있는 붕어빵은 아니다. 개념적인 틀이다.
2. 객체 (object) = 만들어진 붕어빵
- 객체는 그 붕어빵 틀로 '실제로 만들어진 붕어빵 하나하나'를 말한다.
3. 멤버 변수 = 붕어빵 속재료
- 클래스 안에 정의되어 있는 '데이터'부분
- 객체마다 다를 수 있는 고유한 상태를 나타낸다.
4. 멤버 함수 = 붕어빵이 할 수 있는 동작 (굽기, 맛보기, 포장하기)
- 클래스 안에 정의되어 있는 기능 또는 행동 부분.
- 이 기능들은 틀 안에 정의되어 있고, 만들어진 붕어빵 객체가 그 기능들을 실제로 수행하는 것.
// 1. 클래스 정의 (붕어빵 틀 만들기)
class BankAccount {
// 여기에 모든 멤버변수, 생성자, 멤버함수 정의
};
int main() {
// 2. 객체 생성 (실제 붕어빵 굽기)
BankAccount myAccount(123456, "홍길동", 1000);
// 3. 객체 사용하기 (붕어빵 먹기!)
myAccount.deposit(500);
cout << myAccount.getBalance() << endl;
}
연습문제
각 학생은 국어, 영어, 수학 이렇게 3가지 점수를 가지고 있다. 학생별 평균점수와, 최대 점수를 구하시오
class의 정의
- class는 일반적으로 멤버 함수(동작)와, 멤버 변수(데이터)로 구성됩니다.
- 멤버변수
- 클래스 내부에 선언된 변수
- 객체의 상태를 나타냄
- 보통 private으로 선언하여 직접 접근을 제한한다.
- 멤버함수
- 클래스 내부에 선언된 함수
- 객체의 행동을 나타냄
- 멤버변수를 조작하거나 객체의 기능을 구현
- 동작 : 가장 큰 점수 반환 / 과목의 평균 계산
- 데이터 : 각 과목의 점수
- 클래스 이름은 보통 첫 글자부터 대문자로 시작하는 파스칼 케이스(PascalCase)를 많이 쓴다.
- 변수 이름은 첫글자는 소문자이고 그 다음 단어부터는 대문자로 시작하는 카멜케이스(camelCase)를 많이 쓴다.
학생 클래스 정의
class Student
{
//동작 정의 (이를 멤버함수라고 한다.)
double getAvg();
int getMaxNum();
//데이터 정의(이를 멤버변수라고 한다.)
int kor[3];
int eng[3];
int math[3];
}
class의 멤버 함수를 구현하는 방법 두 가지.
1. 클래스 내부에서 멤버 함수의 본문까지 직접 정의하는 방법.
2. 클래스 내부에서 멤버 함수의 선언만 작성하고 클래스 외부에서 구현하는 방법. (이 방법을 많이 쓴다.)
멤버함수 구현 (클래스 내부)
class Student
{
//동작 정의 (이를 멤버함수라고 한다.)
double getAvg()
{
return (kor + eng + math) / 3.0;
}
int getMaxNum()
{
return max(max(kor,eng),math));
}
멤버함수 구현 (클래스 외부)
class Student
{
//동작 정의 (이를 멤버함수라고 한다.)
double getAvg();
int getMaxNum();
//데이터 정의(이를 멤버변수라고 한다.)
int kor[3];
int eng[3];
int math[3];
};
double Student::getAvg() // getAvg()는 Student 클래스에 속해 있는 멤버함수라고 알려주는 표현
{
return (kor + eng + math) / 3.0;
}
int Student::getMaxNum()
{
return max(max(kor, eng), math);
}
위에 클래스를 정의한 부분으로 헤더파일을 구현하고, 아래 부분으 소스파일을 구현할 것.
접근제어
- 클래스의 멤버 함수나 멤버 변수에 접근할 때는 객체 뒤에 멤버 접근 연산자 . 을 사용한다.
- 클래스는 접근 지정자를 사용하여 멤버의 접근 권한을 제어할 수 있다.
- 접근지정자(접근제어자) : public, private, protected
- public
- 외부에서 자유롭게 접근 가능
- 클래스 인터페이스를 정의할 때 사용
- private
- 해당 클래스 내부에서만 접근 가능
- 외부에서 직접 접근 불가
- 데이터 은닉(data hiding)을 위해 사용
- protected
- 해당 클래스와 상속받은 자식 클래스에서만 접근 가능
- 상속 관계에서 중요
- public
- 접근지정자를 따로 지정하지 않으면 기본적으로 private가 지정된다.
- private은 외부에서 . 연산자로 접근하지 못한다. 내부는 가능.
- public은 누구나 접근 가. 보통 멤버함수(getter/setter)나 외부에서 써야 하는 인터페이스를 둔다.
- 일반적으로 멤버함수는 public으로 접근하고, 멤버변수는 private으로 접근한다.
- 접근지정자(접근제어자) : public, private, protected
class Student
{
public:
//동작 정의 (이를 멤버함수라고 한다.)
double getAvg();
int getMaxNum();
private:
//데이터 정의(이를 멤버변수라고 한다.)
int kor[3];
int eng[3];
int math[3];
};
getter와 setter
- private에 있는 변수를 제어할 때는 어떻게 해야 하나 -> 이런 상황에서 사용하는 대표적인 방법이 바로 getter와 setter.
- 멤버 변수를 바꿀 때는 setter를, 값을 가져 올 때는 getter를 사용한다.
- 이렇게 하면 변수를 직접 제어하지 않고 데이터를 안전하게 다룰 수 있다.
- 변수를 외부에 노출하면 잘못 사용할 가능성이 있고, 이를 막을 수 없다.
- getter와 setter를 사용하는 주된 이유는 캡슐화를 구현하기 위함이다.
- 캡슐화(Encapsulation)
- 데이터를 보호하고 은닉
- 유효성 검사 등 데이터 제어 가능
- 내부 구현을 변경해도 외부 코드에 영향 없음
- 코드 유지보수성 향상
- 캡슐화(Encapsulation)
- getter
- private 멤버변수의 값을 읽기 위한 public 멤버함수
- 값을 읽기만 하는 함수. 부작용 없이 const로 만드는게 좋다.
- setter
- private 멤버변수의 값을 설정하기 위한 public 멤버함수
- 값 설정 시 유효성 검사 등 추가 로직 구현 가능
- 값을 바꾸는 함수. 유효성 검사(예: 음수 금지)를 넣어서 객체를 "항상 유효한 상태"로 유지한다.
class Student
{
public:
//동작 정의(이를 멤버함수라고 합니다)
double getAvg();
int getMaxScore();
void setMathScore(int math) --------- 이 아래 부분이 setter ----
{
this->math = math; // this-> '클래스 내의 math' 라는 의미
}
void setEngScore(int eng)
{
this->eng = eng;
}
void setKorScore(int kor)
{
this->kor = kor;
}
int getMathScore() { return math; } ------- 이 아래 부분이 getter ---
int getEngScore() { return eng; }
int getKorScore() { return kor; }
생성자
- 생성자는 객체를 생성할 때마다 한 번씩 자동으로 호출되는 특별한 멤버 함수.
- 객체가 제대로 작동할 수 있도록 초기 설정을 해주는 역할을 한다.
- 보통 생성자는 필요한 멤버 변수를 초기화하거나 객체가 동작할 준비를 하기 위해 사용한다.
- class 이름과 동일한 이름을 가진 함수로 정의된다.
- void나 int 같은 반환형이 없다. (값을 반환하는데 아니라 객체를 만드는 역할이기 때문)
- 일반 함수는 main함수에 적어서 호출해줘야 하는데 생성자는 객체를 만드는 순간 자동으로 호출된다.
- 만약 클래스에 아무 생성자도 만들지 않으면, C++ 컴파일러는 친절하게 기본 생성자를 자동으로 하나 만들어준다.
- 하지만 만약 하나라도 매개변수 있는 생성자를 직접 만들면, 컴파일러는 더 이상 기본 생성자를 자동으로 만들어주지 않는다.
생성자가 필요한 이유
- 객체의 멤버 변수들을 "유의미한 값"으로 초기화해주기 위함.
- 메모리는 기본적으로 '쓰레기 값'이라고 불리는 아무 값이나 막 들어가 있다.
- 만약 생성자가 없다면 객체를 만들었을 때 멤버변수들에 뭐가 들어있을지 아무도 모른다.
- 객체가 처음부터 올바른 상태로 시작하게 하려고 생성자가 꼭 필요하다.
생성자의 호출 순서
- 클래스로 만든 것을 변수로 선언한 것을 객체라고 한다.
- 변수로 선언하여 메모리에 잡히는 것을 인스턴스화 라고 한다.
- 정의된 class를 변수로 선언하면 해당 객체가 메모리에 올라간다.
- class는 설계도에 비유할 수 있으며, 객체 혹은 인스턴스는 설계도에 의해 만들어진 실제 결과물이다.
- 객체가 생성될 때, 멤버 변수를 포함해서 필요한 정보들이 메모리에 올라간다.
- 이 작업이 완료되면 생성자가 호출된다.
기본생성자
- 인자가 없는 생성자.
// 기본 생성자
Person() {
name = "Unknown";
age = 0;
}
매개변수가 있는 생성자
// 매개변수가 있는 생성자
Person(string n, int a) {
name = "n";
age = a;
}
기본매개변수가 있는 생성자
// 기본 매개변수가 있는 생성자
Person(string n = "DefaultName", int a = 18) {
name = "n";
age = a;
}
- 인자를 전달하지 않으면 기본 매개변수 값으로 들어가고, 인자를 전달하면 인자가 값으로 들어간다.
'C++ 공부' 카테고리의 다른 글
| 기본적인 Class 생성해보기 (0) | 2025.08.21 |
|---|---|
| C++언어 기초 - 4. 객체지향 프로그래밍 (0) | 2025.08.21 |
| C++에서 char 배열과 std::string의 차이점 (0) | 2025.08.21 |
| C++언어 기초 - 2. 포인터와 레퍼런스 (0) | 2025.08.19 |
| C++언어 기초 - 1. 프로그래밍 기초 (C언어와의 차이점) (2) | 2025.08.18 |