C++ 초급 - 12. 최신 C++ 기능 소개 (2 - C++14: 개선된 스마트 포인터)

2025. 2. 22. 18:07프로그래밍 언어/C++

📌 12.2 C++14: 개선된 스마트 포인터

C++14에서는 C++11의 부족한 기능을 보완하고, 기존 문법을 개선하여 더 안전하고 직관적인 코드 작성이 가능하다.
그중 가장 중요한 개선점 중 하나는 스마트 포인터(std::unique_ptr)의 생성 방식 개선이다.


📌 1. std::make_unique를 활용한 안전한 스마트 포인터 생성

🔹 (1) 기존 std::unique_ptr 생성 방식 (C++11)

C++11에서는 std::unique_ptr을 생성할 때 new를 직접 사용해야 했다.

💡 예제: C++11에서 std::unique_ptr 생성

#include <iostream>
#include <memory>  // 스마트 포인터 헤더 포함

int main() {
    std::unique_ptr<int> ptr(new int(42));  // 직접 new 사용

    std::cout << "값: " << *ptr << std::endl;  // 42

    return 0;
}

🔹 문제점

  1. 직접 new를 사용하면 예외 발생 시 메모리 누수가 발생할 가능성이 있음.
  2. 객체 생성과 스마트 포인터 초기화가 분리되어 있어, 코드가 불필요하게 복잡해짐.

🔹 (2) C++14의 std::make_unique

  • C++14에서는 std::make_unique를 도입하여 std::unique_ptr을 더 안전하게 생성.
  • 예외 안전성 (Exception Safety)을 보장하여, 객체가 안전하게 생성되도록 보장.

💡 예제: C++14에서 std::make_unique 사용

#include <iostream>
#include <memory>  // 스마트 포인터 헤더 포함

int main() {
    auto ptr = std::make_unique<int>(42);  // 안전한 스마트 포인터 생성

    std::cout << "값: " << *ptr << std::endl;  // 42

    return 0;
}

🔹 출력 결과

값: 42

🔹 장점

  1. new 키워드를 사용하지 않아 코드가 간결해짐.
  2. 예외가 발생해도 메모리 누수가 없음 (예외 안전성 보장).
  3. 스마트 포인터와 객체 생성이 한 번에 이루어져, 가독성이 좋아짐.

🔹 (3) std::make_unique로 배열 할당하기

  • std::make_unique는 배열(std::unique_ptr<T[]>)도 안전하게 생성할 수 있음.

💡 예제: std::make_unique로 배열 생성

#include <iostream>
#include <memory>

int main() {
    auto arr = std::make_unique<int[]>(5);  // 크기 5의 정수 배열 생성

    for (int i = 0; i < 5; i++) {
        arr[i] = i * 10;  // 배열 초기화
        std::cout << arr[i] << " ";
    }
    std::cout << std::endl;

    return 0;
}

🔹 출력 결과

0 10 20 30 40

💡 설명

  • C++11에서는 배열을 std::unique_ptr<int[]>로 직접 할당해야 했지만, std::make_unique<int[]>(size)로 간단히 생성 가능.
  • 메모리는 자동으로 해제되므로 delete[]가 필요 없음.

📌 2. C++11에서 부족했던 기능 보완 (기본적인 문법 개선)

🔹 (1) std::make_unique 도입의 배경

C++11에서는 std::make_shared가 존재했지만, std::make_unique가 없어서 std::unique_ptr을 사용할 때 new를 직접 사용해야 했다.
C++14에서는 이를 개선하여 std::make_unique를 제공함으로써, 예외 안전성을 높이고, 코드의 일관성을 유지할 수 있도록 했다.


🔹 (2) 기존 std::shared_ptr와의 비교

스마트 포인터 C++11 방식 C++14 방식
std::unique_ptr std::unique_ptr<int> p(new int(10)); auto p = std::make_unique<int>(10);
std::shared_ptr auto p = std::make_shared<int>(10); auto p = std::make_shared<int>(10); (변경 없음)

💡 차이점

  • C++14에서는 std::make_unique가 추가되어 std::make_shared와 일관성이 생김.
  • 더 이상 new를 직접 사용할 필요가 없음.

🔹 (3) std::make_shared와 std::make_unique의 차이점

특징  std::make_unique() std::make_shared()
포인터 타입 std::unique_ptr<T> std::shared_ptr<T>
객체 공유 불가능 (단일 소유권) 가능 (참조 카운트 관리)
메모리 관리 객체 단독 소유 여러 포인터가 공유
할당 방식 std::unique_ptr 내부에서 new 호출 객체와 참조 카운트 메모리를 한 번에 할당
메모리 오버헤드 없음 참조 카운트용 추가 메모리 사용
사용 예시 auto p = std::make_unique<int>(10); auto p = std::make_shared<int>(10);

💡 사용 기준

  • 객체를 하나의 포인터에서만 관리해야 한다면 std::make_unique 사용.
  • 객체를 여러 개의 포인터에서 공유해야 한다면 std::make_shared 사용.

🔹 (4) 스마트 포인터를 함수의 반환값으로 활용

  • C++14부터 std::make_unique를 활용하면 스마트 포인터를 함수에서 안전하게 반환할 수 있음.

💡 예제: std::make_unique를 반환하는 함수

#include <iostream>
#include <memory>

std::unique_ptr<int> createUniquePtr() {
    return std::make_unique<int>(100);  // 안전한 스마트 포인터 반환
}

int main() {
    auto ptr = createUniquePtr();
    std::cout << "반환된 값: " << *ptr << std::endl;  // 100
    return 0;
}

🔹 출력 결과

반환된 값: 100

💡 설명

  • 함수에서 std::make_unique로 생성한 스마트 포인터를 안전하게 반환 가능.
  • C++11에서는 std::move()를 사용해야 했으나, C++14에서는 불필요.

📌 3. 정리

개념  설명
C++11 문제점 std::unique_ptr를 생성할 때 new를 직접 사용해야 함
C++14 개선점 std::make_unique 도입으로 더 안전한 std::unique_ptr 생성 가능
예외 안전성 std::make_unique는 예외 발생 시 메모리 누수를 방지
배열 지원 std::make_unique<T[]>(size)로 동적 배열 생성 가능
코드 일관성 std::make_shared와 일관성을 유지하여 가독성 향상