대메뉴 바로가기 본문 바로가기

데이터 기술 자료

데이터 기술 자료 상세보기
제목 비주얼 C++와 함께하는 문법 업그레이드 ①
등록일 조회수 5413
첨부파일  

네이티브 개발자를 위한 비주얼 스튜디오 2015

비주얼 C++와 함께하는 문법 업그레이드 ①



마이크로소프트(이하 MS)의 비주얼 스튜디오 2015는 C++ 개발자를 위한 다양하고 새로운 것들이 많이 시도되고 포함됐다. 비주얼 스튜디오로도 안드로이드 네이티브 앱 개발이 가능해졌을 뿐 아니라 비주얼 스튜디오의 디버거 상에서 중단점을 설정하고 디버깅할 수 있게 되는 등 시대의 흐름에 부응하는 파격적인 기능이 추가됐다. C++ 개발자들이 필요로 하는 컴파일러 성능 향상, 라이브러리 업데이트, 최신 표준 문법 또한 반영됐다. 이 글에서는 이 중 비주얼 스튜디오 2015를 사용할 때 알아야 할 문법 업데이트를 소개한다.



C++ 11(C++0x)로 대변되는 모던 C++(Modern C++) 코딩 스타일은 C++ 개발자들이 알고리즘을 구현하고 비즈니스 로직을 만드는 데 직관적이고 편리한 기능을 제공한다. 특히 플랫폼과 벤더, 구현 등의 특징에 따라 선택되는 최신 언어의 장점을 흡수하며 더 강력해진 라이브러리와 문법은 C++ 개발자들의 기대를 한몸에 받고 있다. 비주얼 스튜디오 2015 출시와 함께 C++도 대폭 업데이트됐다. 이전 버전에서 발견된 400여 개의 컴파일러 버그가 수정됐을 뿐 아니라 빌드 시간도 단축됐다. 기존 코드를 새로 컴파일하는 것만으로 더 좋은 품질의 바이너리가 만들어진다. 솔루션 파일을 열 때 더블 클릭한 시점부터 코드가 화면에 나타날 때까지의 속도가 더 빨라진 것처럼 느껴지는 작은 변화는 물론 많은 개발자가 원하던 리팩토링 기능, 디버깅에 유용한 신기능 등도 주요 변화들이다. 이보다 더 반가운 소식은 문법 개선과 표준 라이브러리 업데이트일 것이다. 이번 비주얼 스튜디오 2015에는 C++ 11, C++ 14뿐 아니라 C++ 17(C++ 1z)에 제안된 것 중 확실히 반영될 것 같은 몇 가지도 발빠르게 반영돼 주목된다.





C99 호환성을 위한 __func__

__func__는 직관적으로 알 수 있듯, 현재 해당 코드가 위치한 곳의 함수명을 알려준다. 비주얼 C++에서는 이미 __FUNCTION __이라는 매크로 상수로 이러한 기능을 지원했다. 그럼에도 __func__이 추가된 것은 C99 문법과 호환성을 위해서다. __FUNCTION__과 __func__ 두 상수의 차이는 __FUNCTION__이 매크로 상수인데 반해, __func__는 전역변수 형태로 접근할 수 있다는 점이다. __func__는 __FUNCTION__과 마찬가지로 함수의 정의부(body)에서만 사용될 수 있으며, 그 이외의 곳에서 사용하면 C3187 에러 메세지와 함께 컴파일이 중단된다.



<리스트 1> __func__로 __FUNCTION__처럼 함수명을 얻을 수 있다. void foo() { cout < < __func__ < < " " < < __FUNCTION__; } 결과값 : foo foo

만일 foo라는 함수 정의부에서 __func__이 사용됐다면 컴파일러는 미리 정의된 변수로 static char const __func__[] = "foo"; 라는 코드를 만든 후 __func__라는 정적 변수를 사용한 것과 동일한 기능을 수행한다. __func__는 __FILE__, __LINE__과 함께 디버깅 정보를 출력하는 데 유용하다.



<리스트 2> __func__의 사용 struct S { S() : s(__func__) { // 생성자명 대입 cout < < __func__ < < endl; // 생성자명 출력 } const char *s; // 생성자명 레퍼런스 }; char const * g_func = __func__; // C3187 에러, 정의부가 아님 void f(const char * s = __func__); // C3187 에러, 정의부가 아님

완전한 우측 값 참조 지원

우측 값 참조는 바로 전 버전의 비주얼 스튜디오에 추가된 C++ 11 문법 업데이트 중 가장 주목 받는 기능 중 하나였다. 기본 타입에 대한 우측 값 참조의 개념은 그리 어렵지 않다. 명시적인 좌측 값과 임시적인 우측 값을 구분할 수 있고, 우측 값 참조라 임시 값에 대한 레퍼런스라는 개념만 알면 되기 때문이다. 우측 값 참조가 어렵게 생각되는 이유는 C++의 클래스에서 우측 값 참조의 특성을 활용해 클래스에 의미 체계 이동(Move semantics)과 완벽한 전달(Perfect forwarding) 이 두 가지를 구현해야만 하기 때문이다. 지금부터는 이 중 이번 업데이트 사항과 관련된 의미 체계 이동에 대해 살펴본다. 우측 값 참조를 이용하면 객체 간 리소스를 전송하는 특성을 이용해 의미 체계 이동을 구현할 수 있다. 이를 통해 코드의 다른 곳에서 사용하지 않아 없어져 버리는 임시 객체가 가지고 있는 리소스를 좌측 값으로 활용할 수 있다.



<리스트 3> 우측 값 참조가 반영되지 않을 경우 operator+가 호출될 때마다 임시 객체가 생성된다. int main() { string s = string("after"); string s2 = s + "n" + "o" + "o" + "n"; cout < < s2 < < endl; }

예컨대 스트링 변수를 통해 문자열을 복사하면 스트링 클래스의 operator+가 호출될 때마다 문자열을 포함하는 임시 스트링 객체를 계속적으로 생성한다. 또한 내부적으로 관리하는 문자열 값을 복사한다. operator+의 인자로 들어가는 객체는 좌측 값인지 우측 값인지 알 수 없으므로 동적 메모리 할당 크기도 늘어나고, 속도도 오래 걸리는 등 전체적으로 성능이 떨어지게 된다. 만일 우측 값 참조를 이용한다면 리소스를 계속적으로 전달받기 때문에 훨씬 효율적으로 동작시킬 수 있다. 일반적으로 클래스에 의미 체계 이동을 구현하기 위해 우측 값 참조를 인자로 받는 이동 생성자(Move constructor)와 이동 할당 연산자(Move assignment operator)를 구현한다. 그러면 컴파일러는 코드의 상황을 판단해 반환 값과 이를 받아 할당하는 객체의 타입이 동일하면 반환 값 최적화(RVO: Return value optimization)와 명명된 반환 값 최적화(NRVO: Named return value optimization)를 바이너리에 반영한다. 이렇게 처리할 수 없는 경우 이동 생성자를 호출한다. 이동 생성자와 이동 할당 연산자를 구현하는 방법은 간단한다(<리스트 4> 참조).



<리스트 4> 이동 생성자와 이동 할당 연산자 구현의 예 class MemoryBlock { public: explicit MemoryBlock(size_t length) : _length(length), _data(new int[length]) {} ~MemoryBlock() { delete[] _data; } MemoryBlock(const MemoryBlock& other) : _length(other._length) , _data(new int[other._length]) { std::copy(other._data, other._data + _length, _data); } MemoryBlock& operator=(const MemoryBlock& other) { if (this != &other) { delete[] _data; _length = other._length; _data = new int[_length]; std::copy(other._data, other._data + _length, _data); } return *this; } private: size_t _length; int* _data; };

먼저 인자로 받은 우측 값 참조 인자의 데이터를 맴버에 할당한 후 맴버에 기본 값을 할당한다. 그리고 우측 값 참조로 받은 인자를 초기화시켜 리소스가 중복 해제되는 것을 막는다. 이동 할당 연산자의 경우 리소스 처리와 현재 인스턴스에 대한 참조 반환 부분을 제외하면 이동 할당 생성자를 이용한 처리와 동일하다.



<리스트 5> 이동 할당 연산자가 정의될 경우 이를 이용하는 이동 생성자를 정의할 수 있다. class MemoryBlock { public: MemoryBlock(MemoryBlock&& other) { *this = std::move(other); } MemoryBlock& operator=(MemoryBlock&& other); };

만일 이동 할당 생성자라고 정의돼 있을 경우 이를 이용하는 생성자를 간단히 구현할 수 있다. std::move는 단순히 우측 값 참조로 타입캐스팅만을 해주는 핼퍼 함수를 통해 자연스럽게 이동 할당 생성자가 호출된다. 이는 코드 상에서 인자가 우측 값 참조로 값이 이동하고 있음을 명시해 주는 역할을 한다.



<리스트 6> std::move 구현 template< class T> inline typename remove_reference< T>::type&& move(T&& arg) noexcept { return (static_cast< typename remove_reference::type&&>(arg)); }

우측 값 참조로 값을 이동시킬 수 있기 때문에 벡터와 같은 컨테이너 선언도 더 직관적이다. STL 벡터를 이용해 데이터가 큰 객체를 관리할 경우 객체의 포인터를 관리하는 벡터를 선언했었지만 비주얼 스튜디오 2015부터는 컨테이너에 객체를 그대로 넣어도 객체 복사로 인한 성능 문제가 발생하지 않는다. 뒷부분에 나오는 간결한 범위 기반의 for 문(Terse ranged-based for loops)에서도 설명하겠지만 컨테이너를 순회할 때도 우측 값 참조를 사용해 직관적이고 편리하면서도 성능 좋은 코드를 얻을 수 있다.



<리스트 7> 우측 값 참조로 인해 컨테이너의 객체 복사 성능이 매우 향상됐다. std::vector< MemoryBlock> v; v.push_back(instance);

클래스는 특수 맴버 함수(Special Member Functions)를 가지고 있다. 특수 맴버 함수는 클래스의 인스턴스를 만들고 제거하고 복사하며 변환하는 등 제어 기능을 하는 함수다. 이 중 생성자, 복사 생성자, 소멸자, 할당 연산자는 사용자가 함수를 별도로 정의하지 않을 경우 컴파일러가 자동으로 만들어 준다. 이전 버전의 비주얼 스튜디오에서는 우측 값 참조 기능을 제공하며, 이를 이용한 의미 체계 이동이 반영된 표준 라이브러리를 제공했다. 또한 개발자가 정의하는 클래스에 대해서는 이동 생성자와 이동 할당 생성자의 구현을 개발자에게 맡겼었다. 반면 비주얼 스튜디오 2015는 명확한 상황일 경우 이동 생성자와 이동 할당 생성자를 특수 맴버 함수에 포함해 자동으로 생성해 준다. 이를 이동 특수 맴버 함수의 암시적 생성(Implicit Generation of Move Special Member Functions)이라 말한다. 이동 특수 맴버 함수의 암시적 생성을 덕분에 비주얼 C++의 우측 값 참조가 더 완벽해졌다.



컴파일 시점에 변수를 상수처럼 사용하는 constexpr

비주얼 스튜디오 2015는 C++ 11 스펙에 포함된 한정자 키워드dls constexpr을 통해 일반화된 상수 표현식(Generalized constant expressions)을 부분적으로 지원한다. 일반화된 상수 표현식을 사용하면 함수 반환 값이나 변수 값을 컴파일 시점에 상수처럼 사용하도록 처리 가능하다. constexpr는 변수와 함수 반환 값의 한정자 위치에 constexpr 키워드를 넣어 사용하면 된다. const로 선언된 변수와 constexpr을 사용한 변수는 모두 컴파일 시간에 상수로 판단된다. const의 경우 상수 뿐 아니라 변수로부터 값을 할당받을 수 있는 반면, constexpr은 반드시 상수 표현식(constant expression)으로 초기화가 이루어져야 한다는 점에 주의하자. 초기화가 이루어 지지 않거나 상수가 아닌 값으로 초기화가 하면 컴파일에 실패할 것이다.



<리스트 8> 반드시 상수 표현식으로부터 초기화해야 하는 constexpr int n = 7; const int a = 7; // OK const int b = n; // OK constexpr int c = 7; // OK constexpr int d; // C2737 에러 constexpr int e = n; // C2127 에러

함수 역시 반환 값의 앞 한정자 자리에 constexpr을 사용한다. constexpr이 지정된 함수가 반환하는 값은 상수로 사용될 수 있다.



<리스트 9> constexpr로 지정된 함수의 반환 값은 상수로 간주될 수 있다. class char_numeric_limits { public: static constexpr int (maxbit)() noexcept { return (CHAR_BIT); }}; const int x = CHAR_MAX; const int y = char_numeric_limits::maxbit();

C++의 템플릿 함수는 코드에서 해당 템플릿이 처음 호출될 때 컴파일러가 컴파일 시간에 인스턴스를 만든다. 이를 함수 템플릿 인스턴스화(Function Template Instantiation)라고 한다. 함수 템플릿 인스턴스화 시 값을 계산하는 기법을 템플릿 메타 프로그래밍(Template Meta Programming)이라고 하는데, 템플릿 메타 프래그래밍으로 계산한 값은 보통 상수로 사용하게 된다. constexpr 역시 컴파일 시간에 생산하는 상수이기 때문에 템플릿 메타 프로그래밍과 비교 가능하다. 예컨대 배열의 크기를 정의하는 부분이나 enum 형의 값처럼 상수가 위치해야만 하는 곳에서 두 값을 모두 상수처럼 사용할 수 있다.



<리스트 10> 템플릿 메타 프로그래밍과 일반화된 상수 표현식의 비교 template < int N> struct tmp_factorial { enum { value = N * tmp_factorial< N - 1>::value }; }; template < > struct tmp_factorial< 0> { enum { value = 1 }; }; constexpr int constexpr_factorial(int n) { return n > 0 ? n * constexpr_factorial(n - 1) : 1; } enum FACTORIAL { first = tmp_factorial< 1>::value , second = constexpr_factorial(2) , third = constexpr_factorial(3) }; int tmp = FACTORIAL::third; // 6

하지만 런타임 시 값을 계산한다면 두 방식은 차이를 보인다. 템플릿 메타 프로그래밍의 경우 컴파일 시간에 상수 값이 결정되기 때문에, 템플릿의 매개변수화된 템플릿 인자는 반드시 상수여야 한다. 반만 constexpr의 경우 동적인 값이라고 할지라도 값을 생성하는데 문제가 없다. 다른 C++의 특징과 다르게 로직이 같은 함수라면 컴파일 시간과 런타임을 위해 이 둘을 각각 만들 필요가 없다.



<리스트 11> 컴파일 타임과 런타임을 위한 같은 함수를 만들 필요가 없다. int n; cin >> n; cout < < tmp_factorial::value; // C2971 에러 cout < < constexpr_factorial(n); // OK

constexpr을 함수에 사용하기 위해서는 몇 가지 조건이 필요하다. 먼저 함수의 반환 값은 반드시 리터럴 타입이어야 한다. 리터럴 타입의 유무는 STL의 type_traits가 제공하는 std::is_literal_type 템플릿을 통해 판단할 수 있다.



<리스트 12> virtual이나 사용자 정의 소멸자 등이 포함된 객체는 리터럴 타입이 아니다. struct A { int m; }; // 리터럴 타입 struct B { virtual ~B(); }; // 리터럴 타입이 아님

반환값이 void일 경우 constexpr을 부여할 수 없다. 그리고 함수는 반드시 return 표현식만이 와야 한다. constexpr이 사용된 함수는 인라인 함수와 같이 컴파일되기 때문에 하나의 표현식만이 와야 한다는 제약이 있다.



<리스트 13> constexpr는 하나의 return 표현식만 허용한다. constexpr int valid_factorial(int n) { return n > 0 ? n * valid_factorial(n - 1) : 1; } constexpr int invalid_factorial(int n) { if (n) { // C3249 return n * invalid_factorial(n - 1); } else { return 1; }}

비주얼 스튜디오 2015가 제공하는 constexpr은 C++ 11의 표준 중 initializer_list나 배열을 초기화하는 것과 같은 집계 초기화를 지원하지 않는다. 또한 클래스 타입의 리터럴 타입을 인자나 반환값으로 사용할 수 없다. C++ 14에는 void 타입의 반환 값을 가지는 함수도 constexpr을 부여할 수 있게 되는 등 조건이 상당히 완화됐다. 이것이 비주얼 스튜디오 2015의 서비스팩을 통해 개선될 constexpr가 기대되는 이유다.



간결한 범위 기반의 for 문(Terse ranged-based for loops)

C 언어의 자료구조는 배열과 포인터 기반이기에, 컨테이너를 순회하는 for 문의 경우 인덱스를 이용해 접근 하는 것이 일반적이다. 예를 들면 배열을 선언하고 0부터 해당 배열의 최대 인덱스 값만큼 순회하며 컨테이너의 값에 접근하는 것이다.



<리스트 14> 배열을 순회하며 접근하는 예 int arr[]={10,11,13,15,17,19,20}; for(int i=0; i< 7; i++) { foo(arr[i]; }

그러나 인덱스를 통해 직접 요소에 접근하는 방식은 에러 처리나 코드상 실수할 여지가 많은 좋지 않은 방식이다. 이런 이유로 코드 재활용이나 잘못된 인덱스 접근에 대한 경험이 풍부한 개발자는 결국 GoF 디자인 패턴의 이터페이터 패턴과 같이 공통 인터페이스를 통해 접근하는 방식을 선호한다.

STL 컨테이너들은 .begin()과 .end()와 같은 공통 인터페이스를 통해 컨테이너의 요소에 접근하는 이터레이터를 사용할 수 있다. STL 사용이 편리해짐에 따라 프로그램 전체 코드에서 배열보다는 STL이 제공하는 컨테이너를 빈번하게 사용하게 된다. 따라서 STL의 컨테이너를 순회하는 방법으로 이터레이터를 이용하는 빈도도 예전보다 많아졌다.

<리스트 15> 복잡한 컨테이너의 길어진 이터레이터 선언 map< basic_string< TCHAR>, vector< int>> Name_Nums_matching; for (map< basic_string< TCHAR>, vector< int>>::iterator iter = Name_Nums_matching.begin() ; iter != Name_Nums_matching.end(); ++iter) { foo(*iter); }

하지만 복잡한 템플릿 컨테이너식의 이터레이터 정의가 길어지면 사용이 다소 불편하다. 그래서 이터레이터를 이용한 프로그래밍 시에는 좀 더 STL 스타일이면서 라이브러리 차원의 컨테이너 순회가 가능한 std::for_each를 쓰기도 한다. 하지만 C++ 11이 auto 타입과 범위 기반의 for 문(Range-based for loops)을 제공하면서 이터레이터의 간결한 표현이 가능해졌다. 범위 기반의 for 문은 이전 버전의 비주얼 스튜디오에서도 사용할 수 있다.



<리스트 16> 범위 기반의 for 문의 정의 for ( for-range-declaration : expression ) { statement }

범위 기반의 for 문은 객체가 .begin()과 .end(), 그리고 operator = 등이 정의돼 있을 때 사용할 수 있다. 해당 맴버를 이용해 컴파일 시간에 순회할 방법을 해석한다.



<리스트 17> 범위 기반 for문의 컴파일 과정에서의 해석 { auto && __range = range_expression ; for (auto __begin = begin_expr , __end = end_expr ; __begin != __end; ++__begin) { range_declaration = *__begin; loop_statement }}

컴파일러의 해석에 따라 컨테이너를 순회하는 동작이 수행된다. auto 형의 경우 컨테이너의 값이 복사되며, auto&의 경우 레퍼런스형으로 추론돼 컨테이너의 실제 값이 변경될 수 있다. 브레이스로 초기화된 리스트(Braced-init-list)를 사용할 경우 std:: initializer_list<>&& 타입으로 추론된다.



<리스트 18> 다양한 범위기반 for 문 예 std::vector v = {0, 1, 2, 3, 4, 5}; for (int const& i : v) { // 쓰기 금지 레퍼런스로 추론 } for (auto i : v) { // int 값으로 복사 } for (auto&& i : v) { // int& 타입의 레퍼런스로 접근 } for(int n : {0,1,2,3,4,5}) { // 브레이스로 초기화된 리스트(braced-init-list) 사용 }

비주얼 스튜디오 2015는 범위 기반 for 문에서 이터레이션에 사용되는 변수 타입을 생략할 수 있는 C++ 17 표준에 들어갈 간결한 범위 기반의 for 문을 지원한다. 간결한 범위 기반의 for 문에 사용되는 이터레이션용 변수는 우측 값 참조 타입으로 추론된다.



<리스트 19> 두 식은 같은 식으로 추론된다. for (i : v) {} for (auto&& i : v) {}

우측 값 참조 타입으로 추론되기 때문에 이터레이션에 사용되는 변수 값을 포워딩 레퍼런스(Forwarding reference)로 변경할 경우 컨테이너의 실제 값이 변경됨에 주의해야 한다. for 문에서 컨테이너의 값을 변경하지 않으려면 auto const& 타입으로 선언하면 된다. 만일 이터레이션 변수가 auto&& 타입이 아닌 auto&일 경우에도 컨테이너의 실제 값이 변경되는 것과 거의 같은 결과를 얻을 수 있다. 또한 레퍼런스 타입이므로 성능에도 큰 차이가 없다. 하지만 몇 가지 경우에는 레퍼런스 타입으로 선언이 불가능하다.



<리스트 20> vector 타입 예 vector< bool> v1 = { true, false }; for (auto& i : v1) {} // C4239 Warning vector< int> v2 = { 1, 1 }; for (auto& i : v2) {} // OK

대표적인 케이스가 vector 타입인데 vector에서 이터레이션에 사용될 레퍼런스 타입의 변수는 상수여야 하므로 auto const& 타입이 돼야 한다. 이렇게 선언할 경우 컨테이너에 있는 실제 값을 변경할 수 없게 된다. 따라서 auto&&로 선언해야 의도대로 동작하는 변수를 선언할 수 있다. 간결한 범위 기반의 for 문 또한 우측 값 참조의 변수로 추론한다.



확장된 sizeof

C++ 11에는 sizeof(Extended sizeof)를 사용할 때 POD의 데이터 맴버를 사용할 수 있는 표준이 추가됐다. 구조체를 하나 정의하고 구조체의 맴버 크기 만큼 배열을 잡고 싶은 경우, 반드시 구조체의 인스턴스 하나가 필요했다. 구조체에 속한 특정 맴버를 지칭하는 foo::a와 같은 표기를 sizeof의 인자로 사용할 경우 컴파일러는 C2070 에러와 함께 컴파일에 실패한다.



<리스트 21> sizeof에 구조체에 속한 특정 맴버를 지칭할 수 없었다. struct foo { int a; int b; }; foo tmp; char foo_a_dump[sizeof(tmp.a)]; char foo_b_dump[sizeof(tmp.b)];

확장된 sizeof 업데이트 덕분에 이제 구조체에 속한 특정 맴버를 지칭해 sizeof를 사용할 수 있게 됐다.



<리스트 22> 확장된 sizeof로 인해 불필요한 객체 생성을 막을 수 있다 char foo_a_dump[sizeof(foo::a)]; char foo_b_dump[sizeof(foo::b)];

이 업데이트를 통해서 불필요한 인스턴스를 생성해야 하는 불편함이 없어지고 코드가 더욱 직관적이게 됐다.



수 분리자

C++ 문법 업데이트 사항 중 복잡하고 이해가 필요한 사항도 더러 있다. 반면 간단한 업데이트로 그간 불편하고 실수할 여지가 많았던 부분을 보완할 수 있는 문법들도 많이 추가됐다. 수 분리자(Digit Seperator)가 대표적이다. 수 분리자는 우리가 은행이나 비용 계산을 할 때 사용하는 콤마를 코드상에서도 사용할 수 있도록 해준다. 아이다(Ada) 언어의 경우 수 분리자를 언더바를 통해 제공한다.



<리스트 23> 아이다 언어에서 수 분리자를 사용하는 모습의 예 My_Savings := My_Savings * 1_000_000.0;

경험이 많은 개발자들은 실수하지 않기 위해 다양한 자신들만의 수 분리자를 흉내내는 경우도 있다. 예컨대 상수의 경우 아래와 같이 define의 ## 트릭을 이용해 수 분리자를 흉내내어 사용할 수도 있다.



<리스트 24> define의 ##을 이용해 수 분리자를 흉내낸 예 define n 1 ## 234 ## 567 int j = n; #undef n

콤마를 사용해 수를 표현하는 게 가장 이상적이다. 그러나 비주얼 C++ 문법의 경우 언더바, 더블언더바 혹은 콤마 등을 함부로 사용할 수 없다. C++ 11 표준에서는 단일 인용 부호(single quotation mark)를 사용해서 수를 표현할 수 있다. 수 분리자가 적용된 수는 값 할당을 비롯해 비교문, 함수의 인자, 선언 등 C++ 문법의 전반에 걸쳐 사용할 수 있다.



<리스트 25> 다양한 수 분리자 실습 int main() { long decval=1'048'576; // 3개의 수를 그룹 지음 long hexval=0x10'0000; // 4개의 수를 그룹 지음 long octval=00'04'00'00'00; // 2개의 수를 그룹 지음 long binval=0b100'000000'000000'000000; cout < < decval < < endl; cout < < hexval < < endl; cout < < octval < < endl; cout < < binval < < endl; return 0; } 결과값: 1048576 1048576 1048576 1048576

웹 환경에서의 실습

C++ 표준의 경우 지속적으로 업데이트되고 비주얼 스튜디오 또한 출시 주기가 빠른 편이기 때문에 프로젝트를 진행하면서 개발 툴을 설치하기가 쉽지 않을 수 있다. 요즘 웹 컴파일러를 제공하는 웹사이트가 많다. 대표적으로 비주얼 C++와 GCC 컴파일러를 실습해 볼 수 있는 좋은 사이트를 추천하며 글을 마친다.

- Visual C++ : webcompiler.cloudapp.net
- GCC : coliru.stacked-crooked.com



출처 : 마이크로소프트웨어 2월호

제공 : 데이터전문가 지식포털 DBguide.net