cpp 기초

Cpp 기본 문법

  • C와 다른 기본사항
  • 디폴트 파라미터
  • 함수 오버로딩
  • 레퍼런스
  • iostream

cpp 확장자 : cpp 문법 기준으로 이 파일이 컴파일 됩니다.

modern은 기본적으로 알고있는게 좋아요. : 최소, modern cpp 11, 14 나머지는 회사 일하면서 익히는게 차라리 나아보여요

C와 다른 기본사항 (C++에서 새로 생긴/강화된 개념)

C++은 C를 기반으로 하지만 객체지향과 타입 시스템, 표준 라이브러리 등이 확장되어 있습니다. 초보자가 알아두면 좋은 차이점:

  • 네임스페이스(namespace)
    • 전역 이름 충돌을 피하기 위해 std:: 같은 네임스페이스를 사용합니다.
    • 예: std::cout, std::string
  • 입출력 스트림(iostream) vs C의 stdio
    • C++은 cin, cout, cerr 같은 스트림을 제공합니다. 타입 안전하고 C++ 스타일 입출력에 적합합니다.
  • 참조(Reference)
    • C에는 없는 개념으로, 포인터보다 안전하게 변수의 별칭을 만들 수 있습니다.
  • 함수 오버로딩, 디폴트 파라미터
    • 같은 이름의 함수를 여러 시그니처로 정의할 수 있습니다. 파라미터에 기본값을 줄 수 있습니다.
  • 객체지향과 생성자/소멸자, 클래스
    • 클래스, 접근 제어( public/private/protected ), 생성자/소멸자, 연산자 오버로딩 등.
  • bool 타입
    • C++에는 bool 타입이 있어 참/거짓을 명확하게 표현합니다.
  • new/delete와 RAII
    • 동적 메모리 할당에 new/delete를 사용하고, 스마트 포인터로 메모리 관리를 권장합니다.

간단 예시 (네임스페이스와 iostream):

#include <iostream>

int main() {
        std::cout << "Hello, C++!" << std::endl;
        return 0;
}

디폴트 파라미터 (Default parameters)

함수 선언에서 일부 파라미터에 기본값을 줄 수 있습니다. 호출 시 해당 인자를 생략하면 기본값이 사용됩니다.

  • 규칙
    • 기본값은 오른쪽(마지막 파라미터)부터 지정해야 함.
    • 선언(또는 헤더)에만 기본값을 주고 정의에서는 생략하는 관습이 일반적입니다.

예시:

#include <iostream>

void greet(const std::string& name, const std::string& title = "님") {
        std::cout << "안녕하세요, " << name << title << "!" << std::endl;
}

int main() {
        greet("철수");            // "안녕하세요, 철수님!"
        greet("영희", " 선생님"); // "안녕하세요, 영희 선생님!"
}

기본값을 사용하면 함수 호출이 간결해지지만, 모호성이나 유지보수 문제를 만들 수 있으니 적절히 사용하세요.


함수 오버로딩 (Function overloading)

같은 함수 이름으로 인자 타입이나 개수, cv-qualifier, ref-qualifier 등을 달리한 여러 함수를 정의해 하나의 이름으로 다양한 동작을 제공하는 C++ 기능입니다. 컴파일러는 호출 시 전달된 인자를 바탕으로 가장 적합한(최적 매칭) 오버로드를 선택합니다.

핵심 개념

  • 오버로딩은 이름은 같지만 서로 다른 시그니처(매개변수 목록)를 가진 함수들로 구성됩니다.
  • 반환 타입만 다른 것은 오버로드가 되지 않습니다(반환형은 오버로드 결정에 참여하지 않음).
  • 멤버 함수의 경우 const, &, && 같은 멤버 한정자도 시그니처의 일부로 취급됩니다.

이름 맹글링(Name mangling)

  • C++는 오버로딩을 지원하기 때문에 컴파일러는 함수의 시그니처(매개변수 타입, 네임스페이스, 클래스 등)를 반영한 내부 심볼명을 생성합니다(이를 네이밍 맹글링이라 함). 이 덕분에 링커는 동일한 기본 이름을 가진 서로 다른 시그니처를 구분할 수 있습니다.
  • 예: 같은 이름 print라도 print(int)와 print(double)은 각기 다른 맹글링된 심볼을 가집니다.
  • extern “C”를 사용하면 맹글링을 막아 C 스타일 심볼명을 유지합니다(오버로딩 불가).
  • 디버깅/심볼 확인 도구:
    • Linux: nm c++filt로 맹글링 해제
    • Windows: dumpbin /SYMBOLS

예제: 기본 오버로딩

// 예제: 간단한 오버로드
#include <iostream>
#include <string>

void print(int x) { std::cout << "int: " << x << '\n'; }
void print(double x) { std::cout << "double: " << x << '\n'; }
void print(const std::string& s) { std::cout << "string: " << s << '\n'; }

int main() {
    print(10);         // int
    print(3.14);       // double
    print("hello");    // string (const char* -> std::string 생성자: user-defined conversion)
}

모호성 예제

void f(int);
void f(double);

f(1.0f); // float -> int (promotion+conversion?) vs float -> double (promotion) 중 어떤 게 더 좋은지 규칙에 따라 선택 또는 모호성

템플릿과 오버로딩

  • 일반적으로 non-template 오버로드가 template보다 우선됩니다(동일한 우수성인 경우).
  • 함수 템플릿의 특수화/부분적 매칭 규칙이 적용되어 더 구체적인 템플릿이 선택될 수 있습니다.
  • SFINAE는 템플릿 오버로드 선택을 세밀하게 제어할 때 유용합니다.

권장 스타일

  • 오버로드는 명확할 때만 사용하세요. 암묵 변환이 일어날 가능성이 높다면 별도 함수 이름을 고려하세요.
  • 관련 오버로드는 같은 파일/헤더에 모아 문서화하고, 필요하면 주석으로 우선순위/의도 설명을 추가하세요.
  • 템플릿 기반 오버로딩은 제약을 명확히 하여 읽기 쉬운 코드를 유지하세요(concepts 또는 enable_if).

요약

  • 함수 오버로딩은 같은 이름으로 여러 동작을 제공하는 강력한 도구입니다.
  • 컴파일러는 후보 선정 → 가능성 판정 → 변환 순위 비교 → 최종 선택의 절차로 가장 적합한 오버로드를 고릅니다.
  • 이름 맹글링은 오버로딩을 구현하는 내부 메커니즘으로, extern “C”를 사용하면 맹글링을 피할 수 있습니다.

레퍼런스 (Reference)

레퍼런스는 기존 변수의 별칭(alias)입니다. 선언 후 다른 대상을 가리키도록 변경할 수 없습니다. 문법은 타입& 이름 입니다.

  • 장점: 포인터보다 문법이 간단하고 널(null) 상태를 가질 수 없음(따라서 안전).
  • 주로 함수 인자의 전달(복사 방지, 수정 허용)에 사용됩니다. const 레퍼런스로 읽기 전용 참조 전달도 가능.

예시:

#include <iostream>

void increment(int& x) { // x는 원래 변수의 별칭
    x += 1;
}

void printConst(const int& x) { // 복사 없이 읽기 전용 접근
    std::cout << x << std::endl;
}

int main() {
    int a = 5;
    increment(a);    // a는 6이 됨
    printConst(a);   // 6
}

iostream (입출력)

C++ 기본 입출력은 헤더의 스트림을 사용합니다.

  • 주요 객체
    • std::cout: 표준 출력
    • std::cin: 표준 입력
    • std::cerr: 표준 에러(버퍼링 없음)
    • std::clog: 표준 로그(버퍼링 있음)
  • 기본 사용법
#include <iostream>
#include <string>

int main() {
        std::string name;
        int age;

        std::cout << "이름: ";
        std::getline(std::cin, name); // 공백 포함 입력
        std::cout << "나이: ";
        std::cin >> age;              // 공백 기준 토큰 입력

        std::cout << "이름: " << name << ", 나이: " << age << std::endl;
}
  • 포맷 제어
    • std::endl: 줄바꿈 후 버퍼 플러시
    • std::setw, std::setprecision 등은 에서 제공
#include <iostream>
#include <iomanip>

double pi = 3.14159265;
std::cout << std::fixed << std::setprecision(3) << pi << std::endl; // 3.142
  • 버퍼링 / 플러시
    • std::endl은 줄바꿈과 동시에 버퍼를 비우므로 자주 쓰면 성능에 영향.
    • 필요 시 std::flush 사용.

nullptr

nullptr는 C++11부터 도입된 널 포인터 리터럴입니다. 기존의 NULL(보통 0으로 정의되던 것)보다 타입이 명확하고 안전합니다.

  • 특징
    • nullptr의 타입은 std::nullptr_t로, 포인터와만 비교/대입되는 것이 안전합니다.
    • 함수 오버로딩에서 포인터와 정수(0)를 구분할 수 있게 해줍니다.

예시 비교:

void f(int);
void f(char*);

f(0);        // f(int)가 호출될 수 있음 (정수 0)
f(nullptr);  // f(char*)가 호출됨 (포인터 널)

널 포인터 확인:

int* p = nullptr;
if (p == nullptr) {
        // 안전하게 널 체크
}

정리 팁:

  • 처음엔 std::cout/std::cin과 기본 문법(변수, 제어문, 함수)에 익숙해지세요.
  • 참조와 포인터의 차이, 값 전달 vs 참조 전달을 실습해보세요.
  • 디폴트 파라미터와 오버로딩은 API 설계 시 편리하지만 남용하면 혼동을 초래합니다.

클래스와 객체지향 (Class vs 객체지향 프로그래밍)

클래스는 C++의 문법적 도구일 뿐이고, 객체지향은 그 문법을 이용한 설계·구조화 방법론입니다. 즉 클래스는 데이터를 묶고 연산을 정의하는 수단이고, 객체지향은 무엇을 클래스로 묶을지·어떤 책임을 부여할지·계층과 상호작용을 어떻게 설계할지에 관한 사고방식입니다.

핵심 요점

  • 클래스: 데이터(멤버)와 동작(멤버 함수)을 하나로 묶는 문법 요소.
  • 객체지향은 문법 위의 설계 원칙들(캡슐화, 단일 책임, 응집도/결합도, 상속, 다형성 등).
  • 레벨 차이: 객체지향은 메모리 뷰, 어셈블리, 링킹 심볼 같은 로우레벨보다 한 단계 추상화된 설계 관점이다.
  • 다중 패러다임: C++는 객체지향을 지원하지만 함수형, 절차적 접근 등 다양한 스타일을 혼합해 쓸 수 있다.
  • 무엇을 클래스로 만들까: 변화가 적고 책임이 명확한 단위를 클래스(또는 타입)로 묶는 것이 좋음(단일 책임 원칙).

results matching ""

    No results matching ""