본문 바로가기
Programming Language/C++

[C++] 클래스와 객체

by spareone 2025. 4. 30.

1. 클래스와 객체

객체지향 프로그래밍에서는 클래스와 객체의 개념이 등장합니다.
객체지향 특징 중 캡슐화를 구현하기 위해 나온 개념인데, 하나의 데이터형을 추상화하기 위해 사용됩니다.

쉽게 말하면 이렇게 됩니다.

  • 클래스 : 붕어빵 틀
  • 객체 : 붕어빵

클래스라는 붕어빵 틀을 이용해, 객체라는 붕어빵을 필요한 만큼 만들어내는 것입니다.

클래스를 구성하려면, 이 데이터형이 어떤 동작을 하는지 구성하고, 이를 추상화 할 필요가 있습니다.

자동차로 예를 들어 보겠습니다.

Class Car {
속성 :
색상
배기량
속도

기능 :
시동 걸기
시동 끄기
가속하기

}

이런 식으로 클래스를 구성하게 됩니다. 이 Car 클래스를 이용해서 세단, SUV, 트럭… 등 실제 자동차(객체)를 만들게 됩니다.

이를 C++ 문법으로 작성한 코드입니다.

class Car {
int color;
int cc;
int speed;
 
void EngineOn() { }
void EngineOFf() { }
void Accelerate() { speed++; }
};
 
int main (){
Car BMW, Benz; // BMW라는 객체와 Benz라는 객체 생성
BMW.color = 1; // 에러 발생. 이유는 아래 글 내용 참고
Benz.EngineOn(); // 에러 발생. 이유는 아래 글 내용 참고
}

2. public과 private

캡슐화를 하는 이유 중 하나는 데이터를 은닉하기 위해서입니다.
즉, 클래스 밖에서 클래스의 멤버 요소들을 건들 수 없게 하는 것입니다.

public과 private 지정을 통해 클래스 밖에서 접근할 수 있는 요소들을 결정하게 됩니다. (미 지정시 모두 private – 이 때문에 위의 코드가 에러가 생깁니다.)
보통 변수를 private, 함수를 public으로 지정하며, 함수를 통해 변수의 값을 수정할 수 있도록 코드를 작성합니다. 이 함수들이 interface 역할을 하게 되는 것입니다.

class Car {
private:
int color;
int cc;
int speed;
 
public:
void EngineOn() { }
void EngineOFf() { }
void ChangeColor(int c) { color = c; }
void Accelerate() { speed++; }
};
 
int main (){
Car BMW, Benz; // BMW라는 객체와 Benz라는 객체 생성
BMW.color = 1; // 에러 발생. private에 직접 접근 불가
BMW.ChangeColor(1) // ChangeColor 함수를 통해 color 변수 값 변경
Benz.EngineOn();
}

3. 구조체와 클래스

C언어에서도 struct(구조체)를 통해 클래스 비슷하게 추상 데이터형을 작성할 수 있습니다.

struct Car {
private:
int color;
int cc;
int speed;
 
public:
void EngineOn() { }
void EngineOFf() { }
void ChangeColor(int c) { color = c; }
void Accelerate() { speed++; }
};
 
int main (){
Car BMW, Benz; // BMW라는 객체와 Benz라는 객체 생성
BMW.color = 1; // 에러 발생. private에 직접 접근 불가
BMW.ChangeColor(1) // ChangeColor 함수를 통해 color 변수 값 변경
Benz.EngineOn();
}

기능적인 측면에서는 거의 동일합니다.
다만, 클래스의 경우 private, public 미 지정시 모두 private로 간주하며, 구조체는 public으로 간주한다는 차이점이 있습니다.

4. 객체의 생성자

Car 클래스로 객체를 생성하면, color라던지… cc라던지… 기본적으로 지정되어야 하는 변수들이 존재합니다.
하지만 이 멤버들은 private이라서 접근이 불가능합니다.

함수를 하나 만들어 접근하는 방법이 있지만, 객체를 생성할 때마다 함수가 실행되어야 합니다.
일일히 작성하기에는 신경써야 할 것이 많기 때문에, 객체를 만들 때 자동으로 이 함수를 실행시킬 수 없을까? 해서 나온 것이 생성자입니다.

즉, 객체가 생성될 때 자동으로 실행되도록 하는 함수를 생성자라고 합니다.

class Car {
private:
int color;
int cc;
int speed;
 
public:
Car(int a, int b = 1000) { color = a; cc = b; } // 생성자
void EngineOn() { }
void EngineOff() { }
void ChangeColor(int c) { color = c; }
void Accelerate() { speed++; }
};
 
int main (){
Car BMW(1, 3000), Benz(2, 5000); // BMW라는 객체(1번 색, 3000cc)와 Benz(2번 색, 5000cc)라는 객체 생성
BMW.color = 1; // 에러 발생. private에 직접 접근 불가
BMW.ChangeColor(2) // ChangeColor 함수를 통해 color 변수 값 변경
Benz.EngineOn();
}

Car 생성자를 통해 color와 cc에 값이 들어갑니다. 생성자의 특징은 다음과 같습니다.

  1. 생성자 이름은 클래스 이름과 같다.
  2. 반환형이 없다.
  3. 매개 변수를 지정할 수 있다.
  4. 생성자 오버로딩이 가능하다. (여러 생성자 작성 가능)
  5. 디폴트 매개변수 지정 가능

만약 멤버 초기화를 위한 생성자를 작성하고자 한다면, 아래와 같은 코드로 작성해도 됩니다.

class Car {
private:
int color;
int cc;
int speed;
 
public:
Car(int a, int b = 1000) : color(a), cc(b) { } // 멤버 초기화 구문
void EngineOn() { }
void EngineOff() { }
void ChangeColor(int c) { color = c; }
void Accelerate() { speed++; }
};
 
int main (){
Car BMW(1, 3000), Benz(2, 5000); // BMW라는 객체(1번 색, 3000cc)와 Benz(2번 색, 5000cc)라는 객체 생성
BMW.color = 1; // 에러 발생. private에 직접 접근 불가
BMW.ChangeColor(2) // ChangeColor 함수를 통해 color 변수 값 변경
Benz.EngineOn();
}

함수 본문이 나오기 전 :이 나오며, 멤버(값)으로 작성하면 됩니다.

5. 객체의 소멸자

생성자가 있으면 소멸자도 존재할까 생각이 들 수 있는데, 존재합니다.

class Car {
private:
int color;
int cc;
int speed;
 
public:
Car(int a, int b = 1000cc) { color = a; cc = b; } // 생성자
~Car() { cout << "폐차" << '\n'; } // 소멸자
void EngineOn() { }
void EngineOff() { }
void ChangeColor(int c) { color = c; }
void Accelerate() { speed++; }
};
 
int main (){
Car BMW(1, 3000), Benz(2, 5000); // BMW라는 객체(1번 색, 3000cc)와 Benz(2번 색, 5000cc)라는 객체 생성
BMW.color = 1; // 에러 발생. private에 직접 접근 불가
BMW.ChangeColor(2) // ChangeColor 함수를 통해 color 변수 값 변경
Benz.EngineOn();
}

객체가 소멸하는 시점은 변수 소멸 시점과 동일합니다

  1. 지역변수 : 해당 지역 수행 완료 후 반환 시
  2. 전역변수 : 프로그램 종료 시

소멸자의 특징은 다음과 같습니다.

  1. 클래스 이름과 동일하나, 앞에 ‘~’ 문자가 붙는다.
  2. 반환형과 반환값이 없다.
  3. 매개 변수가 없다.
  4. 오버로딩을 할 수 없다. (하나의 소멸자만 작성 가능)

보통 소멸자의 경우 클래스 내에서 정의된 동적 할당을 해제하기 위해 사용됩니다.

class CArr {
private:
int count;
int *x;
 
public:
CArr(int a) { count = a; x = new int[count]; } // 생성자 (동적 할당 생성)
~CArr() { delete [] x; } // 소멸자 (동적 할당 해제)
void print() { for(int i = 0; i < count; i++) cout < x[i] << '\n'; }
}
};
 
int main() {
CArr arr(5);
arr.Print();
}

6. 생성자와 소멸자의 호출 순서

class Car {
private:
int color;
int cc;
int speed;
int num;
 
public:
Car(int n, int a, int b = 1000) { // 생성자
num = n; color = a; cc = b;
cout << "출고 :"; print();
}
~Car() { cout << "폐차 : "; print(); } // 소멸자
void EngineOn() { }
void EngineOff() { }
void ChangeColor(int c) { color = c; }
void Accelerate() { speed++; }
void Print() { cout << num << "번 차량" << '\n'; }
};
 
Car BMW(1, 3000, 1); // 전역으로 생성
Car Benz(2, 5000, 2); // 전역으로 생성
 
int main (){
Car BMW(3, 3000, 1); // 지역으로 생성
Car Benz(4, 5000, 2); // 지역으로 생성
}

전역 -> 지역 순으로 생성되며, 객체는 스택에 쌓이는 방식으로 동작합니다. (참고 : http://spareone.io/265)

생성은 차 번호 기준으로 1 -> 2 -> 3 -> 4 순으로 생성되며, 4 -> 3 -> 2 -> 1 순으로 소멸됩니다.

7. 디폴트 생성자와 디폴트 소멸자

생성자와 소멸자를 따로 지정하지 않으면, 컴파일러는 자동으로 디폴트 생성자와 소멸자를 추가시킵니다.

생성자를 하나라도 작성한 경우, 디폴트 생성자는 추가되지 않습니다.

8. 멤버 함수의 외부 정의

멤버 함수를 외부에서 정의할 수도 있습니다.

class Car {
private:
int color;
int cc;
int speed;
int num;
 
public:
Car(int n, int a, int b); // 생성자 프로토타입
void Print();
};
 
Car::Car(int n, int a, int b = 1000) { // 생성자 외부 정의
num = n; color = a; cc = b;
cout << "출고 :"; print();
}
 
void Car::Print() { cout << num << "번 차량" << '\n'; } // 함수 외부 정의
 
int main (){
Car BMW(3, 3000, 1);
}

함수 리턴 타입 작성 후, 클래스 이름을 적고 :: (범위 지정 연산자) 뒤에 함수 이름을 적으면 됩니다.

인라인 함수로도 정의가 가능합니다.

'Programming Language > C++' 카테고리의 다른 글

[C++] C++의 기본 (2)  (0) 2025.04.30
[C++] C++의 기본 (1)  (0) 2025.04.30
[C++] C언어 살펴보기  (0) 2025.04.30

댓글