모던 C++ 프로그래밍 쿡북 - 챕터 1

Embedded June·2020년 7월 5일
0

1. 가능한 한 auto 사용하기

▶ 'auto'의 장점

  1. 변수 초기화를 놓치지 않도록 도와준다. auto는 반드시 초기화가 되어야하기 때문이다.
  2. auto는 항상 올바른 타입을 사용하는 것을 보장한다. 암시적인 형변환이 발생하지 않는다.
  3. OOP 원칙에 부합한다. 변화에 개방적이다.
  4. 타이핑이 적어지고 실수할 확률이 적어진다.
  5. 타입이 항상 오른쪽에 위치하는 일관된 코딩 스타일을 제공한다.
    int *var = new int (5) // 양쪽 모두에 type specifier가 존재한다.
    auto var = new int (5) // 오른쪽에만 type specifier가 존재한다.

▶ 'auto'의 단점

  1. auto는 만능이 아니다. const/volatile/reference에 대한 추론을 컴파일러가 자동으로 하지는 않으므로 반드시 명시해줘야 한다.
  2. long long 같은 다중 단어 타입에는 auto를 사용할 수 없다.
  3. auto의 정확한 타입을 알기 위해서는 IDE의 도움이 필요하다. 즉, 편리한 IDE가 없는 환경에서는 auto가 되려 가독성을 낮추는 결과를 낳을 수 있다.

2. 타입 별칭 및 별칭 템플릿 생성

  • 타입 별칭(Aliasing type)에 대해서는 typedefusing은 역할이 동일하다.
    typedef unsigned char byte == using byte = unsigned char
  • using을 사용한 타입 별칭의 진가는 별칭 템플릿에서 나온다.
    template <typename T>
     class userIterator { /*...*/ }
     template <typename T>
     using vec_t = std::vector<T, userIterator<T>>;
    이렇게 사용하면, vec_t라는 별칭을 다양한 자료형에 대해 사용할 수 있으며 더 읽기 쉽고 명확하다는 장점이 있다.

3. 균일한 초기화 사용하기

  • 균일한 초기화 (uniform initialization)는 C++11부터 도입된 데이터 초기화 방법이다.
  • 기본 타입과 배열 타입 그리고 구조체/클래스 타입과 함수 타입 모두 초기화 방식이 제각기 달랐지만, 중괄호 { }를 이용한 균일한 초기화 덕분에 모두 동일한 방법으로 직접 초기화복사 초기화가 가능해졌다.
    auto var = new int {20};
    auto *var {10, 20, 30};
    auto classVar {10, "hello", 3.14}; ...

4. 범위가 지정된 열거형 사용하기

  • C++11에서는 범위가 지정되지 않은 기존 enum열거형의 여러 문제를 해결하기 위해 범위가 지정된 열거형 enum class를 도입했다.

기존 문제점

enum status {Unknown, Created, Connected};
enum result {OK, FAIL, Unknown};	// 오류 발생

auto var = status::Connected;		// 오류 발생
  • 기존에는 위와 같이 열거형의 범위가 지정되지 않아 다른 열거형에서 이름 중복이 발생하는 경우가 발생할 수 있었고 특정 범위에 대해 제한을 둘 수도 없었다.

해결

enum class status : unsigned int {Unknown, Created, Connected};
enum class result {OK, FAIL, Unknown};	// 오류 발생 안함

auto var = status::Connected;		// 오류 발생 안함
  • enum class덕분에 이제 더 이상 이름 충돌을 일으키지도 않고 enum을 정규화된 이름으로 사용할 수도 있다.
  • 기본 타입을 지정할 수 있으므로 전방 선언도 가능하다.
  • 암시적인 형변환을 지원하지 않으므로 반드시 명시적인 형변환을 해줘야 한다.
    int var = static_cast<int>(status::Unknown);

5. 사용자 정의 타입에 대한 범위 기반 for loop

for loop에 대한 설명

auto cont1 = std::vector<int>{10, 0};
for (auto&& itr : cont1)
    std::cout << itr << "  ";
std::cout << '\n';
auto cont2 = std::map<int, std::string>{{1, "dog"}, {2, "cat"}};
for (auto&& [num, type] : cont2)
    std::cout << "Animal number: " << num << " type: " << type;
std::cout << '\n';

범위기반 for 루프의 등장 덕분에 타입을 지정하지 않고 for 루프를 사용할 수 있으며 바인딩 및 분해 (decomposition)도 적재적소로 사용할 수 있게 되었다.


이때, 범위기반 for 루프를 사용자 정의 타입에 대해 정의하기 위해서는 범위기반 for 루프가 어떤 방식으로 동작해야 하는지 알아야 한다.

auto&& __range = range_expression;
auto __begin = begin_expr;
auto __end = end_expr;
for (; __begin != __end; ++__begin) {
    range_decalaration = *__begin;
    /*...statements...*/
}  

상단의 코드를 보아 범위기반 for 루프가 성립되기 위해서는 다음과 같은 것들이 정의되어야 한다는 점을 알 수 있다.

1. begin()end()함수가 정의되어야 한다.
2. 반복자(Iterator)를 증가시키는 operator++가 정의되어야 한다.
3. 반복자를 참조하는 operator *가 정의되어야 한다.
4. 다른 반복자와 비항등성을 비교하는 operator !=가 정의되어야 한다.

사용자 정의 타입에 대한 for 루프 설명

자 이제 상단의 4가지 조건을 모두 성립하는 사용자 정의 타입을 만들어보자.
이때 사용자 정의 타입은 반복자를 가질 수 있도록 배열 기반 타입이어야 한다.

1. 사용자 정의 타입(클래스) 생성

// 사용자 정의 자료형
template <typename T, const size_t Size>
class DummyArray {
private:
	T data[Size] = {};
public:
	const T& GetAt(const size_t index) const {
		if (index < Size) return data[index];
		throw std::out_of_range("Index out of range!");
	}
	void SetAt(const size_t index, const T& value) {
		if (index < Size) data[index] = value;
		else throw std::out_of_range("Index out of range!");
	}
	size_t GetSize() { return Size; }
};

2. 사용자 정의 타입에 대한 반복자 클래스 생성

// 사용자 정의 자료형을 위한 반복자 클래스 생성
template <typename T, typename C, const size_t Size>
class DummyArrayIteratorType {
private:
	size_t index;
	C& collection;
public:
	explicit DummyArrayIteratorType (C& collection, const size_t index) 
		: collection(collection), index(index) {}
	bool operator != (const DummyArrayIteratorType& other) const { 
                return index != other.index; 
        }
	const T& operator * () const { return collection.GetAt(index); }
	const DummyArrayIteratorType& operator ++ () {
		++index;
		return *this;
	}
};

사용자 정의 자료형에 대한 반복자 클래스를 생성한다. 이때 4가지 조건 중 연산자 오버로딩에 해당하는 3가지 조건을 모두 충족하도록 정의해야 한다.

3. (선택) 별칭 템플릿 생성

// 사용자 정의 반복자 클래스를 위한 별칭 템플릿 선언
template <typename T, const size_t Size>
using DummyArrayIterator = DummyArrayIteratorType<T, DummyArray<T, Size>, Size>;
template <typename T, const size_t Size>
using DummyArrayConstIterator = DummyArrayIteratorType<T, DummyArray<T, Size> const, Size>;

해도 그만 안해도 그만이지만 별칭 템플릿을 생성하는 것이 코드를 깔끔하게 만들어줄 것이다. 일반 반복자와 const형 반복자를 만든다.

4. begin()과 end() 메소드 생성

// begin()과 end() 메소드 생성
// 물론 DummyArrayIteratorType의 멤버로 넣는 방법도 있지만 이 방법이 더 좋다고 한다.
template <typename T, const size_t Size>
inline DummyArrayIterator<T, Size> begin(DummyArray<T, Size>& arr) {
	return DummyArrayIterator<T, Size>(arr, 0);
}
template <typename T, const size_t Size>
inline DummyArrayIterator<T, Size> end(DummyArray<T, Size>& arr) {
	return DummyArrayIterator<T, Size>(arr, arr.GetSize());
}
template <typename T, const size_t Size>
inline DummyArrayConstIterator<T, Size> begin(const DummyArray<T, Size>& arr) {
	return DummyArrayConstIterator<T, Size>(arr, 0);
}
template <typename T, const size_t Size>
inline DummyArrayConstIterator<T, Size> end(const DummyArray<T, Size>& arr) {
	return DummyArrayConstIterator<T, Size>(arr, arr.GetSize());
}

일반 반복자와 const형 반복자에 대한 std::begin() 메소드와 std::end() 메소드를 생성한다. 긴 선언문에 비해 내부는 복잡하지 않다.

5. 나머지

template <typename T, const size_t Size>
void printDummyArray(DummyArray<T, Size>& arr) {
	for (auto&& itr : arr)
		cout << itr << " ";
	cout << '\n';
}

int main() {
	DummyArray<int, 5> arr;
	arr.SetAt(0, 10);
	arr.SetAt(1, 20);
	arr.SetAt(2, 30);
	arr.SetAt(3, 40);
	arr.SetAt(4, 50);

	printDummyArray(arr);
}

이제 사용자가 정의한 자료형에 대해서도 범위 기반 for 루프를 사용할 수 있다.


마치며

  • 겁나 훈훈해보이는 아재가 표지에 그려져있지만 내용은 전혀 그렇지 않다. 지금까지 봤던 C++ 책 중에서 제일 어려웠다.
  • 번역 상태가 좋지 않다고 생각한다. 내용이 어려운건 둘째치더라도 번역까지 괴롭히니 공부하기가 여간 어려운 책이다.
  • 1회독에 100가지 내용을 모두 습득할 수 없는 책인것 같다. 1회독은 그냥 훌훌 읽자.
profile
임베디드 시스템 공학자를 지망하는 컴퓨터공학+전자공학 복수전공 학부생입니다. 타인의 피드백을 수용하고 숙고하고 대응하며 자극과 반응 사이의 간격을 늘리며 스스로 반응을 컨트롤 할 수 있는 주도적인 사람이 되는 것이 저의 20대의 목표입니다.

0개의 댓글