PHpullh
심층 가이드/C++/객체지향

C++ · 심층 가이드

C++ 객체지향 완전 정리

클래스 설계 20개 예제로 생성자 초기화 리스트와 가상 소멸자, 슬라이싱을 피하는 다형성, 값 객체 비교, 의존성 주입까지 C++식 객체 모델을 정리합니다.

주제 20개 · 예제 코드 포함 · 최종 수정 2026-08-30 · 작성 pullh 편집팀

C++의 객체는 기본적으로 값입니다. 참조로만 객체를 다루는 언어에서 오면 이 지점에서 모델이 어긋납니다. 대입하면 복사되고, 함수에 넘기면 또 복사되며, 스코프를 벗어나면 소멸자가 결정적으로 호출됩니다. 이 결정적 소멸이 곧 RAII의 기반이고, 그래서 C++에서는 자원 정리를 언어 기능이 아니라 클래스 설계로 해결합니다. 상속 계층을 그리기 전에 소멸자와 복사·이동 동작을 먼저 정하는 것이 순서입니다.

생성자와 초기화에서는 대입이 아니라 멤버 초기화 리스트가 기본이라는 점을, 캡슐화불변 데이터 객체에서는 const 멤버와 접근자 설계를 봅니다. 인터페이스 또는 trait는 순수 가상 함수로 표현되는데, 이것이 다형성의존성 주입으로 그대로 이어집니다. 인터페이스를 참조나 스마트 포인터로 받아 두면 저장소 객체서비스 객체가 테스트에서 교체 가능해집니다. 이 네 항목은 붙여서 읽는 편이 이해가 빠릅니다.

두 가지 함정이 반복됩니다. 첫째, 기반 클래스 포인터로 파생 객체를 delete할 때 소멸자가 가상이 아니면 정의되지 않은 동작이 됩니다. 다형적으로 쓸 기반 클래스에는 가상 소멸자를, 그렇지 않다면 상속 자체를 막는 편이 안전합니다. 둘째는 슬라이싱입니다. 파생 객체를 기반 타입 으로 받으면 파생 부분이 조용히 잘려 나가고 컴파일러는 아무 말도 하지 않습니다. 다형성이 필요한 자리에서는 값이 아니라 참조나 포인터로 받아야 합니다.

01기본 클래스 또는 구조체

C++에서 기본 클래스 또는 구조체 개념을 코드로 빠르게 확인하는 객체지향 예제입니다.

C++ code

// 기본 클래스 또는 구조체
#include <iostream>
#include <string>

struct User { std::string name; int level; };
int main() {
    User user{"Ada", 1};
    std::cout << user.name << "\n";
}
알아두면 좋은 점

객체 예제는 데이터와 동작이 어떻게 묶이는지에 집중하면 이해가 빨라집니다.

자주 하는 실수

객체 하나에 너무 많은 책임을 넣으면 구조가 빠르게 무거워집니다.

02생성자와 초기화

C++에서 생성자와 초기화 개념을 코드로 빠르게 확인하는 객체지향 예제입니다.

C++ code

// 생성자와 초기화
#include <iostream>
#include <string>

class Connection { public: static Connection local() { return Connection("local"); } const std::string& mode() const { return mode_; } private: explicit Connection(std::string mode) : mode_(std::move(mode)) {} std::string mode_; };
int main() {
    std::cout << Connection::local().mode() << "\n";
}
알아두면 좋은 점

객체 예제는 데이터와 동작이 어떻게 묶이는지에 집중하면 이해가 빨라집니다.

자주 하는 실수

객체 하나에 너무 많은 책임을 넣으면 구조가 빠르게 무거워집니다.

03인스턴스 메서드

C++에서 인스턴스 메서드 개념을 코드로 빠르게 확인하는 객체지향 예제입니다.

C++ code

// 인스턴스 메서드
#include <iostream>
#include <string>

struct User { std::string name; int level; };
int main() {
    User user{"Ada", 3};
    std::cout << user.name << "\n";
}
알아두면 좋은 점

객체 예제는 데이터와 동작이 어떻게 묶이는지에 집중하면 이해가 빨라집니다.

자주 하는 실수

객체 하나에 너무 많은 책임을 넣으면 구조가 빠르게 무거워집니다.

04정적 메서드

C++에서 정적 메서드 개념을 코드로 빠르게 확인하는 객체지향 예제입니다.

C++ code

// 정적 메서드
#include <iostream>
#include <string>

class Connection { public: static Connection local() { return Connection("local"); } const std::string& mode() const { return mode_; } private: explicit Connection(std::string mode) : mode_(std::move(mode)) {} std::string mode_; };
int main() {
    std::cout << Connection::local().mode() << "\n";
}
알아두면 좋은 점

객체 예제는 데이터와 동작이 어떻게 묶이는지에 집중하면 이해가 빨라집니다.

자주 하는 실수

객체 하나에 너무 많은 책임을 넣으면 구조가 빠르게 무거워집니다.

05상속 또는 확장

C++에서 상속 또는 확장 개념을 코드로 빠르게 확인하는 객체지향 예제입니다.

C++ code

// 상속 또는 확장
#include <iostream>
#include <string>

struct User { std::string name; int level; };
int main() {
    User user{"Ada", 5};
    std::cout << user.name << "\n";
}
알아두면 좋은 점

객체 예제는 데이터와 동작이 어떻게 묶이는지에 집중하면 이해가 빨라집니다.

자주 하는 실수

객체 하나에 너무 많은 책임을 넣으면 구조가 빠르게 무거워집니다.

06인터페이스 또는 trait

C++에서 인터페이스 또는 trait 개념을 코드로 빠르게 확인하는 객체지향 예제입니다.

C++ code

// 인터페이스 또는 trait
#include <iostream>
#include <string>

class Connection { public: static Connection local() { return Connection("local"); } const std::string& mode() const { return mode_; } private: explicit Connection(std::string mode) : mode_(std::move(mode)) {} std::string mode_; };
int main() {
    std::cout << Connection::local().mode() << "\n";
}
알아두면 좋은 점

객체 예제는 데이터와 동작이 어떻게 묶이는지에 집중하면 이해가 빨라집니다.

자주 하는 실수

객체 하나에 너무 많은 책임을 넣으면 구조가 빠르게 무거워집니다.

07캡슐화

C++에서 캡슐화 개념을 코드로 빠르게 확인하는 객체지향 예제입니다.

C++ code

// 캡슐화
#include <iostream>
#include <string>

struct User { std::string name; int level; };
int main() {
    User user{"Ada", 7};
    std::cout << user.name << "\n";
}
알아두면 좋은 점

객체 예제는 데이터와 동작이 어떻게 묶이는지에 집중하면 이해가 빨라집니다.

자주 하는 실수

객체 하나에 너무 많은 책임을 넣으면 구조가 빠르게 무거워집니다.

08합성으로 기능 묶기

C++에서 합성으로 기능 묶기 개념을 코드로 빠르게 확인하는 객체지향 예제입니다.

C++ code

// 합성으로 기능 묶기
#include <iostream>
#include <string>

class Connection { public: static Connection local() { return Connection("local"); } const std::string& mode() const { return mode_; } private: explicit Connection(std::string mode) : mode_(std::move(mode)) {} std::string mode_; };
int main() {
    std::cout << Connection::local().mode() << "\n";
}
알아두면 좋은 점

객체 예제는 데이터와 동작이 어떻게 묶이는지에 집중하면 이해가 빨라집니다.

자주 하는 실수

객체 하나에 너무 많은 책임을 넣으면 구조가 빠르게 무거워집니다.

09팩터리 메서드

C++에서 팩터리 메서드 개념을 코드로 빠르게 확인하는 객체지향 예제입니다.

C++ code

// 팩터리 메서드
#include <iostream>
#include <string>

struct User { std::string name; int level; };
int main() {
    User user{"Ada", 9};
    std::cout << user.name << "\n";
}
알아두면 좋은 점

객체 예제는 데이터와 동작이 어떻게 묶이는지에 집중하면 이해가 빨라집니다.

자주 하는 실수

객체 하나에 너무 많은 책임을 넣으면 구조가 빠르게 무거워집니다.

10열거형 상태 표현

C++에서 열거형 상태 표현 개념을 코드로 빠르게 확인하는 객체지향 예제입니다.

C++ code

// 열거형 상태 표현
#include <iostream>
#include <string>

class Connection { public: static Connection local() { return Connection("local"); } const std::string& mode() const { return mode_; } private: explicit Connection(std::string mode) : mode_(std::move(mode)) {} std::string mode_; };
int main() {
    std::cout << Connection::local().mode() << "\n";
}
알아두면 좋은 점

객체 예제는 데이터와 동작이 어떻게 묶이는지에 집중하면 이해가 빨라집니다.

자주 하는 실수

객체 하나에 너무 많은 책임을 넣으면 구조가 빠르게 무거워집니다.

11불변 데이터 객체

C++에서 불변 데이터 객체 개념을 코드로 빠르게 확인하는 객체지향 예제입니다.

C++ code

// 불변 데이터 객체
#include <iostream>
#include <string>

struct User { std::string name; int level; };
int main() {
    User user{"Ada", 11};
    std::cout << user.name << "\n";
}
알아두면 좋은 점

객체 예제는 데이터와 동작이 어떻게 묶이는지에 집중하면 이해가 빨라집니다.

자주 하는 실수

객체 하나에 너무 많은 책임을 넣으면 구조가 빠르게 무거워집니다.

12빌더 패턴

C++에서 빌더 패턴 개념을 코드로 빠르게 확인하는 객체지향 예제입니다.

C++ code

// 빌더 패턴
#include <iostream>
#include <string>

class Connection { public: static Connection local() { return Connection("local"); } const std::string& mode() const { return mode_; } private: explicit Connection(std::string mode) : mode_(std::move(mode)) {} std::string mode_; };
int main() {
    std::cout << Connection::local().mode() << "\n";
}
알아두면 좋은 점

객체 예제는 데이터와 동작이 어떻게 묶이는지에 집중하면 이해가 빨라집니다.

자주 하는 실수

객체 하나에 너무 많은 책임을 넣으면 구조가 빠르게 무거워집니다.

13다형성

C++에서 다형성 개념을 코드로 빠르게 확인하는 객체지향 예제입니다.

C++ code

// 다형성
#include <iostream>
#include <string>

struct User { std::string name; int level; };
int main() {
    User user{"Ada", 13};
    std::cout << user.name << "\n";
}
알아두면 좋은 점

객체 예제는 데이터와 동작이 어떻게 묶이는지에 집중하면 이해가 빨라집니다.

자주 하는 실수

객체 하나에 너무 많은 책임을 넣으면 구조가 빠르게 무거워집니다.

14의존성 주입

C++에서 의존성 주입 개념을 코드로 빠르게 확인하는 객체지향 예제입니다.

C++ code

// 의존성 주입
#include <iostream>
#include <string>

class Connection { public: static Connection local() { return Connection("local"); } const std::string& mode() const { return mode_; } private: explicit Connection(std::string mode) : mode_(std::move(mode)) {} std::string mode_; };
int main() {
    std::cout << Connection::local().mode() << "\n";
}
알아두면 좋은 점

객체 예제는 데이터와 동작이 어떻게 묶이는지에 집중하면 이해가 빨라집니다.

자주 하는 실수

객체 하나에 너무 많은 책임을 넣으면 구조가 빠르게 무거워집니다.

15값 객체 비교

C++에서 값 객체 비교 개념을 코드로 빠르게 확인하는 객체지향 예제입니다.

C++ code

// 값 객체 비교
#include <iostream>
#include <string>

struct User { std::string name; int level; };
int main() {
    User user{"Ada", 15};
    std::cout << user.name << "\n";
}
알아두면 좋은 점

객체 예제는 데이터와 동작이 어떻게 묶이는지에 집중하면 이해가 빨라집니다.

자주 하는 실수

객체 하나에 너무 많은 책임을 넣으면 구조가 빠르게 무거워집니다.

16저장소 객체

C++에서 저장소 객체 개념을 코드로 빠르게 확인하는 객체지향 예제입니다.

C++ code

// 저장소 객체
#include <iostream>
#include <string>

class Connection { public: static Connection local() { return Connection("local"); } const std::string& mode() const { return mode_; } private: explicit Connection(std::string mode) : mode_(std::move(mode)) {} std::string mode_; };
int main() {
    std::cout << Connection::local().mode() << "\n";
}
알아두면 좋은 점

객체 예제는 데이터와 동작이 어떻게 묶이는지에 집중하면 이해가 빨라집니다.

자주 하는 실수

객체 하나에 너무 많은 책임을 넣으면 구조가 빠르게 무거워집니다.

17서비스 객체

C++에서 서비스 객체 개념을 코드로 빠르게 확인하는 객체지향 예제입니다.

C++ code

// 서비스 객체
#include <iostream>
#include <string>

struct User { std::string name; int level; };
int main() {
    User user{"Ada", 17};
    std::cout << user.name << "\n";
}
알아두면 좋은 점

객체 예제는 데이터와 동작이 어떻게 묶이는지에 집중하면 이해가 빨라집니다.

자주 하는 실수

객체 하나에 너무 많은 책임을 넣으면 구조가 빠르게 무거워집니다.

18DTO 매핑

C++에서 DTO 매핑 개념을 코드로 빠르게 확인하는 객체지향 예제입니다.

C++ code

// DTO 매핑
#include <iostream>
#include <string>

class Connection { public: static Connection local() { return Connection("local"); } const std::string& mode() const { return mode_; } private: explicit Connection(std::string mode) : mode_(std::move(mode)) {} std::string mode_; };
int main() {
    std::cout << Connection::local().mode() << "\n";
}
알아두면 좋은 점

객체 예제는 데이터와 동작이 어떻게 묶이는지에 집중하면 이해가 빨라집니다.

자주 하는 실수

객체 하나에 너무 많은 책임을 넣으면 구조가 빠르게 무거워집니다.

19변경 불가능 업데이트

C++에서 변경 불가능 업데이트 개념을 코드로 빠르게 확인하는 객체지향 예제입니다.

C++ code

// 변경 불가능 업데이트
#include <iostream>
#include <string>

struct User { std::string name; int level; };
int main() {
    User user{"Ada", 19};
    std::cout << user.name << "\n";
}
알아두면 좋은 점

객체 예제는 데이터와 동작이 어떻게 묶이는지에 집중하면 이해가 빨라집니다.

자주 하는 실수

객체 하나에 너무 많은 책임을 넣으면 구조가 빠르게 무거워집니다.

20확장 스타일 동작

C++에서 확장 스타일 동작 개념을 코드로 빠르게 확인하는 객체지향 예제입니다.

C++ code

// 확장 스타일 동작
#include <iostream>
#include <string>

class Connection { public: static Connection local() { return Connection("local"); } const std::string& mode() const { return mode_; } private: explicit Connection(std::string mode) : mode_(std::move(mode)) {} std::string mode_; };
int main() {
    std::cout << Connection::local().mode() << "\n";
}
알아두면 좋은 점

객체 예제는 데이터와 동작이 어떻게 묶이는지에 집중하면 이해가 빨라집니다.

자주 하는 실수

객체 하나에 너무 많은 책임을 넣으면 구조가 빠르게 무거워집니다.

정리하며

  • 다형적으로 사용할 기반 클래스에는 가상 소멸자를 두고, 아니라면 상속을 막습니다
  • 파생 객체를 기반 타입 값으로 받으면 슬라이싱이 일어나므로 참조나 포인터를 씁니다
  • 멤버는 생성자 본문 대입이 아니라 초기화 리스트로 채우고 const 멤버를 적극 활용합니다
  • 순수 가상 인터페이스를 주입 받게 설계하면 저장소와 서비스를 테스트에서 교체할 수 있습니다

더 깊이 들어가고 싶다면 C++ 학습 라이브러리에서 다른 주제 가이드를 이어서 보거나, 언어 비교에서 같은 개념이 다른 언어에서 어떻게 표현되는지 확인해 보세요.