posted by REDFORCE 2017. 3. 23. 07:30

목차


1. Auto

2. range based for

3. enum class

4. non-static data member initializers

5. initializer lists

6. default / delete definition

7. override / final

8. emplacement

9. constexpr

10. lambda

11. random

12. thread



2. emplacement


이번 글은 emplacement에 대해서 정리해보도록 하겠습니다.


emplacement를 간단히 설명해보자면


  • 오브젝트 생성과 컨테이너 추가를 한 번에 할 수 있다.
  • STL의 대부분의 컨테이너에서 지원
    • 요소의 생성자 인수를 받아서 컨테이너 내에서 오브젝트를 만든다.
    • push_back() -> emplace_back()
    • push_front() -> emplace_front()
    • insert() -> emplace()

  • push_back 에 비해 요소 추가 비용을 줄일 수 있다.
    • 임시 오브젝트의 복사와 파괴 비용이 발생하지 않는다.
const string str = "Hello World";
// push_back
std::vector<std::string> vStr;
vStr.push_back(str);
vStr.push_back({str[0], 'e'});
// emplacement
vStr.emplace_back("Hello~?");
vStr.emplace_back(str);
vStr.emplace_back(10, 'a');
view raw emplacement.cpp hosted with ❤ by GitHub


보시다시피 사용 방법은 기존 push_back 이나 insert와 동일하다.


다만 push_back이나 insert의 경우 내부적으로 값 복사가 발생하여 비용이 들었으나


emplace_back이나 emplace를 이용하면 값 복사 현상이 없어져서 더 적은 비용으로 기능을 수행할 수 있다!




어..나는 값 복사가 일어나는게 필요한대?


라고 한다면 기존 push_back이나 insert를 쓰면 된다.


posted by REDFORCE 2017. 3. 23. 06:51

목차


1. Auto

2. range based for

3. enum class

4. non-static data member initializers

5. initializer lists

6. default / delete definition

7. override / final

8. emplacement

9. constexpr

10. lambda

11. random

12. thread


이번 글에서는 [7편] override / final 에 대해서 살펴보도록 하겠습니다.


사실 override와 final은 매우 쉬운거라 딱히 한개의 글로 적기 뭐했습니다만..


블로그 분량을 늘려서 빨리 구글에 노출되야해!!!

라는 것 때문에 글을 나눈 것은 아닙니다.


7. override / final

  • override : 컴파일러에게 부모클래스의 멤버 함수를 재 정의함을 알린다.

  • final : 부모 클래스의 특정 멤버 함수를 자식 클래스에서 재정의하지 못하도록 막을 때 사용 한다.


오버라이드는 간단히 부모 클래스에 선언 된 함수를 재정의 할 때나


이미 부모클래스에 있는 함수 다시 재정의 해본다아~~? 하고 절차를 확인할 때 사용 할 수 있습니다.


struct Base
{
virtual void foo( int i );
};
struct Derived : Base
{
virtual void foo(int i) override;
//virtual void foo(float i) override; << Compile Error!!
}


위 Base에 선언 된 가상함수 foo(int i) 를 override 한 것은 잘 되지만

아래 파라미터가 float 형인 것으로 override를 시도하면 컴파일 에러가 발생하게 됩니다.


개인적으로는 override를 이용하여 내가 부모클래스에 만들어 뒀던 가상함수를 


다시 재정의하여 쓸 때 오타가 없는지 확인 하는 용도로도 썼었습니다.



간단한 예로 이렇게 활용했지요~!


Base Class 에 virtual void Update( float timeDelta);  라 만들어진 것을    // Update 가 대문자로 시작

Derived Class 에 virtual void update (float timeDelta) override;  라고 적으면 // update 가 소문자로 시작


이런식으로 적으면 바로 컴파일러가 update와 Update를 구분하여 너 철자 틀렸심. 하고 빨간줄을 그어줍니다.




두 번째 Final은 반대로 final이 선언 된 클래스를 끝으로 더이상 자식한테 재정의를 허용 하지 않을 때 사용합니다.


struct Base
{
virtual void foo(int i) final;
};
struct Derived : Base
{
//virtual void foo( int i ); // Compile Error!!
}


개인적으로는 상속을 하면서 자식으로 뻗어 나갈 때, 이 함수는 더 이상 재정의하면서 계속 썼다간


원 목적에서 완전 틀어저 버릴 거 같아...라는 생각이 들거나


팀 프로젝트에서 내 코드임 재정의하거나 건들지마셈 하고 명시할 때 붙이기도 했습니다.


posted by REDFORCE 2017. 3. 22. 23:09

목차


1. Auto

2. range based for

3. enum class

4. non-static data member initializers

5. initializer lists

6. default / delete definition

7. override / final

8. emplacement

9. constexpr

10. lambda

11. random

12. thread


이번 글에서는 [6편] dafault / delete definition 에 대해서 살펴보도록 하겠습니다.


6. default / delete


default : 컴파일러가 함수를 자동으로 생성하도록 명시적으로 지정

delete : 컴파일러가 함수를 자동으로 생성하지 않도록 명시적으로 지정


class TEST
{
public:
TEST() = default;
TEST(const TEST&) = default;
~TEST() = default;
TEST& operator= (const TEST&) = default;
};
class TEST2
{
public:
TEST2(const TEST2&) = delete;
TEST2 operator= (const TEST2&) = delete;
void* operator new(size_t) = delete; //blocking ( new operator )
};

위의 코드를 살펴보면 class TEST의 생성자와 소멸자는 default로 명시를 하는 것을 볼 수 있습니다.

default로 명시가 되었기 때문에 class TEST의 내용은 컴파일러가 함수를 자동으로 생성하게 됩니다.




반대로 class TEST2는 delete로 함수들이 명시적으로 지정되어 있습니다.

그리고 생성자와 void* operator new(size_t) = delete; 가 정의되어 있는 관계로

new 연산자를 이용한 할당이 블로킹 되어 있습니다.


두 번째 예로 다음 코드를 보도록 하겠습니다.


struct TEST
{
void func(int i) { std::cout << i << std::endl; }
void func(double d) = delete;
}
int main()
{
int i = 11;
TEST test;
test.func(i);
double d = 11.0;
test.func(d); // <<<<< Error!!
return 0;
}


간단히 struct 안에 func(double d) 함수 선언하고 delete로 지정을 하였습니다.

그리고 main에서 func( double d )  함수를 사용하려고 했지만!


Error가 빰빠카밤~ 하고 나오는걸 볼 수 있으실 겁니다.



아그럼 default는 자동으로 생성하는데 뭘해준다는거야?


라고 궁금해 하실까봐.

(사실 아 그냥 대충써... 하고 귀차니즘 발동하려다가 더 보충해봅니다..)


이번엔 default에 대한 예를 적어 보겠습니다.


class MyTest
{
public:
MyTest() { }
private:
MyTest(const MyTest&);
};
int main()
{
MyTest mt;
return 0;
}


위 코드를 보면 MyTest 클래스에서 기본적인 MyTest() 생성자가 기본으로 있습니다.

보통 기본 생성자는 우리가 따로 선언/정의를 해주지 않아도 컴파일러가 몰래 쓰담쓰담 만드는 게 상식입니다.


그렇다면 아래와 같이 코드를 바꾸면 어떻게 될까요?


class MyTest
{
private:
MyTest(const MyTest&);
};
int main()
{
// Compile error : C2512: 'MyTest'
MyTest mt; //Where is basic construct!!?
}


당연히 기본 생성자를 직접 선언도 안했을 뿐더러 다른 생성자를 만들었기 때문에

기본 생성자가 만들어지지 않습니다.


따라서 당연히 컴파일 에러가 빠밤~ 하고 나오게 되조.


이때 편하게 쓰는게 바로 default 입니다.


class MyTest
{
public:
MyTest() = default;
private:
MyTest(const MyTest&);
};
int main
{
MyTest mt;
}


default로 기본 생성자를 명시해줬기 때문에 딱히 정의를 안하더라도 컴파일러가 자동으로 생성해주게 됩니다.

이런식으로 활용하는 것이 default 입니다~



딱히 default 자체는 많이 쓸일이 없을 것 같습니다만.


delete는 유용하게 활용이 가능하겠더군요.



그럼 다음 7편에 override와 final에 대해서 뵙도록 하겠습니다~