족보형: y = 1; for (i = 5; i > 0; i--) { if (i % 2 == 1) y = y + i; else y++; cout << y; } → 6 7 10 11 12
9. 값 전달 vs 참조 전달
매개변수
원본
넘길 수 있는 것
int x
안 바뀜 (복사)
변수, 리터럴, 식
int& x
바뀜
수정 가능한 변수만 (f(5), f(a + 1), const 변수 → 컴파일 오류)
const int& x
못 바꿈
모두
int a[] (배열)
바뀜
배열
vector<int> v
안 바뀜 (복사)
next(i);만 쓰고 반환값을 버리면 i는 그대로 → while (i < 5) next(i);는 무한 반복(논리 오류). i = next(i);로.
10. 참조 초기화 규칙
11. static 지역 변수
int next_value() { static int saved = 1; int local = 1; saved += 2; local += 2; return saved + local;}cout << next_value() << ' ' << next_value(); // 6 8
int r(int n) { static int c = 0; c++; if (n == 0) return c; return r(n - 1); }cout << r(3) << ' '; cout << r(2); // 4 7 (재귀끼리도 공유)
static과 전역 변수는 초기값이 없으면 0, 일반 지역 변수는 쓰레기 값.
추적표에 시작 m / 종료 m 열을 꼭 만들 것 (족보 1번: 같은 코드에서 static 유무로 결과가 3, 5, 6, 8, 9 vs 3, 4, 4, 5, 5).
12. 스코프와 가리기
안쪽에 같은 이름을 선언하면 바깥 이름이 가려짐. 가려진 전역은 ::x.
함수는 자기가 정의된 곳에서 보이는 이름을 씀 (호출한 쪽의 지역 변수와 무관).
for (int i …)의 i는 반복문 밖에서 쓸 수 없음.
13. 재귀 값 구하기와 for / while 변환
함수
기저
점화식
값
if (x <= 0) return 5; return f(x-1) + f(x-1);
f(0) = 5
2·f(x−1)
f(4) = 80, f(n) = 5·2ⁿ
int i = x - 1; if (i <= 0) return 5; …같은 식
x ≤ 1에서 5
2·f(x−1)
f(1)=5, f(4) = 40 (한 칸 밀림)
if (x <= 0) return 1; …같은 식
f(0) = 1
2·f(x−1)
f(n) = 2ⁿ, f(4) = 16
if (x <= 0) return x + 1; return g(x-1)*3번 합;
g(0) = 1
3·g(x−1)
g(3) = 27, g(−2) = −1
위 g에서 기저를 x < 0으로
g(−1) = 0
3·g(x−1)
모두 0
int fFor(int x) { // f(x) = 2 f(x-1), f(0) = 5 int result = 5; for (int i = 1; i <= x; i++) result *= 2; return result; // x <= 0이면 반복 0번 → 5}int fWhile(int x) { int result = 5, i = 1; while (i <= x) { result *= 2; i++; } return result;}
재귀 호출 앞의 출력은 내려가며, 뒤의 출력은 올라오며(역순) 실행: p(3)이 cout << n; p(n-1); cout << n;이면 321123.
비교는 strcmp(a, b) == 0 (==는 주소 비교). strcmp("Zoo", "apple") < 0 (대문자가 앞).
대입은 strcpy(name, "Kim") (name = "Kim" 오류). 빈 문자열은 strcpy(name, "") 또는 name[0] = '\0'.
strlen = 문자 수, sizeof = 배열 크기. char a[20] = "Hello"; → 5 vs 20.
24. new / delete
new ↔ delete, new[] ↔ delete[]. 틀리면 UB.
누수: 해제 없이 주소를 잃음. 댕글링: 해제 후 사용. 해제 후 p = nullptr;.
25. 접근 O/X 표 푸는 법
class A { public: int x; A* pa; protected: int y; private: int z; }; class B : public A { public: A objA; }; class C : public B { public: C* pc; B objB; };
표현
in A
in B
in C
y
O
O
O
z
O
X
X
objA.y
X
X
X
pa->z
O
X
X
objB.y
X
X
X
pc->y
X
X
O
26. 상속 종류별 접근
기반 멤버
public 상속
protected 상속
private 상속
public
public
protected
private
protected
protected
protected
private
private
접근 불가
접근 불가
접근 불가
class D : A (지정자 생략) → private 상속. struct는 public.
private 상속하면 손자 클래스에서도 x, y 접근 불가.
27. 생성자·소멸자
생성자를 하나라도 만들면 기본 생성자는 사라진다 → P a;, P arr[3]; 오류.
순서: 기반 → 멤버(선언 순서) → 자신, 소멸은 반대. 초기화 목록에 쓴 순서는 무시.
Derived() : p2("2"), p1("1") → B P(1) P(2) D | ~D ~P(2) ~P(1) ~B
지역 객체는 생성 역순 소멸, 전역은 main 전 생성·main 후 소멸, new는 delete할 때만 소멸.
const 멤버·참조 멤버는 초기화 목록에서만 초기화.
28. 복사 생성자 vs 대입
코드
호출
T b = a;, T c(a);
복사 생성자
c = b;
대입 연산자
f(T t)에 f(a), catch (T e)
복사 생성자
f(T& t)
없음
포인터 멤버의 기본 복사 = 얕은 복사(같은 메모리 공유, 이중 해제) → 깊은 복사 생성자 작성.
29. virtual 출력 규칙
호출 (C2 객체)
일반 funA
virtual funB
C1* o2 = new C2; o2->
C1
C2
C2* o3 = new C2; o3->
C2
C2
C1* o1 = new C1; o1->
C1
C1
기반 포인터로 delete할 거면 virtual 소멸자. 없으면 ~Derived가 안 불림.
파생 클래스에 같은 이름을 선언하면 기반의 같은 이름 함수가 모두 가려짐(d.f() 오류, d.H::f()는 OK).
순수 가상 함수(= 0)가 있으면 추상 클래스 → 객체 생성 불가.
30. static 멤버, this, 연산자 오버로딩
static 데이터 멤버는 클래스 밖에서 int Player::count = 0;으로 정의. static 함수엔 this 없음 → 일반 멤버 사용 불가.
Counter& add()는 같은 객체에 연결, Counter addCopy()는 복사본 반환 → c.addCopy(1).addCopy(1)은 c에 한 번만 반영.