본문 바로가기

프로그래머/기타

오늘의 복습 | C++ 11 | lambda function | binary tree | linked list

#C++11에서 추가된 핵심 기능

  • auto
    • 자료형을 추론
    • 실제 자료형은 컴파일 하는 동안 결정됨.
    • 따라서, 반드시 auto 변수를 초기화해야 함.
  • static_assert
    • 컴파일 중에 assertion 평가
    • 컴파일러가 assert 조건이 참인지 아닌지 앎
    • 실패하면 컴팡리러는 컴파일 에러를 뱉음
  • default/delete
    • default 키워드를 사용하면, 컴파일러가 특정한 생성자, 연산자 및 소멸자를 만들어 낼 수 있음
    • 그래서, 비어 있는 생성자나 소멸자를 구체화할 필요가 없음
    • 또한 기본 생성자, 연산자 및 소멸자를 더 분명하게 표시할 수 있음
    • 컴파일러가 자동으로 생성자를 만들어 주길 원치 않는다면 delete 키워드 사용
    • private 접근 제어자로 빈 생성자를 만드는 트릭은 이제 그만
    • 올바른 에러 메시지도 나옴
  • final/override
    • 클래스가상 함수를 파생 클래스에서 오버라이딩 못 하도록 하려면 final 키워드를 사용
    • 컴파일 도중 확인함.
    • 당연히 가상함수가 아니면 쓸 수 없음
    • override 키워드는 함수 오버라이딩의 의도를 명확히 밝힘으로써 잘못된 가상함수 오버라이딩 시 에러를 내서, 실수를 방지해줌

  • nullptr
    • C에서는 #define NULL 0
    • C++에서는 typedef decltype(nullptr) nullptr_t;
    • 숫자로 형변환이 안됨으로서, 의도를 명확히
  • 고정폭 정수형
    • 기본 자료형에 대한 표준이 없었음.
    • int8_t / uint8_t
    • int16_t / uint16_t
    • 가독성 향상을 위해 낡은 기존 자료형보다 이것들을 쓰자
  • enum class
    • C++11에서 제대로 지원
    • 정수형으로의 암시적 캐스팅이 없음
    • 자료형 검사!
    • 또한 enum에 할당할 바이트 양을 정할 수도 있음

  • unordered_map
  • unorered_set
  • array
    • 요소 수를 기억하지 않음
  • 범위 기반 for 반복문
    • for(int score : scores)
    • for(auto& score : scoreMap)
    • 이전의 for 반복보다 가독성이 더 높음
    • stl 컨테이너와 c 스타일 배열 모두에서 작동함
    • auto 키워드를 범위 기반 for 반복에 쓸 수 있음
    • 컨테이너 / 배열을 역순회할 수 없음

스마트 포인터

  • 스마트 포인터를 쓰면, delete를 직접 호출할 필요 없다
  • 가비지 컬렉터보다 빠르다
  • unique_ptr

    #include <memory>
    #include "Vector.h"
    int main()
    {
      std::unique_ptr<Vector> myVector(new Vector(10.f, 30.f));
      myVector->Print();
    
      return 0;
    }
  • shared_ptr

  • weaked_ptr


이동 생성자 및 이동 대입 연산자


constexpr

  • 컴파일 시에 값 평가를 강제하기 위해서 템플릿 메타프로그래밍을 남용했음
  • 이러지 않아도 컴파일러가 자발적으로 그렇게 해주는 경우도 있긴 했음
  • constexpr가 프로그래머의 의도를 보여주는 더 나은 방법
  • 우리의 의도는 컴파일 도중에 값을 평가하는 것임을 컴파일러에게 알려줌
  • 컴파일러가 컴파일 도중에 변수들을 결정지어 줌
  • 함수는 그럴려고 최대한 노력함
    • 평가할 수 없을 경우 실행시에 그 함수가 호출 됨

람다식

[<captures>](<parameters>) <specifiers> -> <return_type>
{
    <body>
}

캡쳐블록
- 람다 식을 품는 범위 안에 있는 변수를 람다 식에 넘겨줄 때 사용
- []
- 비어있음, 캡쳐하지 않음
- =
- 부모 함수의 모든 변수를 캡쳐
- 값에 의한 캡쳐. 모든 외부 변수를 캡쳐함
- 람다 식 안에서 수정할 수 없음
- &
- 참조에 의한 캡쳐. 모든 외부 변수를 캡쳐함.
- <변수 이름>
- 특정 변수를 값으로 캡쳐
- 람다 식 안에서 수정할 수 없음
- &<변수이름>
- 특정 변수를 참조로 캡쳐

int main()
{
    float score1 = 80.f;
    float score2 = 20.f;

    auto max = [=]() {return score1 > score2 ? score1 : score2; };
}
int main()
{
    float score1 = 80.f;
    float score2 = 20.f;

    auto changeValue = [=, &score1]() 
    {
        score1 = 100.f;
        std::cout << score2; 
    };

    changeValue();
}

지정자
- 선택사항
- mutable
- 값에 의해 캡쳐된 개체를 수정할 수 있게 함
- 괜찮은 언어 디자인, 허나 C++에 너무 늦게 들어옴

int main()
{
    int value = 100;
    auto foo = [value]() mutale
    {
        std::cout << ++value << std::endl;
    }
    foo();
}

반환형
- 선택사항
- 반환형을 적지 않으면 반환문을 통해 유추해줌


가변 인자 템플릿

template<typename ... Arguments>
class <클래스명>
{
};

#Binary Tree

  • 트리의 요소 하나를 나타내는 동그라미를 node

  • 최상단의 노드를 root node

  • 자식이 없는 노드를 terminal node

  • 트리 안에 존재하는 트리를 subtree

  • 이진 트리는 각 노드가 자식을 최대 2명 가지는 트리를 의미

  • 완전 이진 트리는 각 레벨당 2^n만큼 노드를 가진다.

  • 전위순회(preorder) : root -> left subtree -> right subtree

  • 중위순회(Inorder) : left subtree -> root -> right subtree

  • 후위순회(Postorder) : left subtree -> right subtree -> root

구현

#include <cstdio>
#define TREESIZE 100

class tree
{
private:
    int arr[TREESIZE];
    int size = 1;
public:
    tree(){
        for(int i=0; i<TREESIZE; i++) {
            arr[i] = 0;
        }
    }
    void insert(int n){
        if(size < TREESIZE){
            arr[size++] = n;
        }
        else{
            printf("Tree is full!");
        }
    }
    void preorder(int root){
        if(root <= 0 || root >= size) return;
        printf("%d", arr[root]);
        preorder(root*2);
        preorder(root*2 + 1);
    }
};

int main(){
    tree sampleTree = tree();
    for(int i=1; i<=7; i++){
        sampleTree.insert(i);
    }
    sampleTree.preorder(1);

    return 0;
}

#Single Linked List - revert

input) 2 -> 4 -> 7 -> NULL
output) 7 -> 4 -> 2 -> NULL


#include <iostream>


typedef struct _node
{
    int data;
    struct _node *next;
} Node;

Node* Reverse(Node* head){
    Node* tail = NULL;  // 새로 만들어질 노드이 헤드 포인터
    Node* curNode;      // 남아있는 오리지널 노드의 주소를 가리킬 포인터

    while(head != NULL){
        curNode = head->next;
        head->next = tail;
        tail = head;
        head = curNode;
    }
    return tail;
}