2018년 5월 17일 목요일

C++ 11/14 관련 내용 대충 정리, 형식시스템

최신 C++을 공부하려 책을 보긴 했었는데
내용이 기존 문법과 함께 여기저기 흩어져 있어 보기에 좀 어려워
MSDN을 참고하여 대충 정리하려함.
역시 설명은 MSDN이 갑이다..

C++11/14/17 기능에 대한 지원(최신 C++)
: https://msdn.microsoft.com/ko-kr/library/hh567368.aspx


C++ 형식 시스템

https://msdn.microsoft.com/ko-kr/library/hh279663.aspx

이제 auto를 사용하자.

값형식

: https://msdn.microsoft.com/ko-kr/library/hh438479.aspx

Value vs. reference types


* copy by value, copy by reference에 대한 개념 때문인지 내용이 어색 하기는 함.

- 본문에서 C++ class는 value type이지만 객체지향(OOP)를 지원하고 다형성 동작을 가능하도록 reference type으로 지정할 수도 있음.
- Value type은 메모리, 레이아웃 제어와 관련이 있고 reference type은 다형성을 위한 base class와 가상함수(virtual functions)와 관련이 있음.
- Value type은 copyable (복사생성자-copy constructor, 복사연산자copy assignment operator 활성화)하지만 Reference type은 non-copyable(복사생성자, 복사연산자 비활성화)하고 다형성을 지원하는 가상 소멸자(virtual destructor)를 사용함.
- Value type은 위의 특성 상 복사가능하고 각기 독립적인 값을 제공하는 contents 용도로 주로 사용되고 reference type은 다형성의 측면에서 object의 identity를 나타내며 polymorphic type이라고도 불린다. (??)

아래는 reference type의 예

class MyRefType {  
private:  
    MyRefType & operator=(const MyRefType &);  
    MyRefType(const MyRefType &);  
public:  
    MyRefType () {}  
};  
  
int main()  
{  
    MyRefType Data1, Data2;  
    // ...  
    Data1 = Data2;  
}  

: 복사생성자와 복사연산자를 private로 지정하여 복사할 수 없게 만들었음.

Value types and move efficiency


새로운 복사 최적화로 인해 복사 할당 오버헤드가 방지됨.

컴파일러의 copy allocation overhead 방지를 위해 이동 생성자를 생성 될 수 있는데 적절한 이동을 위해서는 사용자가 직접 &&를 사용하여 이동 생성자와 이동 연산자를 정의해야 한다.
복사 생성자를 사용할 필요가 있다면 이동 생성자를 정의하는 것이 deep copy보다 효율적임. 또한 이동을 고려하되 복사를 방지하고자 할 경우 unique_ptr를 사용하는 것도 방법임.
이동 문법은 함수에서 값을 리턴하거나 container에 삽입하는 상황에서 사용 될 수 있어 복사보다 효율적임.

 
#include <set>  
#include <vector>  
#include <string>  
using namespace std;  
  
//...  
set<widget> LoadHugeData() {  
    set<widget> ret;  
    // ... load data from disk and populate ret  
    return ret;  
}  
//...  
widgets = LoadHugeData();   // efficient, no deep copy  
  
vector<string> v = IfIHadAMillionStrings();  
v.insert( begin(v)+v.size()/2, "scott" );   // efficient, no deep copy-shuffle  
v.insert( begin(v)+v.size()/2, "Andrei" );  // (just 1M ptr/len assignments)  
//...  
HugeMatrix operator+(const HugeMatrix& , const HugeMatrix& );  
HugeMatrix operator+(const HugeMatrix& ,       HugeMatrix&&);  
HugeMatrix operator+(      HugeMatrix&&, const HugeMatrix& );  
HugeMatrix operator+(      HugeMatrix&&,       HugeMatrix&&);  
//...  
hm5 = hm1+hm2+hm3+hm4+hm5;   // efficient, no extra copies  

value-like class에서도 deep copy 보다 move를 사용하는 것이 효율적일 수 있고 이동 생성자, 이동 연산자를 생성하여 적절히 이동 될 수 있도록 할 수 있다.

#include <memory>  
#include <stdexcept>  
using namespace std;  
// ...  
class my_class {  
    unique_ptr<BigHugeData> data;  
public:  
    my_class( my_class&& other )   // move construction  
        : data( move( other.data ) ) { }  
    my_class& operator=( my_class&& other )   // move assignment  
    { data = move( other.data ); return *this; }  
    // ...  
    void method() {   // check (if appropriate)  
        if( !data )   
            throw std::runtime_error("RUNTIME ERROR: Insufficient resources!");  
    }  
};  
  



형식 변환 및 형식 안정성


https://msdn.microsoft.com/ko-kr/library/hh279667.aspx

암시적 형식 변환(Implicit conversion)


당연하겠지만 확대 변환은 데이터의 손실, 축소 변환은 데이터 손실 발생 가능.


명시적 변환(Explicit conversion, casts)


아래는 C 스타일 캐스트 연산자임.

(int) x; // old-style cast, old-style syntax  
int(x); // old-style cast, functional syntax  

왠만하면 static_cast, dynamic_cast, const_cast, reinterpret_cast 를 사용하자.

static_cast : 컴파일 시점에서 문제 없을 경우 캐스팅

double d = 1.58947;  
int i = d;  // warning C4244 possible loss of data  
int j = static_cast<int>(d);       // No warning.  
string s = static_cast<string>(d); // Error C2440:cannot convert from  
                                   // double to std:string  

// No error but not necessarily safe.  
Base* b = new Base();  
Derived* d2 = static_cast<Derived*>(b);  

dynamic_cast : 런타임시 기본 타입에서 파생 타입으로 캐스팅, 일부 오버헤드 있음.

Base* b = new Base();  

// Run-time check to determine whether b is actually a Derived*  
Derived* d3 = dynamic_cast<Derived*>(b);  

// If b was originally a Derived*, then d3 is a valid pointer.  
if(d3)  
{  
   // Safe to call Derived method.  
   cout << d3->DoSomethingMore() << endl;  
}  
else  
{  
   // Run-time check failed.  
   cout << "d3 is null" << endl;  
}  

//Output: d3 is null;  

const_cast : const<->비const로 캐스팅, 근데 사용하는 건 좀..

void Func(double& d) { ... }  
void ConstCast()  
{  
   const double pi = 3.14;  
   Func(const_cast<double&>(pi)); //No error.  
}  

reinterpret_cast : 그냥 캐스팅
아래는 관련 캐스팅의 차이

const char* str = "hello";  
int i = static_cast<int>(str);//error C2440: 'static_cast' : cannot  
                              // convert from 'const char *' to 'int'  
int j = (int)str; // C-style cast. Did the programmer really intend  
                  // to do this?  
int k = reinterpret_cast<int>(str);// Programming intent is clear.  
                                   // However, it is not 64-bit safe.  









댓글 없음:

댓글 쓰기