반응형

함수 템플릿

  • 함수 하나로 다양한 형식의 데이터를 같은 알고리즘으로 처리할 수 있다.
  • 개방 폐쇄 원칙(OCP)을 지키면서 여러가지 용도로 널리 쓸 수 있는 함수를 만드는 방법이다.
  • 매개변수의 데이터 형식을 함수를 작성하는 시점이 아닌, 사용(호출)하는 시점에 정할 수 있다.
  • 함수 템플릿이 포함된 소스 코드를 컴파일하면 해당 함수에 사용된 데이터 형식으로 함수의 실제 구현체가 만들어진다.
  • 모든 데이터 형식에 대응할 수 있는 알고리즘으로 정의해야한다. 그렇지 않으면 컴파일 오류가 발생한다.
  • 컴파일러는 함수 템플릿을 호출하는 구문을 만나면 인자로 전달한 값으로 템플릿 매개변수의 데이터 형식을 추론하고, 이 형식으로 완성된 함수를 오브젝트 코드로 만든다. 이 과정을 템플릿의 인스턴스화 라고 한다.
  • 템플릿 매개변수의 데이터 형식을 명시해 함수를 호출하면 컴파일러가 데이터 형식을 추론하지 않고 해당 형식으로 변환한 후에 곧바로 인스턴스화한다. 즉 형식 추론은 시간이 많이 소요되는 작업이기 때문에 명시적 호출을 지향한다.
  • 특정 데이터 형식만 다른 알고리즘으로 처리하게 할 수 있다. 이를 템플릿 특수화라고 한다. 함수 템플릿은 명시적 특수화, 부분 특수화 중 명시적 특수화만 사용할 수 있다.
  • 컴파일러가 함수 호출을 처리할 때는 오버로딩 해석 과정을 거치기 때문에 부분 특수화는 금지시켰다. 즉 오버로딩으로 해결이 가능하기 때문이다.

작업 예시

  • 명시적 특수화
#include <iostream>
#include <string>

using namespace std;

template <typename T>
T data_sum(T operand1, T operand2) {
  return operand1 + operand2;
}

// 템플릿 타입이 double 일 경우 실행할 함수를 재정의
template <>
double data_sum(double operand1, double operand2) {
  return  (int)operand1 + (int)operand2;
}

int main() {
  int data1 = 3, data2 = 5;
  double data3 = 4.5, data4 = 8.9;
  string data5 = "Hello, ", data6 = "World!";

  cout << "정수형 데이터 합: " << data_sum(data1, data2) << endl;
  cout << "실수형 데이터 합: " << data_sum(data3, data4) << endl;
  cout << "문자열 데이터 합: " << data_sum(data5, data6) << endl;

  return 0;
}

/*
실행 결과

정수형 데이터 합: 8
실수형 데이터 합: 12
문자열 데이터 합: Hello, World!
*/

클래스 템플릿

  • 템플릿 매개변수를 활용해 다양한 형식에 대응할 수 있는 범용 클래스를 만드는 방법이다.
  • 클래스 템플릿에서는 객체를 생성할 때 템플릿 매개변수의 형식을 명시해주어야 한다.(C++ 17 이후 컴파일러에서는 형식 추론이 가능하지만 컴파일러는 템플릿 매개변수의 형식을 완벽하게 추론해 낼 수 없기 때문에 직접 명시하는 것을 지향한다.)
  • 템플릿 매개변수의 일부만 지정할 수 있는 부분 특수화를 이용할 수 있다.
  • 클래스 템플릿의 선언과 정의를 별도의 파일로 분리하면 안되며, 한 파일에 있어야 한다. 왜냐하면 템플릿은 컴파일 시점에 인스턴스화 되기 때문에 다른 파일에 있을 경우 링크 오류가 발생한다.

작업 예시

  • 부분 특수화
#include <iostream>

using namespace std;

template <typename Type1, typename Type2>
class data_package {
public:
  data_package(Type1 first, Type2 second) : first(first), second(second) {}
  void print_out_element() {
    cout << "첫 번째 요소: " << first <<
      ", 두 번째 요소: " << second << endl;
  }
private:
  Type1 first;
  Type2 second;
};

// 부분 특수화 첫번째 매개변수는 string으로 고정하고 두번째만 템플릿 매개변수로 사용
template <typename T>
class data_package<string, T> {
public:
  data_package(string first, T second) : first(first), second(second) {}
  void print_out_element() {
    cout << first << "과 함께 입력된" <<
      ", 두 번째 요소: " << second << endl;
  }
private:
  string first;
  T second;
};
int main() {
  data_package<int, double> template_inst1(5, 10.5);
  data_package<string, int> template_inst2("문자열", 10);

  template_inst1.print_out_element();
  template_inst2.print_out_element();
}

 

반응형

'Programming Language > C++' 카테고리의 다른 글

컨테이너와 반복자  (0) 2025.11.05
용어 정리  (0) 2025.11.04
객체지향 설계 원칙  (0) 2025.11.03
다형성을 활용한 디자인 패턴  (0) 2025.11.03
프로그래밍 패러다임  (0) 2025.10.30
반응형

Solid 원칙

소프트웨어의 유지 보수와 확장성에 도움이 되는 다섯 가지 원칙이다.

단일 책임 원칙(Single Responsibility Principle : SRP)

  • 클래스는 한 가지 기능만 수행해야 하고, 한 가지 이유로만 변경해야 한다. 즉, 어떤 클래스가 A 라는 기능을 수정할 때도 변경되고, B라는 기능을 수정할 때도 변경되는 현상을 지양해야 한다는 의미이다.
  • 변경 사항이 한 클래스에 국한되는 것변경된 클래스가 다른 클래스에 영향을 주지 않는것, 이 두 가지가 핵심이다.
  • 다중 상속이 필요한 상황에서 컴포지션, 어그리게이션은 단일 책임 원칙을 지켜 수정 범위를 한 클래스에 갇히게 하는 좋은 방법이다.

개방 폐쇄 원칙(Open-Closed Principle : OCP)

  • 확장에 열려(개방)있고 수정에 닫혀(폐쇄) 있어야 한다.
  • 추상 클래스(인터페이스)를 통해 구현할 수 있다.
  • 프로그램에서 주요 기능의 흐름은 추상 클래스를 활용해 작성하고 이를 상속받아 구현하는 클래스에 따라서 세부 동작이 결정되게 한다.이러한 설계 패턴을 템플릭 메서드 패턴 이라고 한다. 즉 추상 클래스에 주요 기능을 모두 가상 함수로 선언하고 이를 상속 받는 자식(구현) 클래스에서 가상 함수를 각각 구현한다. 그리고 템플릿 함수(예시. 전역 함수이면서 파라미터로 부모 클래스를 받는 함수)에서 자식 클래스의 객체에 오버라이딩된 가상 함수를 호출해 논리의 흐름을 완성. 확장이 필요하면 자식 클래스를 추가한다.

리스코프 치환 원칙(Liskov Substitution Principle : LSP)

  • 자식 클래스는 부모 클래스를 대체할 수 있어야 한다.
  • 부모 클래스를 상속받아 구현한 자식 클래스는 부모 클래스로 업캐스팅이 가능하다.
  • 자식 클래스에서 부모 클래스의 멤버 함수를 상속받아 오버라이딩하거나 유지해야 한다.

인터페이스 분리 원칙(Interface Segregation Principle : ISP)

  • 단일 책임 원칙을 인터페이스에 적용한 것으로 생각하면 이해가 쉽다.
  • 인터페이스는 작고 섬세해야 하고, 클래스는 필요한 인터페이스만 구현해야 한다.

의존성 역전 원칙(Dependency Inversion Principle : DIP)

  • 상위 수준 모듈은 하위 수준의 모듈에 의존해서는 안되며 상위, 하위 수준 모두 추상레이어(인터페이스)에 의존해야 한다.
  • 개방 폐쇄 원칙을 적용하기 위해 사용된다.
  • 특정 클래스에 클래스가 아닌 필요한 인터페이스를 어그리게이션함으로써 인터페이스에 의존하도록 설계가 가능하다.

 

반응형

'Programming Language > C++' 카테고리의 다른 글

용어 정리  (0) 2025.11.04
템플릿  (0) 2025.11.03
다형성을 활용한 디자인 패턴  (0) 2025.11.03
프로그래밍 패러다임  (0) 2025.10.30
예외 처리  (0) 2025.10.30
반응형

전략 패턴(Strategy Pattern)

  • 하나의 행동(알고리즘)을 여러 방식으로 교체할 수 있게 하는 패턴. 즉 객체의 행위를 런타임에 바꿀 수 있도록 전략을 분리한다.
  • 같은 기능을 다른 알고리즘으로 정의하고 싶을 때 각각을 캡슐화하여 교체할 수 있게 하는 것이며, 소프트웨어의 유지 보수성을 높이고 새로운 알고리즘을 추가하기도 쉽다.

작업 예시

#include <iostream>
#include <memory>
using namespace std;

// 전략 인터페이스
struct MoveStrategy {
    virtual void Move() = 0;
    virtual ~MoveStrategy() = default;
};

// 구체적인 전략들
struct WalkMove : MoveStrategy {
    void Move() override { cout << "Walking...\n"; }
};

struct FlyMove : MoveStrategy {
    void Move() override { cout << "Flying...\n"; }
};

// Context
class Character {
    unique_ptr<MoveStrategy> strategy;
public:
    Character(unique_ptr<MoveStrategy> s) : strategy(move(s)) {}
    void Move() { strategy->Move(); }

    void SetStrategy(unique_ptr<MoveStrategy> s) {
        strategy = move(s);
    }
};

int main() {
    Character c(make_unique<WalkMove>());
    c.Move(); // Walking...

    c.SetStrategy(make_unique<FlyMove>());
    c.Move(); // Flying...
}

팩토리 패턴(Factory Pattern)

  • 객체 생성 과정을 감추고, 타입 선택을 유연하게 하는 패턴. 즉 객체를 직접 new 하지 않고, '무엇을 만들지'를 팩토리 클래스에 위임한다.
  • 객체를 일정한 규칙으로 생성할 수 있으며 이와 관련한 복잡한 로직을 캡슐화하여 시스템의 확장성을 높이고 변화에 대응하기가 쉽다.

작업 예시

#include <iostream>
#include <memory>
using namespace std;

// Product 인터페이스
struct Weapon {
    virtual void Attack() = 0;
    virtual ~Weapon() = default;
};

// 구체적인 제품들
struct Sword : Weapon {
    void Attack() override { cout << "Swing sword!\n"; }
};

struct Bow : Weapon {
    void Attack() override { cout << "Shoot arrow!\n"; }
};

// Factory
class WeaponFactory {
public:
    static unique_ptr<Weapon> CreateWeapon(const string& type) {
        if (type == "sword") return make_unique<Sword>();
        else if (type == "bow") return make_unique<Bow>();
        return nullptr;
    }
};

int main() {
    auto weapon = WeaponFactory::CreateWeapon("sword");
    weapon->Attack(); // Swing sword!
}

두가지 패턴 비교

구분  전략 패턴 팩토리 패턴
초점 “행동 교체” “객체 생성”
시점 런타임 (행동 변경) 컴파일 or 런타임 (생성 시 결정)
구성 요소 Context + Strategy Factory + Product
목적 같은 객체가 다른 행동을 가질 수 있게 어떤 객체를 만들지 결정
다형성 사용 행동(메서드) 위주 타입(객체 종류) 위주
예제 AI 이동 전략, 정렬 알고리즘 선택 무기 생성, UI 위젯 생성

 

비유 상황 전략 패턴  팩토리 패턴
게임 캐릭터 “걷기 ↔ 달리기 ↔ 날기 전략 교체” “전사 ↔ 궁수 ↔ 마법사 객체 생성”
자동차 “운전 모드 (스포츠 ↔ 에코) 변경” “세단 ↔ SUV ↔ 트럭 생성”
실제 공장 생산 방법을 바꾸는 것 어떤 제품을 생산할지 결정하는 것
반응형

'Programming Language > C++' 카테고리의 다른 글

템플릿  (0) 2025.11.03
객체지향 설계 원칙  (0) 2025.11.03
프로그래밍 패러다임  (0) 2025.10.30
예외 처리  (0) 2025.10.30
레퍼런스 변수  (0) 2025.10.30
반응형

프로그래밍 패러다임

  • 프로그램을 어떤 절차와 구조로 만들 것인지에 대한 스타일이나 접근 방법을 나타내며, 언어가 지원하는 기능, 코드의 구조, 문제 해결 접근 방식 등에 차이가 있다.
  • 종류로는 비구조적 프로그래밍, 절차적 프로그래밍, 객체지향 프로그래밍이 있다.

비구조적 프로그래밍

  • 코드를 구조화하지 않고 작성하는 방법으로 프로그래밍 하는 것이다.
  • 첫번째 줄부터 마지막 줄까지 차례대로 실행되며, 코드의 흐름을 이동하는 goto 문을 사용하는 특징이 있다.
  • 대표적인 언어로는 어셈블리어, 초창기의 포트란이 있다.

절차적 프로그래밍

  • 소스 코드를 여러 부분으로 나눠서 활용하는 패러다임으로 프로시저(함수)를 이용해 구조화하는 방식이다.
  • 코드의 논리 구조를 모듈화하여 재사용할 수 있으며, 라이브러리처럼 누군가가 만들어 놓은 기능을 사용하여 개발도 가능하다.
  • 프로그램이 수행할 기능을 프로시저라는 단위로 쪼개어 문제를 해결하는 하향식(top-down)방법이다.(하향식 방법은 도시에 행정 구역을 나누는 것으로 이해)
  • 대표적인 언어로는 C, 코볼(cobol), 포트란이 있다.

절차적 프로그래밍에 한계

  • 프로그램의 규모가 커지면 흐름 코드가 복작해지며 프로시저 (함수) 도 대폭 늘어난다. 또한 프로시저 간의 관계도 복잡해진다.(객체지향 프로그래밍에 추상화, 다형성)
  • 다층 구조를 포현할 방법이 없다.(객체지향 프로그래밍에 상속성)
  • 직접 접근하지 말아야 할 프로시저나 전역 변수에 접근을 막을 수 없다.(객체지향 프로그래밍에 캡슐화)

객체지향 프로그래밍

  • 객체라는 논리적인 개념으로 코드를 구성하고, 다양한 객체 간의 관계를 구성할 수 있다.
  • 논리적인 단위를 먼저 정의한 후에 이를 조합해 프로그램이 수행할 기능을 만드는 상향식(bottom-up)방법이다.(상향식 방법은 레고 블록을 쌓아 도시를 만드는 것으로 이해)

객체지향 프로그래밍의 특징

1. 추상화 : 현실 세계의 사물을 모델링하여 객체로 만들 때, 어떤 부류에서 불필요한 요소는 배제하고 공통된 특징만 추출하는 것

  - 장점 : 클래스로 만들면 코드의 불필요한 부분을 줄이고 범용성과 재사용성을 높일 수 있다.

 

2. 캡슐화 : 복잡한 내부 기능을 묶어 외부에서 불필요한 정보를 감추는 것(접근 지정자로 정보를 은닉화 하는 것)

  - 장점 : 프로그램의 복잡도는 낮아지고 재사용성을 높일 수 있다.

 

3. 상속성 : 다양한 특성으로 추상화되고 캡슐화한 클래스를 확장하고 변형하는 것.(부모 객체의 특성을 이어받는 것) 

  - 장점 : 상속을 통해 논리적인 포함 관계와 공통의 특징을 더 명확하게 모델링할 수 있다.

 

4. 다형성 : 자식 객체는 부모 객체의 역할을 대신할 수 있다. 상속 관계의 객체에서 같은 기능(함수)이 다르게 동작하는 특성

  - 장점 : 기능이 확장, 변경되어도 소스 코드 변경을 최소화할 수 있다.

 

반응형

'Programming Language > C++' 카테고리의 다른 글

객체지향 설계 원칙  (0) 2025.11.03
다형성을 활용한 디자인 패턴  (0) 2025.11.03
예외 처리  (0) 2025.10.30
레퍼런스 변수  (0) 2025.10.30
정적 변수와 상수 변수  (0) 2025.10.30
반응형

예외 처리

  • 예외 처리를 해두면 예기치 못한 상황에서도 유연하게 대처할 수 있다.
  • 프로그램의 안정성을 높이고 설사 오류가 발생하더라도 비정상으로 종료되지 않도록 한다.
  • catch(...) 문으로 기타 예외를 처리할 수 있다.
  • catch 문으로 받지 못하는 예외를 던질 경우 런타임 오류가 발생하며 프로그램이 강제로 종료된다.
  • 함수에서 예외가 처리되지 않는 경우 함수를 호출한 쪽으로 전달되는 현상을 스택 풀기라고 하며, 이를 이용하여 함수에 예외가 처리된다.
  • 형식
    • try : 예외가 발생할 수 있는 코드 블록을 중괄호 {}로 감싼다.
    • throw : 예외를 catch 블록으로 던진다.
    • catch : throw로 던진 예외를 받아서 처리한다.
#include <iostream>

using namespace std;

void func_throw() {
  cout << "func_throw()" << endl;
  cout << "throw -1" << endl;
  throw - 1;   // 정수 형식 예외 던지기
  cout << "after throw -1" << endl; // 실행되지 않음.
}

int main() {
  try {
    func_throw();
  }
  catch (int exec) {   // 정수 형식 예외 받기
    cout << "catch " << exec << endl;
  }

  return 0;
}

/*
출력 결과

func_throw()
throw -1
catch -1
*/

어설션(assertion)을 이용한 예외처리

  • 디버그 모드에서 오류가 생길 수 잇는 부분을 검사할 수 있는 매크로이다.
  • 예상치 못한 상황에서 프로그램 동작을 중단시키는 도구이다.
  • assert 는 디버그 모드에서만 컴파일되므로 다른 코드에 영향을 주지 않는 코드로만 작성해야된다. 예를 들어 릴리즈 모드에서 영향을 주는 코드는 작성하지 않도록 한다.

예외 처리 생략

  • 함수가 예외를 던지지 않음을 명시하면 컴파일러가 코드를 최적화하고 빠르게 실행하는 데에 도움이 된다.
  • noexcept 키워드를 사용한다.

예외 처리 생략 작업 예시

  • 설명) 함수가 예외를 던지지 않음을 명시. func 함수에서 예외를 던질 경우 컴파일 경고가 발생한다.
int func() noexcept
  • 설명) 함수가 예외를 던지는지 확인. func 함수에 예외를 던지는지 확인해 true, false를 리턴한다.
bool does_not_throw = noexcept(func());

예외 처리 실패 대응

  • 종료 처리 함수를 설정하는 set_terminate로 프로그램이 강제 종료되기 전에 특정 동작을 수행하도록 구성할 수 있다.
  • 프로그램이 종료될 때는 내부적으로 terminate라는 런타임 함수에서 프로그램을 강제로 종료시키는 abort 함수가 호출된다.
  • exit 함수 : 상태 코드 0을 전달하면 프로그램을 정상으로, -1을 전달하면 비정상으로 종료한다.
  • abort 함수 : 어떤 정리 작업도 수행하지 않은 채 프로그램을 비정상으로 즉시 종료한다.
#include <iostream>
#include <cstdlib>  // exit와 set_terminate 함수 사용을 위해 추가

using namespace std;

// 종료 처리 함수 
void myterminate() {
  cout << "myterminate called" << endl;
  exit(-1);      // 프로그램을 비정상적으로 종료
}

int main(void) {
  set_terminate(myterminate);    // 종료 처리 함수를 myterminate로 지정

  throw 1;    // 처리되지 않는 예외를 던짐

  return 0;   // 이 줄은 실행되지 않음, 위에서 예외가 던져졌기 때문
}

 

반응형

'Programming Language > C++' 카테고리의 다른 글

다형성을 활용한 디자인 패턴  (0) 2025.11.03
프로그래밍 패러다임  (0) 2025.10.30
레퍼런스 변수  (0) 2025.10.30
정적 변수와 상수 변수  (0) 2025.10.30
함수와 구조체  (0) 2025.10.30
반응형

레퍼런스 변수

  • 변수에 또 다른 이름, 별칭을 부여한다는 의미로, &(Ampersnad)기호를 변수 앞에 선언하여 변수를 만든다.
  • 메모리 주소가 아닌 원본 변수를 참조한다는 의미다.
  • 형식 : 자료형 &레퍼런스 변수 이름 = 대상 변수 이름;
  • 설명) swap 함수가 호출될 때, 레퍼런스 변수들이 선언된다. a, b 변수에 별칭(ref_a, ref_b)이 부여된다.
#include <iostream>

using namespace std;

// 매개변수를 레퍼런스 변수로 선언
void swap(int& ref_a, int& ref_b) {
  // 교환 전 a, b 값
  cout << "[swap func] before swap, ref_a: " << ref_a << "  ref_b : " << ref_b << endl;

  int temp = ref_a;
  ref_a = ref_b;
  ref_b = temp;

  // 교환 후 a, b 값
  cout << "[swap func] after swap, ref_a: " << ref_a << "  ref_b : " << ref_b << endl;
}

int main() {
  int a = 5;
  int b = 10;

  // swap 함수 호출 전 a, b 값
  cout << "[main] before swap, a: " << a << "  b: " << b << endl << endl;

  swap(a, b);

  // swap 함수 호출 후 a, b 값
  cout << endl << "[main] after swap, a: " << a << "  b: " << b << endl;

  return 0;
}

레퍼런스를 사용할 때 주의 사항

  • 레퍼런스 변수는 다른 변수에 종속적이므로 독립적으로 존재할 수 없다. 선언 후 반드시 참조할 원본 변수를 지정해야 한다.
int value = 10;
int &ref_value = value;
int &ref_value2; // 컴파일 오류 발생
  • 참조할 대상이 지정된 레퍼런스 변수가 다른 변수를 참조하지 않도록 주의가 필요하다.
    레퍼런스를 재지정할 경우 의도와 다르게 동작하는 문제가 발생 할 수 있다.
#include <iostream>

using namespace std;

int main() {
	int value = 10;
	int& ref_value = value;

	int value2 = 20;
	ref_value = value2;   // 레퍼런스 재지정, 의도와 다르게 동작

	cout << "value: " << value << endl;           // 20
	cout << "ref_value: " << ref_value << endl;   // 20

	return 0;
}
  • 레퍼런스 변수는 상수를 참조할 수 없다. 즉 const 키워드로 지정된 상수 변수나 리터럴(literal)(리터럴의 메모리 공간은 읽기만 가능하다.)은 참조할 수 없다. 따라서 해당 영역을 참조하여 변경할 수 없는 상수를 참조할 수 없다. 
int &ref_value = 10;   // 컴파일 오류 발생

레퍼런스와 포인터의 차이

  • 포인터는 강력하지만 능숙하게 다루기 어렵다.
  • 레퍼런스는 포인터를 비교적 안전하게 사용할 수 있도록 만든 도구로 생각할 수 있다.
  • 포인터는 동적 할당 메모리에 의해 해제하지 않으면 메모리 누수가 발생하지만, 레퍼런스는 메모리가 누수되지 않도록 프로그램의 안정성을 보장하는데 유용하게 사용할 수 있다. 

 

반응형

'Programming Language > C++' 카테고리의 다른 글

프로그래밍 패러다임  (0) 2025.10.30
예외 처리  (0) 2025.10.30
정적 변수와 상수 변수  (0) 2025.10.30
함수와 구조체  (0) 2025.10.30
포인터와 메모리  (0) 2025.10.29

+ Recent posts