C++ 초급 - 12. 최신 C++ 기능 소개 (1 - C++11: 주요 기능 소개)

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

📌 12. 최신 C++ 기능 소개

C++은 지속적인 발전을 거듭하며 새로운 기능이 추가되어 코드의 가독성을 높이고 성능을 최적화하는 기능들을 제공한다.
초급 과정에서는 각 버전별 핵심 기능을 간단히 이해하는 데 초점을 맞춘다.


📌 12.1 C++11: 주요 기능 소개

C++11은 C++의 가장 큰 변화 중 하나로, 타입 추론, 메모리 관리, 이동 시멘틱스 등 코드의 안전성과 효율성을 향상시키는 기능이 추가되었다.


📌 1. auto와 decltype을 활용한 타입 추론

🔹 (1) auto를 활용한 타입 자동 유추

  • 변수의 타입을 컴파일러가 자동으로 추론.
  • 타입을 명확히 알 필요가 없을 때 유용.

💡 예제: auto 사용법

#include <iostream>
#include <vector>

int main() {
    auto num = 10;       // int
    auto pi = 3.14;      // double
    auto isValid = true; // bool

    std::vector<int> v = {1, 2, 3};
    auto it = v.begin(); // std::vector<int>::iterator

    std::cout << "num: " << num << ", pi: " << pi << ", isValid: " << isValid << std::endl;
    return 0;
}

🔹 출력 결과

num: 10, pi: 3.14, isValid: 1

💡 설명

  • auto를 사용하면 변수 선언 시 타입을 직접 지정하지 않아도 됨.
  • 컨테이너의 반복자(std::vector<int>::iterator)도 auto를 사용하면 더 간결하게 표현 가능.

🔹 (2) decltype을 활용한 타입 추론

  • decltype은 주어진 표현식에서 타입을 추론하여 반환.
  • 타입을 기반으로 변수를 선언할 때 사용.

💡 예제: decltype 사용법

#include <iostream>

int main() {
    int x = 42;
    decltype(x) y = 10;  // y의 타입은 x와 동일 (int)

    std::cout << "x: " << x << ", y: " << y << std::endl;
    return 0;
}

🔹 출력 결과

x: 42, y: 10

💡 설명

  • decltype(x)는 x의 타입(int)을 추론하여 y를 선언.

📌 2. nullptr 도입으로 NULL보다 안전한 포인터 처리

🔹 (1) 기존 NULL의 문제점

  • C++에서는 NULL이 0으로 정의되어 있어, 정수와 혼동될 수 있음.
  • nullptr을 도입하여 포인터를 명확하게 표현 가능.

💡 예제: nullptr 사용법

#include <iostream>

void func(int x) {
    std::cout << "정수 함수 호출: " << x << std::endl;
}

void func(char* p) {
    std::cout << "포인터 함수 호출" << std::endl;
}

int main() {
    func(NULL);    // 정수 함수 호출 (NULL이 0으로 해석됨)
    func(nullptr); // 포인터 함수 호출 (nullptr 사용)

    return 0;
}

🔹 출력 결과

정수 함수 호출: 0
포인터 함수 호출

💡 설명

  • NULL을 전달하면 정수(int)와 포인터(char*) 함수 중 어떤 것이 호출될지 애매.
  • nullptr을 사용하면 포인터를 명확하게 구분할 수 있음.

📌 3. 스마트 포인터 (std::unique_ptr, std::shared_ptr)

🔹 (1) 기존 new / delete의 문제점

  • 수동으로 메모리를 할당(new)하고 해제(delete)해야 함.
  • 예외가 발생하면 delete를 호출하지 못해 메모리 누수가 발생할 수 있음.

🔹 (2) std::unique_ptr (소유권이 하나인 스마트 포인터)

  • 하나의 포인터만 객체를 소유하며, 복사 불가능.
  • std::move()를 사용하여 소유권 이전 가능.

💡 예제: std::unique_ptr 사용법

#include <iostream>
#include <memory> // 스마트 포인터 사용을 위한 헤더

int main() {
    std::unique_ptr<int> ptr = std::make_unique<int>(42);

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

    // std::move()를 사용하여 소유권 이전
    std::unique_ptr<int> ptr2 = std::move(ptr);
    std::cout << "이전된 값: " << *ptr2 << std::endl;

    return 0;
}

🔹 출력 결과

값: 42
이전된 값: 42

💡 설명

  • std::make_unique<int>(42)로 안전하게 동적 메모리 할당.
  • std::move(ptr)을 사용하여 소유권 이전 가능.

🔹 (3) std::shared_ptr (참조 카운트 기반 스마트 포인터)

  • 여러 개의 std::shared_ptr가 같은 객체를 공유 가능.
  • 참조 카운트(reference count)를 사용하여 마지막 shared_ptr이 삭제될 때 자동으로 메모리 해제.

💡 예제: std::shared_ptr 사용법

#include <iostream>
#include <memory>

int main() {
    std::shared_ptr<int> p1 = std::make_shared<int>(100);
    std::shared_ptr<int> p2 = p1;  // 같은 객체를 공유

    std::cout << "p1의 값: " << *p1 << ", 참조 카운트: " << p1.use_count() << std::endl;
    std::cout << "p2의 값: " << *p2 << ", 참조 카운트: " << p2.use_count() << std::endl;

    return 0;
}

🔹 출력 결과

p1의 값: 100, 참조 카운트: 2
p2의 값: 100, 참조 카운트: 2

💡 설명

  • std::shared_ptr를 사용하면 여러 개의 포인터가 같은 객체를 관리할 수 있음.
  • use_count()를 통해 참조 카운트를 확인 가능.

📌 4. 이동 시멘틱스 (move semantics)로 불필요한 복사 방지

🔹 (1) 기존의 복사 방식 문제

  • 객체를 반환할 때 값 복사가 발생하여 성능 저하.

🔹 (2) 이동 시멘틱스 (std::move())를 활용한 최적화

  • 객체를 이동하여 복사를 방지하고, 기존 리소스를 재사용.

💡 예제: 이동 생성자 활용

#include <iostream>
#include <vector>

class MyClass {
public:
    std::vector<int> data;

    MyClass(std::vector<int> d) : data(std::move(d)) {} // 이동 시멘틱스 적용
};

int main() {
    std::vector<int> v = {1, 2, 3, 4, 5};
    MyClass obj(std::move(v));  // v의 데이터가 obj로 이동됨

    std::cout << "obj의 데이터 크기: " << obj.data.size() << std::endl;
    std::cout << "이동 후 v의 크기: " << v.size() << std::endl;

    return 0;
}

🔹 출력 결과

obj의 데이터 크기: 5
이동 후 v의 크기: 0

💡 설명

  • std::move(v)를 사용하여 벡터의 데이터를 obj로 이동.
  • 이동 후 v의 크기가 0이 되어, 복사가 발생하지 않았음을 확인 가능.

📌 5. 정리

개념  설명
auto, decltype 타입을 자동으로 추론하여 변수 선언을 단순화
nullptr 기존 NULL보다 안전한 포인터 처리 방식
스마트 포인터 std::unique_ptr, std::shared_ptr을 활용한 안전한 메모리 관리
이동 시멘틱스 std::move()를 활용하여 불필요한 복사를 방지