z 전문가를 위한 C++ : 1 ~ 3장 :: C++, 그래픽 기술 블로그

전체를 다 보기에는 양이 많고, 아는 부분도 많아 필요한 부분만 정리하도록 하겠습니다.

▶ if, switch 문의 이니셜라이저 (C++17)

if 문과 switch문 안에 이니셜라이저를 넣는 기능이 추가되었습니다.

if (<이니셜라이저> ; <조건문>) { <본문> }
switch (<이니셜라이저> ; <표현식>) { <본문> }

위와 같은 형태로 주어지며, if문의 조건문 혹은 switch문의 표현식 및 본문에서 이를 사용할 수 있습니다.

▶ [[fallthorugh]] (C++17)

attribute에 해당하며, switch 문에서 폴스루 구문을 발견했는데 케이스가 비어 있지 않다면, 경고 메시지를 발생시킵니다. 이를 막기 위해 [[fallthrough]] 구문을 적어 방지합니다.

switch (backgroundColor) {
  case Color::DarkBlue;
    doSomething1();
    [[fallthrough]];       // 경고 메세지 방지
  case Color::DarkBlue;
    doSomething2();
    break;
  case Color::DarkBlue;
    break;
};

▶ 현재 함수 이름

함수에는 내부적으로 __func__이라는 로컬 변수가 정의되어 있으며, 이 변수는 현재 함수의 이름을 값으로 가지고 있어 활용 가능합니다.

▶ C 스타일 배열

초기화를 할 때 initializer_list로 초기화를 하면, 배열의 크기가 컴파일러가 알아서 지정해줄 수도 있고, 배열의 크기가 정해져있다면, initializer_list에 나온 원수 수만큼 초기화를 해주고 나머지는 0으로 초기화시켜줍니다. C++17부터는 std::size로 C 스타일 배열의 크기를 알아낼 수 있습니다. 그 전에는 sizeof로 사이즈를 구한 뒤 자료형 크기로 나누어 구하였습니다.

int myArray[3] = {2}; // {2, 0, 0}과 동일
unsigned int arraySize = std::size(myArray); // 3

▶ 구조적 바인딩 (C++17)

구조적 바인딩을 이용하면 여러 개의 변수를 선언할 때 배열, 구조체, 페어 또는 튜플의 값으로 초기화할 수 있습니다. 변수 선언과 동시에 구조적 바인딩을 통해 값을 할당할 때는 변수의 타입이 아닌 반드시 auto를 사용해야합니다. 구조적 바인딩은 표현식 값 개수가 반드시 일치해야하며, 배열 뿐만 아니라 모든 멤버가 Non-static이면서도 public으로 선언된 데이터 구조라면 어떤 것도 적용할 수 있습니다.

std::array<int, 3> values = { 11, 22, 33};
auto [x, y, z] = values;

struct Point {double mX, mY, mZ;};
Point point{1.0, 2.0, 3.0};
auto [x, y, z] = point;

▶ 범위 기반 for 문

C 스타일의 루프, initializer_list 및 STL처럼 iterator를 리턴하는 std::begin(), std::end() 메서드가 정의된 모든 타입에 적용이 가능합니다.

▶ shared_ptr 배열 (C++17)

shared_ptr에 배열도 저장할 수 있지만, 배열을 저장하는 shared_ptr을 생성할 때는 make_shared<>()를 사용할 수 없고, 다음과 같이 작성해야 합니다.

shared_ptr<Employee[]> employees(new Employee[10]);

▶ 유니폼 초기화

C++11 이전에는 구조체와 클래스의 타입 변수 초기화 방법이 달랐습니다.

Struct a = {10, 10, 2.5};
Class b(10, 10, 2.5);

C++11 이후로는 타입을 초기화할 때 {...} 문법을 사용하는 유니폼 초기화를 따르도록 통일 됐습니다. 중괄호로 빈 집합 표시를 해주면 제로 초기화를 할 수도 있습니다. 유니폼 초기화를 사용하면 축소 변환을 방지할 수 있는데, 이는 float가 int로 변환 되어 초기화 되는 것을 방지시킬 수 있습니다.

void func(int i) {};

int main()
{
  int x = {3.14}; // Error!
  func({3.14});   // Error!
  func(3.14);     // OK! But, Warning;
};

 

▶이니셜라이저 리스트 초기화

이니셜라이저는 다음과 같이 두개가 존재합니다.

  • 복제 리스트 초기화 : T obj = {arg1, arg2, ...};
  • 직접 리스트 초기화 : T obj{arg1, arg2, ...};

C++17부터 auto 추론 기능과 관련하여 크게 달라 졌는데, C++17 이전에는 둘다 initializer_list<>로 처리 되었지만, 이후에는 auto는 직접 리스트 초기화에 대해 값 하나만 추론하게 되었습니다. 또한 복제 리스트 초기화에서 중괄호 안에 나온 원소는 반드시 타입이 모두 같아야만 합니다.

auto a = {11};      // initializer_list<int>
auto b = {11, 22};  // initializer_list<int>

auto a{11};         // int!!
auto b{11, 22};     // 원소가 너무 많다는 에러 발생

▶ literal pooling

Literal pooling이란 string literal은 내부적으로 메모리의 읽기 전용 영역에 저장되는데, 컴파일러는 같은 string literal이 코드에 여러 번 나오면 그 중 한 string에 대한 레퍼런스를 재사용하는 방식으로 메모리를 절약합니다. 즉 hello가 500번 나와도 메모리 공간을 딱 하나만 할당합니다. 또한 string literal은 메모리의 읽기 전용 영역에 존재하므로, const char가 n개인 배열로 타입을 지정하여 변경할 수 없도록 합니다.

▶ raw string litral

문자열 표기에 있어 "만 사용한다면 \t, \n과 같은 이스케이프 시퀀스를 사용해야기 때문에, 안의 문자를 문자 그대로 받아들이는 raw string literal이 존재합니다. 하지만 이는 안에 )"를 표기할 수 없기에, extended string literal이 존재하는데 이는 R"*( ... )*"의 형태로 *에는 최대 16문자로 표현할 문자열에 들어가 있는 문자열이 아니면 무엇이든 들어가도 괜찮으며, 고유한 구분자 시퀀스를 앞뒤에 두는 것을 통해 표현가능합니다. 밑의 예시는 *에 -를 넣은 경우 입니다. 

const char *str = R"(Is this? )";
const char *str = R"-(Is this? )-";

▶ std::string literal

표준 사용자 정의 리터럴 s를 사용하려면 using namespace std::string_literals; 또는 using namespace std;가 필요합니다.

auto string1 = "Hello Wolrd";  // const char*
auto string1 = "Hello Wolrd"s; // std::string

▶ std::string_view (C++17)

C++17 이전에는 읽기 전용 스트링을 받는 함수의 매개변수 타입을 쉽게 결정할 수 없었습니다. const char*로 지정하면, std::string에서 c_str() 혹은 data()를 이용하여 string을 const char*로 변환해서 호출해야 했습니다. 이를 해결한 것이 string_view로 string_view는 const string& 대신에 사용 가능하며, 스트링을 복사하지 않아 오버헤드도 존재하지 않습니다. 

 string과 string_view는 서로 연결/결합할 수 없습니다. 결합하려면 .data()를 통해 바꾸어 합쳐줘야 합니다. std::string은 값 전달하는 경우 이를 복사해서 오버헤드가 생기는 반면, string_view는 길이와 포인터만 가지고 있기에, 참조 전달에 가깝습니다. 이는 string, const char*, literal에 대해 모두 오버헤드 없이 만족스럽게 참조 전달을 진행할 수 있습니다.

 string_view를 사용하는 것만으로는 string이 생성되지 않기에 명시적으로 string으로 변환하거나, data를 통해 변환될 수 있도록 해주어야 합니다. string_view 또한, 리터럴 sv를 통해 string_view literal이 구현가능하며, using namespace std::string_literals; 또는 using namespace std;가 필요합니다.

 

 

'C++공부 > 그 외의 C++' 카테고리의 다른 글

전문가를 위한 C++ : 13 ~ 15장  (0) 2022.07.14
전문가를 위한 C++ : 10 ~ 12장  (0) 2022.07.13
전문가를 위한 C++ : 4 ~ 9장  (0) 2022.07.12
Optimized C++ 9 - 13장  (0) 2022.07.08
Optimized C++ : 4 ~ 8장  (0) 2022.07.07

+ Recent posts