C++을 기반으로 객체지향 프로그래밍(OOP)의 본질과 기초 문법을 학습합니다.
struct Knight {
int hp;
int attack;
int defence;
};
void HealMe(Knight& k, int value) {
k.hp += value;
}
위처럼 구조체와 함수를 따로 작성. 재사용성과 확장성이 부족함.
class Knight {
public:
void HealMe(int value) { _hp += value; }
int _hp;
int _attack;
int _defence;
};
각 객체는 자신만의 상태와 행동을 가지고 있음
| 구분 | 클래스 | 객체 |
|---|---|---|
| 개념 | 설계도 | 클래스 기반 실체 |
| 메모리 | 존재하지 않음 | 스택(또는 힙)에 할당됨 |
| 목적 | 생성 규칙 정의 | 실제 데이터 처리 및 동작 |
예시:
class Player {
public:
void Attack(); // 행동
void HealMe(int value); // 행동
int _hp; // 상태
int _attack;
int _defence;
};
객체가 만들어져야만 멤버 변수는 메모리에 실제로 할당됨
this가 전달됨this는 현재 함수가 호출된 객체 자신의 주소를 의미this->를 사용하여 명확히 구분Knight(int _hp, int _attack, int _defence) {
this->_hp = _hp; // 오른쪽은 매개변수, 왼쪽은 멤버 변수
}
내부적으로는
Knight* this로 시작하며,this->_hp식으로 작동
class Knight {
public:
Knight() {
_hp = 0;
_attack = 0;
_defence = 0;
}
};
Knight(int hp, int attack, int defence) {
this->_hp = hp;
this->_attack = attack;
this->_defence = defence;
}
생성자 오버로딩이 존재하면, 컴파일러는 기본 생성자를 자동 생성하지 않음
~ 사용~Knight() {
cout << "~Knight()" << endl;
}
주로 메모리 해제, 로그 출력 등 정리 작업에 사용됨
Knight(const Knight& other) {
this->_hp = other._hp;
this->_attack = other._attack;
this->_defence = other._defence;
}
매개변수는
const참조로 받아야 원본 수정 방지 + 성능 향상
| 항목 | struct | class |
|---|---|---|
| 기본 접근제어 | public | private |
| 사용 목적 | 데이터 묶음 | 객체지향 프로그래밍 설계 |
| 상속 여부 | 가능 | 가능 |
struct Knight1 {
int hp; // public
};
class Knight2 {
int hp; // private
};
#pragma once
class Player {
public:
void Attack();
void Die();
void HealMe(int value);
int _hp;
int _attack;
int _defence;
};
#include "Player.h"
#include <iostream>
using namespace std;
void Player::Attack() { cout << "Attack" << endl; }
void Player::Die() { cout << "Die" << endl; }
void Player::HealMe(int value) {
_hp += value;
cout << "Heal " << _hp << endl;
}
Knight k1;
k1.Attack(); // this == &k1
이번 파트의 중심은 객체 생성과 관련된 생성자 종류 및 규칙, 복사 생성자의 의미, 디버깅을 통한 메모리 확인, 그리고 객체 접근 제어와 오류 해결 전략입니다.
특히 실무에서 자주 마주치는 오류 메시지와 메모리 확인법, 그리고 클래스 설계 원칙을 통해 실전 감각까지 학습할 수 있도록 구성하였습니다.
class Knight {
public:
Knight() {
_hp = 0;
_attack = 0;
_defence = 0;
}
};
📌 이 점을 모르면 다음과 같은 오류 발생 가능:
Knight k2; // 오류 C2512: 적절한 기본 생성자가 없습니다.
해결 방법: 직접 기본 생성자를 명시해줄 것.
Knight(int hp, int attack, int defence) {
this->_hp = hp;
this->_attack = attack;
this->_defence = defence;
}
this->를 사용해 멤버 변수를 구분Knight k1(100, 10, 1); // 직접 초기화
Knight k2 = Knight(100, 10, 1); // 복사 초기화
Knight k3{100, 10, 1}; // 유니폼 초기화 (C++11 이상)
Knight(const Knight& other) {
this->_hp = other._hp;
this->_attack = other._attack;
this->_defence = other._defence;
}
const 참조로 받아야 성능 및 안정성 확보Knight k1(100, 10, 1);
Knight k2(k1); // 복사 생성자 호출
Knight k1;
// _hp, _attack, _defence 모두 초기화하지 않으면 쓰레기값
🔎 디버깅 창 예시:
_hp = -858993460
_attack = -858993460
_defence = -858993460
📌 이유: 스택에 할당된 지역 변수는 초기화되지 않으면 이전 메모리의 찌꺼기 값(쓰레기값) 을 갖게 됨
Knight() {
_hp = 0;
_attack = 0;
_defence = 0;
}
🔎 디버깅 창 예시:
Knight()
_hp = 0
_attack = 0
_defence = 0
~Knight()
→ 안전하게 초기화되었기 때문에 예측 가능한 값 출력됨
🔍 예시 이미지 분석:
_hp, _attack, _defence가 연속으로 배치됨this 포인터는 해당 주소를 참조함this->hp → *(300 + 0)
this->attack → *(300 + 4)
this->defence → *(300 + 8)
Knight: 사용할 수 있는 적절한 기본 생성자가 없습니다.
🔎 원인:
Knight k1(100, 10, 1);
Knight k2; // 오류! 기본 생성자 없음
✔ 해결:
Knight() {
_hp = 0; _attack = 0; _defence = 0;
}
| 지정자 | 설명 | 기본값 |
|---|---|---|
| public | 어디서든 접근 가능 | struct |
| private | 클래스 내부에서만 접근 가능 | class |
| protected | 파생 클래스까지 접근 가능 | - |
struct Knight1 {
int hp; // public
};
class Knight2 {
int hp; // private (기본값)
};
Knight1 k1;
k1.hp = 10; // O
Knight2 k2;
k2.hp = 10; // ❌ 오류
| 항목 | 내용 |
|---|---|
| 절차지향 vs 객체지향 | 객체지향은 상태와 행동을 묶어 코드의 응집도를 높임 |
| 클래스/객체 | 클래스는 설계도, 객체는 실체. 클래스 없이는 객체 생성 불가 |
| this 포인터 | 현재 객체 자기 자신을 가리키는 주소. 멤버 함수 내부에서 자동 전달됨 |
| 생성자 종류 | 기본 생성자, 오버로딩 생성자, 복사 생성자. 자동 생성 조건 주의 |
| 소멸자 | 객체 종료 시 자동 호출. 메모리 정리 시 유용 |
| 오류 C2512 | 기본 생성자 없을 때 발생. 명시적으로 만들어줘야 함 |
| struct vs class | 접근제어 기본값(public vs private) 차이. class는 객체지향 용도로 사용 권장 |
| 파일 분리 | .h에 선언, .cpp에 정의. #pragma once로 중복 방지 |