빠른 흐름
cpp
// user.h
#pragma once
#include <string>
class User {
public:
explicit User(std::string name);
void print() const;
const std::string& getName() const;
private:
std::string name_;
};cpp
// user.cpp
#include "user.h"
#include <iostream>
#include <utility>
User::User(std::string name) : name_(std::move(name)) {}
void User::print() const { std::cout << name_ << "\n"; }
const std::string& User::getName() const { return name_; }기본 흐름
헤더/소스 분리 이유 — 재컴파일 범위 최소화
C++은 파일 단위로 컴파일합니다. 구현을 헤더에 넣으면 헤더를 포함하는 모든 .cpp가 그 구현을 컴파일합니다. 구현이 바뀔 때마다 전체가 재컴파일됩니다. 선언만 헤더에 두면 구현 변경 시 해당 .cpp 파일만 재컴파일됩니다.
text
user.h ← main.cpp, server.cpp, util.cpp 모두 include
user.cpp 구현 변경 → user.cpp만 재컴파일 ✅
user.h 선언 변경 → main.cpp, server.cpp, util.cpp 모두 재컴파일 ⚠️#pragma once vs include guard
헤더를 여러 번 포함하면 동일 선언이 중복되어 컴파일 오류가 납니다. 이를 막는 두 가지 방법이 있습니다.
cpp
// 방법 1: #pragma once (간단, 거의 모든 컴파일러 지원)
#pragma once
class Foo { ... };
// 방법 2: include guard (표준, 이식성 최고)
#ifndef MY_PROJECT_FOO_H
#define MY_PROJECT_FOO_H
class Foo { ... };
#endif
// 두 방법 중 하나만 쓰면 됨 — 둘 다 쓰는 경우도 있지만 중복헤더에 넣어야 하는 것 vs 넣으면 안 되는 것
cpp
// ✅ 헤더에 넣어야 하는 것
class Foo { ... }; // 클래스 선언
void bar(int x); // 함수 선언
using MyInt = int; // 타입 별칭
template<typename T> T max(T a, T b); // 템플릿 정의 (구현도 헤더에)
inline int square(int x) { return x*x; } // inline 함수
constexpr int LIMIT = 100; // constexpr 상수
// ❌ 헤더에 넣으면 안 되는 것 — ODR 위반
int globalVar = 0; // ❌ 여러 .cpp에서 포함 시 중복 정의
void doSomething() { ... } // ❌ 함수 구현 (inline 아닌 경우)
using namespace std; // ❌ 헤더를 include한 모든 파일에 영향전방 선언 — 불필요한 include 줄이기
헤더에서 다른 타입의 포인터/참조만 사용할 때는 해당 헤더를 include하지 않고 전방 선언으로 충분합니다. 컴파일 의존성이 줄어 빌드가 빨라집니다.
cpp
// user.h — Widget의 포인터만 사용
// ❌ #include "widget.h" // Widget 전체 정의 불필요
class Widget; // ✅ 전방 선언으로 충분
class User {
Widget* widget_; // 포인터/참조는 전방 선언으로 OK
// Widget widget_; // ❌ 값 타입은 전체 정의 필요
public:
void setWidget(Widget* w);
};
// user.cpp에서 Widget을 실제로 사용할 때 include
#include "widget.h"체크포인트
| 상황 | 적합한 선택 |
|---|---|
| 중복 include 방지 | #pragma once 또는 include guard |
| 포인터/참조만 사용 | 전방 선언 (class Foo;) |
| 템플릿 구현 위치 | 헤더에 (별도 .tpp 파일 후 include도 가능) |
| inline 함수 | 헤더에 정의 (ODR 예외) |
| 전역 변수를 여러 파일에서 공유 | 헤더에 extern, 소스에서 한 번만 정의 |
주의할 점
헤더에 함수 구현을 넣으면 여러 .cpp에서 포함 시 ODR(One Definition Rule) 위반으로 링커 오류가 납니다.
cpp
// bad.h
void doWork() { // ❌ 비 inline 함수 구현을 헤더에
std::cout << "work\n";
}
// a.cpp와 b.cpp 모두 bad.h를 include하면:
// → 링커 오류: doWork이 두 번 정의됨
// ✅ 헤더에는 선언만
// good.h
void doWork();
// good.cpp
void doWork() { std::cout << "work\n"; }
// ✅ inline이면 헤더에 구현 가능
// good.h
inline void doWorkInline() { std::cout << "work\n"; }
// ❌ using namespace std를 헤더에 넣으면
// 그 헤더를 포함하는 모든 파일이 std 전체를 가져옴
// → 이름 충돌 위험, 다른 팀원에게 강제됨참고 링크
1 sources