2014년 1월 15일 수요일

C++11 Feature 정리 - nullptr, Range-for statement, override and final

Code Project의 "Ten C++11 Features Every C++ Developer Should Use"를 기반으로 
C++11 feature들에 대해서 초보적인 시각에서 간단히 정리 함.

http://www.codeproject.com/Articles/570638/Ten-Cplusplus11-Features-Every-Cplusplus-Developer


[nullptr]

일반적으로 macro로 사용하던 NULL을 명시적으로 nullptr로 정의 한것 같다.
이전에 사용하던 macro들의 값은 0 이거나 (void*)0 이었는데
0일 경우는 pointer가 아닌 type에도 사용가능하고  (void*)0일 경우는 C++에서 conversion
 문제가 발생 될 수 있어 
pointer형에만 사용가능한 nullptr를 사용하는 것이 가독성 측면과 불필요한 conversion으로 인한 문제를 피할 수 있을 것 같음.

void foo(int* p) {}

void bar(std::shared_ptr<int> p) {}

int* p1 = NULL;
int* p2 = nullptr;   
if(p1 == p2)
{
}

foo(nullptr);
bar(nullptr);

bool f = nullptr;
int i = nullptr; // error: A native nullptr can only be converted to bool or, using reinterpret_cast, to an integral type

[Range-for statement]

for each 컨셉 문법이고 boost library나 java같은 곳에서는 이미 사용하고 있던 문법이라 이애하기는 쉬울듯.
사용 대상은 array나 iterative한 data structure, begin()/end()를 overloading한 object라면 사용가능하다. (Range-for statement는 array를 사용하듯 T v[N]에 대해 v+N 과 같이 loop을 진행한다.)
이 구문을 실행하기 위해서 컴파일러는 : 다음에 명시된 것에 대해서 iterator를 찾고자 v.begin() / v.end() 아니면 begin(v) / end(v)를 호출하거나 scope 상에서 begin() / end()가 있는지 확인하며 없다면 에러를 리턴한다.


std::map<std::string, std::vector<int>> map;
std::vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
map["one"] = v;

for(const auto& kvp : map) 
{
  std::cout << kvp.first << std::endl;

  for(auto v : kvp.second)
  {
     std::cout << v << std::endl;
  }
}

int arr[] = {1,2,3,4,5};
for(int& e : arr) 
{
  e = e*e;
}



[Override and final]

위 code project article의 저자가 말한 듯이
C++에서의 overriding을 사용할 때 문제가 되는 부분은
 . virtual keyword를 반드시 사용해야 한다는 것과
 . 자식 class에서의 virtual keyword 사용의 optional 인것으로 인한 가독성 문제 때문에 

overriding 될때는 부모 class에서나 자식 class에서나 모두 keyword를 항상 붙이기도 했었음. 
하지만 C++ programming language 책에서 Bjarne Stroustrup 아저씨께서는 virtual keyword를 자식까지 붙이는 것을 추천하지 않으시고 명시적으로 overriding function을 알리려면 'override'를 사용하라고 하고 있음.

위 Article에서는 저자가 virtual keyword의 문제점으로
 . function argument가 다를 경우 overloading으로 처리되어 부모 class 타입에서 overloading된 function을 호출 하여도 호출되지 않는 점과 
 . overriding된 경우에서도 const keyword의 다름으로 호출되지 않는 문제점을 말하고 있음.

* 'override'는
- override된(prototype 같음) virtual function에 대해서 사용 가능
- override는 final과 같이 contextual keyword이다.
 . override를 정의하기 전에 이미 C++로 구현된 수많은 코드들에서 사용되고 있었을 것이므로.. 기존 코드 maintenance 문제로 reserved keyword로 처리하지 못했을 것이다.
- override는 class 내에서만 사용 가능하다.

 class Derived : public Base {
    void f() override;
    void g() override;
 };

void Derived::f() override{  // Error

}

void g(){ // OK

}

- virtual은 prefix이고 override는 suffix이다.
 . void f(int) const noexcept override; // ok
 . override void f(int) const noexcept; // error
 . void f(int) override const noexcept; // error


http://en.cppreference.com/w/cpp/language/override
struct A
{
    virtual void foo();
    void bar();
};
 
struct B : A
{
    void foo() const override; // Error: Has a different signature from A::foo
    void foo() override; // OK: base class contains a virtual function with the same signature
    void bar() override; // Error: B::bar doesn't override because A::bar is not virtual
};


그리고 final은 해당 함수를 더이상 overriding 하지 않겠다는 의미로 사용하면 되지만
virtual 함수 사용 시 발생되는 불필요한 performance overload (Bjarne Storoustrup께서는 25% 정도라고 함.)를 제거하고자 하는 목적으로 사용하기도 함.

사용 법은 override와 마찬가지임. 

class B 
{
public:
   virtual void f(int) {std::cout << "B::f" << std::endl;}
};

class D : public B
{
public:
   virtual void f(int) override final {std::cout << "D::f" << std::endl;}
};

class F : public D
{
public:
   virtual void f(int) override {std::cout << "F::f" << std::endl;}   // error
};



















댓글 없음:

댓글 쓰기