2. constexpr
• C++11
• 변수, 함수를 컴파일 타임에 연산해서 상수화한다.
• 변수는 상수로 처리된다.
• 함수는 컴파일 타임에 연산된다는 것을 보장해서 constexpr 변수가 사
용할 수 있게 된다.
• 함수 내용은 return문으로만 이루어져야함.
• VS2013에서는 아직 완전히 지원되지 않는다.
3. static_assert
• C++11
• static_assert(조건, 메시지);
• assert를 컴파일 타임에 처리한다.
• 조건이 false면 컴파일 에러를 낸다.
• assert와 달리 built-in keyword
• 조건문에 변수는 const나 constexpr로 선언된 변수를 써야 한다.
• 컴파일 타임에 값을 알 수 있는 변수(사실상 상수)
4. decltype
• C++11
• 타입 지정자
• decltype(expression)
• 괄호 안의 식을 평가해서 타입을 생성
• 컴파일되면 타입이 되므로 변수 선언에 사용 가능.
• 대체 함수 선언 구문에서 사용되기도 함
• auto func(param) -> decltype(expr)
• decltype(expr)타입이 반환된다.
• 리턴값을 특정할 수 없는 템플릿 함수에서 사용됨
5. 디폴트 멤버 함수 생성 규칙
• 생성자가 선언된 경우
• 디폴트 생성자는 생성되지 않는다.
• 가상 소멸자가 선언된 경우
• 디폴트 소멸자는 생성되지 않는다.
• 이동 생성자 또는 이동 대입 연산자가 선언된 경우
• 디폴트 복사 생성자와 디폴트 대입 연산자는 생성되지 않는다.
• 복사 생성자, 대입 연산자, 이동 생성자, 이동 대입 연산자, 소멸자 중 하나라도 선언된 경우
• 디폴트 이동 생성자와 디폴트 이동 대입 연산자는 생성되지 않는다.
• 복사 생성자나 소멸자가 선언된 경우
• 디폴트 대입 연산자는 생성되지 않는다.
• 대입 연산자나 소멸자가 선언된 경우
• 디폴트 복사 생성자는 생성되지 않는다.
• 이 규칙은 상속된 클래스에도 영향을 줄 수 있다.
6. 디폴트 멤버 함수 명시적으로 생성/제거
• 디폴트 멤버 함수를 명시적으로 생성
• 디폴트_멤버_함수 =default
• Foo() =default;
• 규칙으로 인해 자동 생성되지 않은 함수들을 생성할 수 있다.
• 디폴트 멤버 함수를 명시적으로 제거
• 디폴트_멤버_함수 =delete
• Foo(const Foo&) =delete;
• void* operator new(std::size_t) =delete;
• 복사할 수 없거나, new로 생성할 수 없는 클래스를 만들 수 있다.
7. new
• 메모리 할당 및 객체 초기화 연산자
• [::] new [placement] type [initializer]
• 명시적으로 전역 new를 사용할 경우 ::을 붙인다.
• placement: 추가 인수
• type: 할당할 객체 타입
• initializer: 초기화 구문
• 작동 방식
• 지정한 타입의 크기만큼 메모리를 할당한다. (operator new())
• 할당된 메모리에 객체를 초기화한다. (생성자 호출)
• 지정한 타입의 포인터를 반환한다.
8. operator new
• 메모리 할당 단계에서 호출된다.
• 디폴트 new가 존재하며 오버로딩도 가능하다.
• 오버로딩은 전역 new 오버로딩과 클래스 멤버 new 오버로딩이 있음.
• 디폴트 new의 종류
• void* operator new(size_t) throw(bad_alloc);
• 일반적인 new. bad_alloc을 throw
• void* operator new(size_t, nothrow_t) throw();
• exception을 throw하지 않는 new
• void* operator new(size_t, void*) throw();
• 메모리 할당 없이 생성자만 호출하는 new
10. delete
• 객체 소멸 및 메모리 해제 연산자
• [::] delete [[]] cast-expression
• 명시적으로 전역 delete를 사용할 때 ::를 붙인다.
• 배열을 해제할 때 []를 붙인다.
• cast-expression: new로 할당된 메모리의 포인터
• 작동 방식
• 객체를 소멸한다 (소멸자 호출)
• 메모리를 해제한다 (operator delete())
11. operator delete
• 메모리 해제 단계에서 호출된다.
• 디폴트 delete가 존재하면 오버로딩도 가능하다.
• new와 같이 전역 delete, 클래스 멤버 delete 오버로딩이 있다.
• 디폴트 delete의 종류
• void operator delete(void*) throw();
• 일반적인 delete. 사실상 사용자는 이것만 쓴다.
• void operator delete(void*, nothrow_t) throw();
• 예외를 던지지 않지만 어차피 원래 것도 안 던진다.
• void operator delete(void*, void*) throw()
• 아무 일도 하지 않는다.
• 아래 두 개는 대응되는 operator new가 실패했을 때 내부적으로 사용되는 듯.
13. operator new[], operator delete[]
• 배열을 new 하고 delete할 때 호출된다.
• 비배열 버전처럼 디폴트 전역 함수가 있고, 오버로딩 가능하다.
• operator new, delete를 클래스 멤버 오버로딩하면 전역 new,
delete는 가려지게 되므로 주의.
14. 가변 인자 리스트
• 함수에서 가변적인 개수의 인수를 받아야 할 때 사용한다.
• 인수에 …을 넣으면 그 이후로 들어오는 인수는 모두 가변 인자
리스트에 들어간다.
• int printf(const char* format, …);
• 함수 내부에서 매크로 함수를 이용한다.
• #include <stdarg.h>
• va_list
• va_start(list, 마지막 고정 인수);
• va_arg(list, 가져올 타입);
• va_end(list);
18. ranged for(range based for)
• C++11
• for (range_decl : range_expr) loop_statement
• 내부 구현은 일반 for문을 사용.
{
auto&& _range = range_expr;
for (auto _begin = begin_expr, _end = end_expr; _begin != _end; ++_begin)
{
range_decl = *__begin;
roop_statement
}
}
19. ranged for 지원하는 class 설계법
• 주요 구문들
• begin_expr
• _range.begin();
• begin(_range);
• end_expr
• _range.end();
• end(_range);
• iterator
• ++_begin
• *_begin
20. lvalue, rvalue
• 원래 의미
• lvalue: 대입식의 왼쪽에 오는 값
• rvalue: 대입식의 오른쪽에 오는 값
• C++에서의 실제 의미
• lvalue: (일반적으로) 할당될 수 있는 값, 식이 끝나도 남는 영속적인 값
• 주소를 가진다.
• var, *var, var[i], ++var
• 주의: const로 선언된 변수도 lvalue이다. (non-modifiable lvalue)
• rvalue: (일반적으로) 할당될 수 없는 값, 식이 끝나면 죽는 일시적인 값
• 주소를 가지지 않는다.
• 1, a + b, std::string(“Str”), var++
• rvalue도 const일 수 있다.
22. &, &&
• &
• lvalue reference
• Type& ref = lvalue;
• &&
• rvalue reference
• C++11
• rvalue를 참조하여 보존할 수 있다.
• Type&& ref = rvalue;
※ 이름이 있는 rvalue reference는 lvalue이다.
※ const Type&은 rvalue도 참조할 수 있다.
23. rvalue reference
• reference타입이기 때문에 내부적
으로는 포인터 사용.
• 그럼 rvalue는 어디에?
• local에 임시로 저장된다.
• 옆에 보이는 ebp-0x24
• 사실상 lvalue reference와 비슷함.
24. 의미 체계 이동
• rvalue reference를 사용하는 주 이유.
• 실제로 내부에서 구현되는 것은 아니고 의미 부여 측면이 큼.
• 이동 생성자, 이동 대입 연산자로 구현
• 복사 생성자와 대입 연산자의 이동버전
• 디폴트 이동 생성자와 디폴트 이동 대입 연산자는 없음.
• Class(const Class&& other);
• Class& operator=(const Class&& other);
• 복사가 아닌 이동이기 때문에 속도가 빠르다.
• 원본 객체의 정보는 모두 사라진다.
27. std::move
• #include <utility>
• std::move(arg);
• lvalue를 rvalue로 변경
• 이동 생성자나 이동 대입 연산자를 호출하고 싶을 때 쓰면 된다.
• static_cast<Type&&>도 같은 역할을 한다.
• 당연하지만 rvalue를 넣으면 그냥 rvalue로 나온다.
28. Template
• Generic Programming이 가능해지는 문법요소.
• Generic Programming?
• 데이터 형식에 의존하지 않고, 하나의 값이 여러 다른 데이터 타입들을 가질 수
있는 기술에 중점을 두어 재사용성을 높일 수 있는 프로그래밍 방식(wikipedia)
• 어떤 타입으로도 쓸 수 있는 함수/구조체/클래스를 만든다.
• 몇몇 매크로 함수 대체 가능
• void 포인터를 대체하여 일반화된 함수를 만들 수 있음.
• 스마트 포인터나 컬렉션 등 유틸 클래스를 만들기 좋음.
• C++은 Strong type이기 때문에 내부적으로는 코드 자동 생성.
29. Template
• template <template-parameter-list> declaration
• template-parameter-list: 콤마로 구분된 템플릿 인수 목록
• 템플릿 인수의 종류는 다음 장 설명
• declaration: 함수 또는 구조체/클래스/공용체의 선언/정의
• 예시
• template <class T> void function(T arg);
• template <class T> struct Foo{ T member; };
30. Template
• 템플릿 인수 종류
• parameter-declaration
• 비 타입 매개 변수. 일반 변수 선언을 쓰면 된다.
• int i
• (class | typename) identifier [= typename]
• 타입 매개 변수. identifier는 원하는 식별자.
• class T 또는 typename T
• = typename은 디폴트 타입을 설정하는 데에 쓴다.
• class T = int
• template<template-parameter-list> class [identifier] [= name]
• 템플릿 클래스를 템플릿 인수로 쓸 수 있다는 의미
• template<class T> class Type
31. Template function
• 호출할 때 템플릿 인수를 명시적으로 쓰지 않아도 된다.
• 인수 추론 작업을 거친다.
• 단, 비 타입 매개변수가 있다면 써줘야 한다.
32. Template class
• 명시적으로 템플릿 인수를 지정해주어야만 한다.
• 객체의 크기를 컴파일 타임에 결정하기 위해서
• 함수처럼 인수를 언제나 받는 것이 아니기 때문에 추론을 할 수 없다.
34. Template의 인수 추론
• P : Parameter Type, A : Argument Type
• template<class T> void func(const T& a); 에서 const T&가 P이다.
• func(123); 에서 int가 A이다. 즉, T에 int가 들어가는 것이다.
1. P가 래퍼런스 타입이 아닌 경우
• A가 배열 타입이면, 포인터로 바뀐다.
• A: int[3] -> int*
• void func<int*>(int* a);
• A가 함수 타입이라면, 함수 포인터로 바뀐다.
• A: void(int) -> void(*)(int)
• void func<void(*)(int)>(void(*a)(int));
• A가 cv-qualified 타입이라면, 최상위 cv-qualifier는 무시된다.
• cv는 const와 volatile을 의미한다. 즉, 가장 앞의 const나 volatile은 무시된다.
• A: const int -> int
• void func<int>(int a);
35. Template의 인수 추론
2. P가 cv-qualified 타입이라면 최상위 cv-qualifier는 무시된다.
• P: const T -> T
• void func<int>(int a);
3. P가 래퍼런스 타입이라면, P가 사용된다.
• P: T& -> T&
• void func<int>(int& a);
4. P가 cv-unqualified인 rvalue(universal reference)이고 A가
lvalue라면, A대신 A의 lvalue reference가 사용된다.
• P: T&& and A: int(lvalue) -> A: int&
• void func<int&>(int& && a); // ???
36. 참조 축소 규칙
• 인수 추론 과정에서 reference type을 결정하는 규칙
• 과정
• 템플릿 타입을 인수의 타입으로 치환한다.
• void func<int&>(T&& arg)라면 T가 int&로 치환됨
• void func<int&>(int& && arg)
• 참조 축소 규칙에 의거하여 최종 type을 결정한다.
치환된 인수 최종 Type
& & &
&& & &
& && &
&& && &&
37. typename
• Template 정의에서 사용.
• Template type에 종속된 타입을 사용할 때 명시적으로 표현.
• T::iterator
• iterator가 타입인지, 함수인지, 정적 멤버 변수인지 알 수 없음.
38. std::forward
• #include <utility>
• 변수가 원래 rvalue또는 rvalue ref였으면 rvalue로 복구한다.
• 템플릿 함수지만 명시적으로 타입을 표기해야한다.
• std::forward<Type>(arg);
• template<typename T> void function(T&& arg);
• 참조 축소 규칙에 의해 lvalue, rvalue 모두 인수로 사용 가능하다.
• arg는 인수로 무엇이 들어오던지 lvalue다.
• lvalue가 들어오면: arg는 lvalue reference
• rvalue가 들어오면: arg는 이름이 있는 rvalue reference == lvalue
• 즉 원래 인수가 rvalue였더라도 rvalue로써 다른 함수를 호출할 수 없다.
40. 함수 포인터
• 함수를 가리키는 포인터
• 함수도 변수와 마찬가지로 메모리에 존재한다.
• text 영역 또는 code 영역 또는 code segmentation
• 함수의 이름은 함수의 주소를 나타낸다.
• 정확히는 함수의 시작 위치를 가리킨다.
• return_type (*identifier) (argument_list)
• 함수 정의 부분에서 함수의 이름이 (*이름)으로 바뀌었다고 보면 됨.
• void (*func)(int)
• 표기가 길기 때문에 자주 사용하는 경우 typedef를 한다.
• typedef void (*Func)(int);
• Func가 새로운 타입 명이 된다.
42. 멤버 함수 포인터
• 클래스의 메서드를 가리키는 포인터.
• 함수 포인터와 개념은 동일
• 함수 포인터와 규칙이 조금 다른 부분이 존재한다.
• return_type (namespace::*identifier) (argument_list)
• void (Foo::*func)()
• 주의: void (*Foo::func)()는 옳지 않음.
• Foo::func가 identifier로 인식됨.
• void Foo::(*func)()도 아님
• 명시적으로 포인터 연산자를 사용해야 함.
• &를 사용하지 않으면 잘못된 함수 호출이라고 생각한다.
• 명시적으로 호출 주체(this)를 지정해야 함.
44. Function object, Functor
• 함수 객체. 객체를 함수처럼 사용하는 것.
• 객체의 식별자를 함수 이름처럼 사용한다.
• () 연산자를 오버로딩해서 만든다.
• 특정 STL 함수에서 사용된다.
• 특히 algorithm 함수들
• std::sort, std::find_if etc.
• 함수 (포인터)와의 차이점
• 멤버 변수, 멤버 함수를 가질 수 있다.
• 함수 포인터 사용 시 불가능한 inline이 가능하다.
• 함수 포인터 호출은 대응되는 함수를 inline 할 수 없음.
• Functor는 언제나 대응되므로 가능함.
• Functor 사용 측에서는 Template으로 범용성을 보완.
46. lambda
• C++11
• Closure
• An unnamed function object capable of capturing variables in scope.
• 람다는 별도로 정의된 Closure type이다.
• 함수 포인터 형식으로 캐스팅이 가능하다.
• ‘[‘ capture-list ’]’ [( params )] [mutable] [exception] [attribute] [-> return-type] ‘{‘ body ‘}’
• capture-list : 캡처할 변수 리스트
• reference(&), value(=) 방식으로 가져올 수 있음.
• mutable : 람다에서 value 캡처된 변수들을 변경할 수 있음
• return-type : 반환 타입. 쓰지 않으면 body의 return으로 추론한다.
48. std::reference_wrapper
• C++11
• #include <functional>
• Reference type을 wrapping한 객체
• Reference type을 쓸 수 없는 곳에 쓸 수 있음.
• 참조를 담을 수 없는 container에 참조를 담을 수 있다.
• std::bind나 std::thread등에 인수를 넘길 때 참조를 넘길 수 있다.
• std::ref와 std::cref로 만든다.
• std::ref는 std::reference_wrapper를 반환
• std::cref는 const std::reference_wrapper를 반환
49. std::bind
• C++11
• #include <functional>
• 함수의 인수에 값을 bind해놓는 것.
• 멤버 함수의 경우 this도 바인딩
• placeholder로 일부 인수는 추가로 입력 받도록 남길 수 있음
• 래퍼런스 변수는 std::ref나 std::cref로 넘긴다.
51. std::function
• C++11
• #include <functional>
• Wrapper for a callable object.
• 함수 포인터, Functor, lambda, bind된 함수 모두 담을 수 있는 타입.
• 특정 형식의 임의 루틴을 인터페이스로 받는 것이 가능해진다.
• 단순 template 사용 시 함수의 형식을 제한할 수 없음.
• void RunForAllItems(std::function<void(Item*)> routine);
53. std::unique_ptr
• C++11
• #include <memory>
• 포인터 래퍼 객체
• 래퍼 객체가 소멸하는 시점에 들어있는 객체가 파괴된다.
• unique_ptr 자체를 new로 생성하지는 말자.
• 객체를 유일하게 보존한다.
• unique_ptr은 복사할 수 없다.
• 이동만 가능하다.
• std::make_unique로도 만들 수 있다.
55. std::shared_ptr
• C++11
• #include <memory>
• 포인터 래퍼 객체
• 객체를 여러 shared_ptr에서 가지고 갈 수 있다.
• reference count를 이용해서 수명을 관리한다.
• 더 이상 참조하는 shared_ptr이 없으면 들어있는 객체는 파괴된다.
• std::make_shared로도 만들 수 있다.
58. std::weak_ptr
• C++11
• #include <memory>
• std::shared_ptr의 약한 버전
• reference count를 늘리지 않는다.
• 순환참조에 의해 reference count가 줄지 않는 현상을 막기 위해 쓴다.
• 들고 있는 객체가 유효한지 expired 메서드로 확인할 수 있다.
• lock 메서드로 shared_ptr을 만들 수 있다.
• expried되었다면 nullptr을 return한다.