모던 C++와 C 대안

객체 지향 프로그래밍(OOP)

C++는 클래스와 가상 함수를 직접 제공하고 C는 구조체와 함수 포인터로 유사한 관계를 표현합니다.

C++ 객체 지향 프로그래밍이란?

OOP는 상태, 불변 조건과 동작을 가진 객체를 중심으로 프로그램을 구성합니다. C++는 클래스, 접근 제어, 상속과 가상 디스패치를 제공하며 C에서는 이를 명시적으로 구현합니다.

OOP의 네 가지 핵심 원칙

  • 캡슐화는 내부 상태 접근을 제어해 불변 조건을 지킵니다.
  • 추상화는 작은 공개 인터페이스를 제공합니다.
  • 상속은 유효한 기본 클래스 계약을 특수화합니다.
  • 다형성은 가상 호출로 구체 동작을 선택합니다.

C와 C++의 표현 방식

C

C는 구조체와 함수 포인터를 조합하고 규칙과 API로 불변 조건과 소유권을 관리합니다.

C++

C++는 private 상태, 상속과 override를 사용하며 컴파일러가 검사합니다.

설계 팁: 다형 기본 클래스에는 가상 소멸자를 두고 소유하는 raw pointer를 피하며 합성을 우선하세요.

C와 C++ 코드 예제

C
실행 →
main.c
#include <stdio.h>

typedef struct Account Account;
typedef void (*MonthEnd)(Account *account);

struct Account {
    const char *owner;
    double balance;
    double interest_rate;
    MonthEnd month_end;
};

void standard_month_end(Account *account) {
    (void)account;
}

void savings_month_end(Account *account) {
    account->balance *= 1.0 + account->interest_rate;
}

void print_account(Account *account) {
    account->month_end(account);
    printf("%s: %.2f\n", account->owner, account->balance);
}

int main(void) {
    Account accounts[] = {
        {"Ada", 150.0, 0.0, standard_month_end},
        {"Lin", 200.0, 0.05, savings_month_end}
    };

    for (int i = 0; i < 2; ++i) {
        print_account(&accounts[i]);
    }
    return 0;
}
예상 출력
Ada: 150.00
Lin: 210.00
C++
실행 →
main.cpp
#include <iomanip>
#include <iostream>
#include <string>
#include <utility>
#include <vector>

class Account {
    std::string owner_;
    double balance_;

protected:
    void add_balance(double amount) { balance_ += amount; }

public:
    Account(std::string owner, double balance)
        : owner_(std::move(owner)), balance_(balance) {}

    virtual ~Account() = default;
    virtual void month_end() {}

    const std::string& owner() const { return owner_; }
    double balance() const { return balance_; }
};

class SavingsAccount final : public Account {
    double interest_rate_;

public:
    SavingsAccount(std::string owner, double balance, double rate)
        : Account(std::move(owner), balance), interest_rate_(rate) {}

    void month_end() override {
        add_balance(balance() * interest_rate_);
    }
};

int main() {
    Account standard("Ada", 150.0);
    SavingsAccount savings("Lin", 200.0, 0.05);
    std::vector<Account *> accounts{&standard, &savings};

    std::cout << std::fixed << std::setprecision(2);
    for (Account *account : accounts) {
        account->month_end();
        std::cout << account->owner() << ": "
                  << account->balance() << '\n';
    }
}
예상 출력
Ada: 150.00
Lin: 210.00

C와 C++ 비교

C 버전은 구조체에 상태와 함수 포인터를 저장합니다. C++ 버전은 잔액을 private으로 두고 공통 인터페이스를 상속해 가상 함수를 오버라이드합니다. 호출 코드는 구체 타입을 검사하지 않습니다.

C

C는 prefix, callback, macro와 context 구조를 씁니다.

C++

언어 기능이 scope와 type safety를 갖춘 추상화를 제공합니다.

연습 문제

두 버전을 실행하고 수정하여 각 언어의 보장을 관찰하세요.

  • C 메커니즘을 먼저 작성하세요.
  • 수동 cleanup을 RAII로 바꾸세요.
  • Allocation과 virtual dispatch를 확인하세요.