클래스와 객체
1. 클래스의 정의
C++은 C 언어에 객체지향 프로그래밍을 추가했으며, 이와 밀접한 개념이 클래스와 객체입니다.
클래스(class)는 사용자가 정의하는 사용자 정의 데이터 타입으로, 데이터와 함수를 캡슐화합니다. 클래스 내 데이터는 멤버 변수 또는 속성, 함수는 멤버 함수 또는 메서드라 합니다.
C의 구조체(struct)와 유사하지만, 클래스는 함수 캡슐화가 가능합니다. 이는 스택, 큐 등 자료구조 구현에 유리합니다.
class Rectangle {
public: // 접근 지정자
int calculateArea(int w, int h) { // 면적 계산
return w * h;
}
private:
int _width; // 너비
int _height; // 높이
};
접근 지정자는 멤버의 접근 권한을 제어합니다:
- public: 클래스 외부에서 직접 접근 가능
- private: 클래스 외부에서 접근 불가
- protected: 외부 접근 불가 (상속 시 활용)
일반적으로 멤버 변수는 private, 공개 함수는 public으로 선언합니다. C++의 struct도 클래스 역할을 하며, 차이점은:
- class: 기본 접근 권한 private
- struct: 기본 접근 권한 public
2. 객체 인스턴스 생성
클래스는 객체 생성을 위한 템플릿입니다. 객체 생성 시 인스턴스화라 하며, 이때 메모리가 할당됩니다. 클래스 정의 시 멤버 변수는 선언만 되며, 객체 생성 시 실제 메모리 공간이 할당됩니다.
3. 객체 크기 계산
객체 크기는 C 구조체의 메모리 정렬 규칙을 따릅니다. 크기 계산은 멤버 변수에만 의존하며, 멤버 함수는 포함되지 않습니다. 멤버 함수는 공용 코드 영역에 저장되어 모든 객체가 공유하기 때문입니다.
예외 사항:
- 멤버 변수가 없거나 빈 클래스: 크기 1바이트 (객체 존재 표시용)
4. 암시적 this 포인터
동일한 멤버 함수를 서로 다른 객체가 호출할 때, this 포인터로 객체를 구분합니다. 컴파일러는 멤버 함수 호출 시 첫 번째 매개변수로 현재 객체를 가리키는 this 포인터를 자동 추가합니다.
// 실제 컴파일 후 함수 형태
void init(Rectangle* const this, int w, int h);
this 포인터는 함수 내부에서 사용 가능하지만, 매개변수에 명시적으로 작성할 수 없습니다.
클래스의 기본 멤버 함수
기본 멤버 함수는 명시적 구현이 없을 때 컴파일러가 자동 생성하는 함수입니다. 주요 4가지 함수를 중점적으로 다룹니다.
1. 생성자
객체 초기화를 담당하는 특수 멤버 함수입니다.
- 이름: 클래스명과 동일, 반환형 없음
- 객체 생성 시 자동 호출
- 기본 생성자: 매개변수 없음, 전체 기본값 생성자, 컴파일러 생성자 포함
class Point {
public:
Point(int x = 0, int y = 0) : _x(x), _y(y) {}
void print() const {
cout << _x << ", " << _y;
}
private:
int _x, _y;
};
컴파일러 생성 생성자는:
- 기본 타입: 초기화하지 않음 (임의 값)
- 사용자 정의 타입: 해당 타입의 기본 생성자 호출
2. 복사 생성자
기존 객체로 새 객체를 초기화하는 생성자입니다.
- 첫 매개변수: 반드시 참조 타입 (값 전달 시 무한 재귀 발생)
- 컴파일러 생성 복사 생성자는 얕은 복사 수행
- 동적 메모리 사용 시 반드시 직접 구현
class Vector {
public:
Vector(const Vector& v) : _size(v._size) {
_data = new int[_size];
memcpy(_data, v._data, _size * sizeof(int));
}
private:
int* _data;
size_t _size;
};
3. 소멸자
객체 소멸 시 리소스 해제를 담당합니다.
- 이름: ~클래스명, 매개변수/반환형 없음
- 컴파일러 생성 소멸자는:
- 기본 타입: 처리 안 함
- 사용자 정의 타입: 해당 타입의 소멸자 호출
- 동적 메모리 사용 시 반드시 직접 구현
class Buffer {
public:
~Buffer() {
delete[] _buf; // 할당된 메모리 해제
}
private:
char* _buf;
};
4. 대입 연산자 오버로딩
기존 객체 간 값 복사를 처리합니다.
- 반드시 멤버 함수로 구현
- 컴파일러 생성 버전은 얕은 복사 수행
- 매개변수: const 참조 타입 권장
- 반환값: \*this 참조 (연속 대입 지원)
Matrix& operator=(const Matrix& m) {
if(this != &m) { // 자기 할당 방지
_rows = m._rows;
_cols = m._cols;
}
return *this;
}
5. 핵심 원칙
동적 리소스 사용 시 복사 생성자, 소멸자, 대입 연산자를 반드시 함께 구현해야 합니다.
6. const 멤버 함수와 주소 연산자
const 멤버 함수는 함수 끝에 const를 붙여 선언하며, 객체의 상태를 변경하지 않음을 보장합니다.
void display() const {
cout << _value; // 멤버 수정 불가
}
주소 연산자 오버로딩은 일반적으로 컴파일러 생성 버전을 사용합니다. 특수한 경우에만 직접 구현합니다.
// 기본 구현
Data* operator&() { return this; }
const Data* operator&() const { return this; }