C++ 시작하기 - 최신 C++Welcome back to C++ - Modern C++

C++는 만들어진 이후 전 세계에서 가장 널리 사용되는 프로그래밍 언어 중 하나가 되었습니다.Since its creation, C++ has become one of the most widely used programming languages in the world. 잘 작성된 C++ 프로그램은 빠르고 효율적입니다.Well-written C++ programs are fast and efficient. C++ 언어는 다른 언어보다 유연성이 높습니다. 가장 높은 추상화 수준은 물론 낮게는 회로 설계 시에도 사용할 수 있습니다.The language is more flexible than other languages: It can work at the highest levels of abstraction, and down at the level of the silicon. C++는 고도로 최적화된 표준 라이브러리를 제공합니다.C++ supplies highly optimized standard libraries. 이를 통해 낮은 수준의 하드웨어 기능에 액세스하여 속도를 최대한으로 높이고 메모리 요구 사항을 최소화할 수 있습니다.It enables access to low-level hardware features, to maximize speed and minimize memory requirements. C++를 사용하여 만들 수 있는 앱은Using C++, you can create a wide range of apps. 게임, 디바이스 드라이버, 고성능 과학 소프트웨어,Games, device drivers, and high-performance scientific software. 기본 포함 프로그램,Embedded programs. Windows 클라이언트 앱 등 다양합니다.Windows client apps. 심지어 다른 프로그래밍 언어를 위한 라이브러리와 컴파일러도 C++로 작성됩니다.Even libraries and compilers for other programming languages get written in C++.

C++의 본래 요구 사항 중 하나는 C 언어와의 역 호환성이었습니다.One of the original requirements for C++ was backward compatibility with the C language. 따라서 C++에서는 원시 포인터, 배열, null 종료 문자열, 기타 기능을 통해 항상 C 스타일 프로그래밍이 가능했습니다.As a result, C++ has always permitted C-style programming, with raw pointers, arrays, null-terminated character strings, and other features. 이로 인해 성능이 향상될 수 있는 반면 버그 및 복잡성이 생성될 수도 있습니다.They may enable great performance, but can also spawn bugs and complexity. C++가 진화하면서 C 스타일 관용구를 사용할 필요성을 크게 줄이는 기능이 강조되었습니다.The evolution of C++ has emphasized features that greatly reduce the need to use C-style idioms. 필요하다면 이전 C 프로그래밍 기능을 사용할 수도 있지만 최신 C++ 코드에서는 그 필요성이 점점 줄어듭니다.The old C-programming facilities are there when you need them, but with modern C++ code you should need them less and less. 최신 C++ 코드는 보다 간단하고 안전하고 명쾌하면서도 속도는 가장 빠릅니다.Modern C++ code is simpler, safer, more elegant, and still as fast as ever.

다음 섹션에서는 최신 C++의 주요 기능에 대한 개요를 제공합니다.The following sections provide an overview of the main features of modern C++. 별도로 언급하지 않는 한 여기에 나열된 기능은 C++11 이상에서 사용할 수 있습니다.Unless noted otherwise, the features listed here are available in C++11 and later. Microsoft C++ 컴파일러에서는 /std 컴파일러 옵션을 설정하여 프로젝트에 사용할 표준 버전을 지정할 수 있습니다.In the Microsoft C++ compiler, you can set the /std compiler option to specify which version of the standard to use for your project.

리소스 및 스마트 포인터Resources and smart pointers

C 스타일 프로그래밍의 주요 버그 클래스 중 하나는 메모리 누수입니다.One of the major classes of bugs in C-style programming is the memory leak. 메모리 누수의 흔한 원인은 new 를 사용하여 할당된 메모리의 delete 호출 오류입니다.Leaks are often caused by a failure to call delete for memory that was allocated with new. 최신 C++는 Resource Acquisition Is Initialization(RAII) 원칙을 강조합니다.Modern C++ emphasizes the principle of resource acquisition is initialization (RAII). 개념은 간단합니다.The idea is simple. 리소스(힙 메모리, 파일 핸들, 소켓 등)는 개체에 의해 소유되어야 합니다.Resources (heap memory, file handles, sockets, and so on) should be owned by an object. 이 개체는 해당 생성자에서 새로 할당된 리소스를 만들거나 받아 해당 소멸자에서 삭제합니다.That object creates, or receives, the newly allocated resource in its constructor, and deletes it in its destructor. RAII 원칙은 소유하는 개체가 범위를 벗어나면 모든 리소스가 운영 체제에 제대로 반환되도록 보장합니다.The principle of RAII guarantees that all resources get properly returned to the operating system when the owning object goes out of scope.

RAII 원칙을 쉽게 채택할 수 있도록 C++ 표준 라이브러리는 std::unique_ptr, std::shared_ptr, std::weak_ptr의 세 가지 스마트 포인터 형식을 제공합니다.To support easy adoption of RAII principles, the C++ Standard Library provides three smart pointer types: std::unique_ptr, std::shared_ptr, and std::weak_ptr. 스마트 포인터는 소유하고 있는 메모리의 할당 및 삭제를 처리합니다.A smart pointer handles the allocation and deletion of the memory it owns. 다음 예제는 make_unique()에 대한 호출에서 힙에 할당된 배열 멤버가 있는 클래스를 보여 줍니다.The following example shows a class with an array member that is allocated on the heap in the call to make_unique(). newdelete 에 대한 호출은 unique_ptr 클래스에 의해 캡슐화됩니다.The calls to new and delete are encapsulated by the unique_ptr class. widget 개체가 범위를 벗어나면 unique_ptr 소멸자가 호출되어 배열을 위해 할당된 메모리를 해제합니다.When a widget object goes out of scope, the unique_ptr destructor will be invoked and it will release the memory that was allocated for the array.

#include <memory>
class widget
{
private:
    std::unique_ptr<int> data;
public:
    widget(const int size) { data = std::make_unique<int>(size); }
    void do_something() {}
};

void functionUsingWidget() {
    widget w(1000000);   // lifetime automatically tied to enclosing scope
                // constructs w, including the w.data gadget member
    // ...
    w.do_something();
    // ...
} // automatic destruction and deallocation for w and w.data

힙 메모리를 할당할 때는 가능하면 항상 스마트 포인터를 사용하세요.Whenever possible, use a smart pointer when allocating heap memory. new 및 delete 연산자를 명시적으로 사용해야 하는 경우 RAII 원칙을 따릅니다.If you must use the new and delete operators explicitly, follow the principle of RAII. 자세한 내용은 개체 수명 및 리소스 관리(RAII)를 참조하세요.For more information, see Object lifetime and resource management (RAII).

std::stringstd::string_viewstd::string and std::string_view

C 스타일 문자열은 버그의 또 다른 주요 원인입니다.C-style strings are another major source of bugs. std::stringstd::wstring를 사용하면 C 스타일 문자열과 관련된 거의 모든 오류를 제거할 수 있습니다.By using std::string and std::wstring, you can eliminate virtually all the errors associated with C-style strings. 또한 검색, 추가, 앞에 추가 등에서 멤버 함수의 이점을 얻을 수 있습니다.You also gain the benefit of member functions for searching, appending, prepending, and so on. 두 가지 모두 속도에 고도로 최적화되어 있습니다.Both are highly optimized for speed. 읽기 전용 액세스만 필요한 함수에 문자열을 전달하는 경우 C++17에서는 std::string_view를 사용하여 훨씬 큰 성능상 이점을 얻을 수 있습니다.When passing a string to a function that requires only read-only access, in C++17 you can use std::string_view for even greater performance benefit.

std::vector 및 기타 표준 라이브러리 컨테이너std::vector and other Standard Library containers

표준 라이브러리 컨테이너는 모두 RAII 원칙을 따르며The standard library containers all follow the principle of RAII. 요소의 안전한 탐색을 위한 반복기를 제공합니다.They provide iterators for safe traversal of elements. 또한 성능에 고도로 최적화되어 있으며, 정확성을 철저하게 테스트했습니다.And, they're highly optimized for performance and have been thoroughly tested for correctness. 이 같은 컨테이너를 사용하면 사용자 지정 데이터 구조에 유입될 수 있는 버그 또는 비효율성의 가능성을 없앨 수 있습니다.By using these containers, you eliminate the potential for bugs or inefficiencies that might be introduced in custom data structures. C++에서 원시 배열 대신 vector를 순차 컨테이너로 사용하세요.Instead of raw arrays, use vector as a sequential container in C++.

vector<string> apples;
apples.push_back("Granny Smith");

map(unordered_map 아님)을 기본 연관 컨테이너로 사용하세요.Use map (not unordered_map) as the default associative container. 중복 제거 및 다중 케이스에는 set, multimap, multiset를 사용하세요.Use set, multimap, and multiset for degenerate and multi cases.

map<string, string> apple_color;
// ...
apple_color["Granny Smith"] = "Green";

성능 최적화가 필요한 경우 다음을 사용하는 것이 좋습니다.When performance optimization is needed, consider using:

  • 예를 들어 클래스 멤버로서 포함이 중요한 경우 array 형식.The array type when embedding is important, for example, as a class member.

  • unordered_map과 같이 순서가 지정되지 않은 연관 컨테이너.Unordered associative containers such as unordered_map. 이 컨테이너에는 보다 낮은 요소당 오버헤드와 상수 시간 조회가 있지만 올바르고 효율적으로 사용하기는 더 어렵습니다.These have lower per-element overhead and constant-time lookup, but they can be harder to use correctly and efficiently.

  • 정렬 vector.Sorted vector. 자세한 내용은 알고리즘을 참조하세요.For more information, see Algorithms.

C 스타일 배열을 사용하지 마세요.Don’t use C-style arrays. 직접 데이터 액세스가 필요한 이전 API의 경우 f(vec.data(), vec.size());와 같은 접근자 메서드를 대신 사용합니다.For older APIs that need direct access to the data, use accessor methods such as f(vec.data(), vec.size()); instead. 컨테이너에 대한 자세한 내용은 C++ 표준 라이브러리 컨테이너를 참조하세요.For more information about containers, see C++ Standard Library Containers.

표준 라이브러리 알고리즘Standard Library algorithms

프로그램을 위한 사용자 지정 알고리즘 작성이 필요하다고 가정하기 전에 먼저 C++ 표준 라이브러리 알고리즘을 검토하세요.Before you assume that you need to write a custom algorithm for your program, first review the C++ Standard Library algorithms. 표준 라이브러리에는 검색, 정렬, 필터링, 무작위화 등 여러 일반적 작업을 위해 계속 늘어나는 알고리즘 모음이 포함되어 있습니다.The Standard Library contains an ever-growing assortment of algorithms for many common operations such as searching, sorting, filtering, and randomizing. 수식 라이브러리는 광범위합니다.The math library is extensive. C++17부터 많은 알고리즘의 병렬 버전이 제공됩니다.Starting in C++17, parallel versions of many algorithms are provided.

몇 가지 중요한 예는 다음과 같습니다.Here are some important examples:

  • 기본 탐색 알고리즘인 for_each(범위 기반 for 루프와 함께 사용).for_each, the default traversal algorithm (along with range-based for loops).

  • 컨테이너 요소의 not-in-place 수정을 위한 transform.transform, for not-in-place modification of container elements

  • 기본 검색 알고리즘인 find_if.find_if, the default search algorithm.

  • sort, lower_bound, 기타 기본 정렬 및 검색 알고리즘.sort, lower_bound, and the other default sorting and searching algorithms.

비교 연산자를 쓰려면 strict < 를 사용하고 가능한 경우 명명된 람다를 사용합니다.To write a comparator, use strict < and use named lambdas when you can.

auto comp = [](const widget& w1, const widget& w2)
     { return w1.weight() < w2.weight(); }

sort( v.begin(), v.end(), comp );

auto i = lower_bound( v.begin(), v.end(), comp );

명시적 형식 이름 대신 autoauto instead of explicit type names

C++11에서는 변수, 함수, 템플릿 선언에 사용할 auto 키워드가 도입되었습니다.C++11 introduced the auto keyword for use in variable, function, and template declarations. auto 가 개체의 형식을 추론하도록 컴파일러에 지시하므로 명시적으로 입력할 필요가 없습니다.auto tells the compiler to deduce the type of the object so that you don't have to type it explicitly. auto 는 추론된 형식이 중첩된 템플릿인 경우 특히 유용합니다.auto is especially useful when the deduced type is a nested template:

map<int,list<string>>::iterator i = m.begin(); // C-style
auto i = m.begin(); // modern C++

범위 기반 for 루프Range-based for loops

배열 및 컨테이너에 대한 C 스타일 반복은 인덱싱 오류가 발생하기 쉬우며 입력하기도 번거롭습니다.C-style iteration over arrays and containers is prone to indexing errors and is also tedious to type. 이러한 오류를 제거하고 코드를 더 읽기 쉽게 만들려면 표준 라이브러리 컨테이너 및 원시 배열과 함께 범위 기반 for 루프를 사용하세요.To eliminate these errors, and make your code more readable, use range-based for loops with both Standard Library containers and raw arrays. 자세한 내용은 범위 기반 for을 참조하세요.For more information, see Range-based for statement.

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> v {1,2,3};

    // C-style
    for(int i = 0; i < v.size(); ++i)
    {
        std::cout << v[i];
    }

    // Modern C++:
    for(auto& num : v)
    {
        std::cout << num;
    }
}

매크로 대신 constexprconstexpr expressions instead of macros

C와 C++의 매크로는 컴파일 전에 전처리기에 의해 처리되는 토큰입니다.Macros in C and C++ are tokens that are processed by the preprocessor before compilation. 매크로 토큰의 각 인스턴스는 파일이 컴파일되기 전에 정의된 값 또는 식으로 교체됩니다.Each instance of a macro token is replaced with its defined value or expression before the file is compiled. 매크로는 일반적으로 C 스타일 프로그래밍에서 컴파일 시간 상수 값을 정의하는 데 사용됩니다.Macros are commonly used in C-style programming to define compile-time constant values. 그러나 매크로는 오류가 발생하기 쉬우며 디버그하기 어렵습니다.However, macros are error-prone and difficult to debug. 최신 C++에서는 컴파일 시간 상수에 constexpr 변수를 사용하는 것이 좋습니다.In modern C++, you should prefer constexpr variables for compile-time constants:

#define SIZE 10 // C-style
constexpr int size = 10; // modern C++

균일한 초기화Uniform initialization

최신 C++에서는 모든 형식에 중괄호 초기화를 사용할 수 있습니다.In modern C++, you can use brace initialization for any type. 이러한 형태의 초기화는 배열, 벡터 또는 기타 컨테이너를 초기화할 때 특히 편리합니다.This form of initialization is especially convenient when initializing arrays, vectors, or other containers. 다음 예제에서는 S 인스턴스 세 개를 사용하여 v2가 초기화됩니다.In the following example, v2 is initialized with three instances of S. v3는 중괄호를 사용하여 초기화되는 S 인스턴스 세 개를 사용하여 초기화됩니다.v3 is initialized with three instances of S that are themselves initialized using braces. 컴파일러는 v3의 선언된 형식을 기반으로 각 요소의 형식을 추론합니다.The compiler infers the type of each element based on the declared type of v3.

#include <vector>

struct S
{
    std::string name;
    float num;
    S(std::string s, float f) : name(s), num(f) {}
};

int main()
{
    // C-style initialization
    std::vector<S> v;
    S s1("Norah", 2.7);
    S s2("Frank", 3.5);
    S s3("Jeri", 85.9);

    v.push_back(s1);
    v.push_back(s2);
    v.push_back(s3);

    // Modern C++:
    std::vector<S> v2 {s1, s2, s3};

    // or...
    std::vector<S> v3{ {"Norah", 2.7}, {"Frank", 3.5}, {"Jeri", 85.9} };

}

자세한 내용은 중괄호 초기화를 참조하세요.For more information, see Brace initialization.

이동 의미 체계Move semantics

최신 C++는 불필요한 메모리 복사본을 제거할 수 있도록 이동 의미 체계를 제공합니다.Modern C++ provides move semantics, which make it possible to eliminate unnecessary memory copies. 이전 버전의 C++에서는 특정 상황에서 복사본이 불가피했습니다.In earlier versions of the language, copies were unavoidable in certain situations. 이동 작업은 복사를 수행하지 않고 한 개체에서 다음 개체로 리소스 소유권을 전송합니다.A move operation transfers ownership of a resource from one object to the next without making a copy. 일부 클래스는 힙 메모리, 파일 핸들 등의 리소스를 소유합니다.Some classes own resources such as heap memory, file handles, and so on. 리소스 소유 클래스를 구현할 때 이동 생성자와 해당 이동 대입 연산자를 정의할 수 있습니다.When you implement a resource-owning class, you can define a move constructor and move assignment operator for it. 컴파일러는 복사본이 필요하지 않은 상황에서 오버로드 확인 중에 이러한 특수 멤버를 선택합니다.The compiler chooses these special members during overload resolution in situations where a copy isn't needed. 표준 라이브러리 컨테이너 형식은 개체에 대해 이동 생성자를 호출합니다(정의된 경우).The Standard Library container types invoke the move constructor on objects if one is defined. 자세한 내용은 이동 생성자 및 이동 대입 연산자(C++)를 참조하세요.For more information, see Move Constructors and Move Assignment Operators (C++).

람다 식Lambda expressions

C 스타일 프로그래밍에서는 함수 포인터를 사용하여 함수를 다른 함수에 전달할 수 있습니다.In C-style programming, a function can be passed to another function by using a function pointer. 함수 포인터는 유지 관리하고 이해하기에 불편합니다.Function pointers are inconvenient to maintain and understand. 함수 포인터가 참조하는 함수는 호출되는 지점과 멀리 떨어진 소스 코드 다른 곳에서 정의될 수 있습니다.The function they refer to may be defined elsewhere in the source code, far away from the point at which it's invoked. 또한 형식이 안전하지 않습니다.Also, they're not type-safe. 최신 C++는 operator() 연산자를 재정의하는 클래스인 함수 개체를 제공하므로 함수 개체를 함수처럼 호출할 수 있습니다.Modern C++ provides function objects, classes that override the operator() operator, which enables them to be called like a function. 함수 개체를 만드는 가장 편리한 방법은 인라인 람다 식을 사용하는 것입니다.The most convenient way to create function objects is with inline lambda expressions. 다음 예제는 람다 식을 사용하여 for_each 함수가 벡터의 각 요소에 대해 호출할 함수 개체를 전달하는 방법을 보여 줍니다.The following example shows how to use a lambda expression to pass a function object, that the for_each function will invoke on each element in the vector:

    std::vector<int> v {1,2,3,4,5};
    int x = 2;
    int y = 4;
    auto result = find_if(begin(v), end(v), [=](int i) { return i > x && i < y; });

람다 식 [=](int i) { return i > x && i < y; }는 " int 형식의 단일 인수를 취하며 이 인수가 x보다 크고 y보다 작은지 여부를 나타내는 부울을 반환하는 함수"로 해석할 수 있습니다.The lambda expression [=](int i) { return i > x && i < y; } can be read as "function that takes a single argument of type int and returns a boolean that indicates whether the argument is greater than x and less than y." 주변 컨텍스트의 xy 변수를 람다에서 사용할 수 있습니다.Notice that the variables x and y from the surrounding context can be used in the lambda. [=]는 이러한 변수가 값에 의해 캡처됨을 명시합니다. 즉, 람다 식에는 이러한 값의 고유한 복사본이 있습니다.The [=] specifies that those variables are captured by value; in other words, the lambda expression has its own copies of those values.

예외Exceptions

최신 C++에서는 오류 상태를 보고하고 처리하는 가장 좋은 방법으로 오류 코드가 아닌 예외를 강조합니다.Modern C++ emphasizes exceptions rather than error codes as the best way to report and handle error conditions. 자세한 내용은 최신 C++ 예외 및 오류 처리 모범 사례를 참조하세요.For more information, see Modern C++ best practices for exceptions and error handling.

std::atomic

스레드 간 통신 메커니즘에 C++ 표준 라이브러리 std::atomic 구조체와 관련 형식을 사용하세요.Use the C++ Standard Library std::atomic struct and related types for inter-thread communication mechanisms.

std::variant(C++17)std::variant (C++17)

C 스타일 프로그래밍에서는 일반적으로 공용 구조체를 사용하여 서로 다른 형식의 멤버가 동일한 메모리 위치를 점유할 수 있도록 함으로써 메모리를 보존합니다.Unions are commonly used in C-style programming to conserve memory by enabling members of different types to occupy the same memory location. 하지만 공용 구조체는 형식이 안전하지 않으며 프로그래밍 오류가 발생하기 쉽습니다.However, unions aren't type-safe and are prone to programming errors. C++17에서는 공용 구조체보다 강력하고 안전한 대안으로 std::variant 클래스가 도입되었습니다.C++17 introduces the std::variant class as a more robust and safe alternative to unions. std::visit 함수를 사용하면 variant 형식의 멤버에 형식이 안전한 방식으로 액세스할 수 있습니다.The std::visit function can be used to access the members of a variant type in a type-safe manner.

참조See also

C++ 언어 참조C++ Language Reference
람다 식Lambda Expressions
C++ 표준 라이브러리C++ Standard Library
Microsoft C++ 언어 규칙 테이블Microsoft C++ language conformance table