8월 30, 2016

[EC++ 13] Use objects to manage resources.

Resource 를 object 안에 넣고 이 object의 dtor가 resource를 해제하도록 하여, 사용이 끝난 Resource의 해제를 보장할 수 있다.


auto_ptr

  • Resource를 획득하여 바로 resource-managing objects로 넘김
  • Resource Acquisition Is Initialization(RAII) objects를 사용하여 Resources 를 관리하는 것.
  • Resource-managing objects는 resource의 해제를 보장하기 위해 dtor를 사용

단, auto_ptr 은 소유권을 공유하지 않기 때문에 auto_ptr a에서 auto_ptr b로 복사시 a는 NULL이 되어버림.


Reference-Counting Smart Pointer (RCSP)

배타적인 소유권을 갖는 auto_ptr과 달리 shared_ptr은 소유권을 공유할 수 있다. 클래스가 소유권을 공유하는 객체들의 수를 기록하는 reference counter를 멤버로 가지고 있어서, 소유권을 공유하는 객체들의 숫자가 0이 되면 resource가 해제된다.

단, RCSP는 cycles of references(ex.객체가 서로를 가르키고 있는 경우)에 빠지는 경우 제대로 동작하지 않는다는 단점이 있다.


또 다른 유의점

std::auto_ptr 과 shared_ptr 의 dtor는 (delete[] 가 아니라 delete 를 호출한다. 따라서 이들 객체가 관리하는 resource가 배열인 경우 올바르게 작동하지 않는다. 이런 경우 custom deleter를 지정하여 resource가 적절한 방식으로 해제될 수 있도록 신경써야 한다.


Summary

  • To prevent resource leaks, use RAII objects that acquire resources in their constructors and release them in their destructors.
  • Two commonly useful RAII classes are tr1::shared_ptr and auto_ptr. tr1::shared_ptr is usually the better choice, because its behavior when copied is intuitive. Copying an auto_ptr sets it to null.

Reference

  • Effective C++ by Scott Meyers

8월 25, 2016

[EC++ 12] Copy all parts of an object.

Objective

  • copy all local data member
  • invoke the appropriate copying function in all base classes

잘 정의된 object-oriented system은 copy constructor, copy assignment operator 오직 두가지의 func.을 통해서만 객체 복사를 허용한다. 이 두 함수를 직접 정의하지 않는 경우 컴파일러가 default 함수를 대신 정의해주지만, 필요한 경우 사용자가 두 함수를 직접 정의할 수도 있다. 그러나 이 경우 새로운 data member가 클래스에 추가될 때는 copy funcions에도 반드시 이를 반영해야 한다. 만약 사용자가 실수로 추가된 data member의 복사를 빼놓더라도 컴파일러에선 아무런 경고를 해주지 않는다.

새로운 data member가 클래스에 추가되었을 때
  1. 모든 copying functions update
  2. 모든 ctor update
실수로 update를 하지않더라도 컴파일러는 어떤 경고도 하지 않으므로 주의.
derived class의 copy functions을 구현하는 경우에는 base class 부분도 반드시 고려를 해야한다. 따라서, derived class의 copy functions 안에서 base class의 copy functions을 반드시 호출해줘야 한다.


copy functions 간의 중복 코드 제거

한 copy function에서 다른 copy function을 호출하는 것은 피해야 한다. 특정 상황에서 객체가 오염될 수 있으며 논리적으로도 맞지 않기 때문이다. 꼭 중복 코드를 제거하고 싶다면 copy functions 안에서 공통적으로 호출되는 init(..) 과 같은 별개의 함수를 private으로 정의하는 것이 좋은 방법이다.



Summary

  • Copying functions should be sure to copy all of an object's data members and all of its base class parts.
  • Don't try implement one of the copying functions in terms of the other. Instead, put common functionality in a third function that both call.

Reference

  • Effective C++ by Scott Meyers

8월 24, 2016

[EC++ 11] Handle assignment to self in operator=.

Objective

  1. self-assignment-safe
  2. exception-safe

self-assignment problem

객체가 자기자신에게 assignment를 할 때 self-assignment가 발생. operator= 구현 과정에서 dest. 객체가 유지하고 있던 resource를 해제하고 source. 객체의 resource를 복사한다. 그런데 self-assignment인 경우, dest. 객체와 source. 객체가 동일하다. 따라서 dest. 객체의 resource를 해제하면 source. 객체의 resource 역시 해제되어 아무 것도 남지 않게되서 operation이 의도와 다르게 작동하게되는데, 이를 self-assignment problem이라 한다.

self-assignment problem은 dest. 객체와 source. 객체가 같은 객체인지 비교하는 과정을 삽입함으로써 해소할 수 있다.


exception-safe

assignment operation 과정에서 dest.객체의 resource를 해제한 뒤, 새로운 resource를 (복사)할당 받아 dest.의 resource로 넘겨주는데, 할당 과정에서 exception이 발생할 경우 operation이 의도한대로 작동하지 않을 수 있다. 이런 경우 statement의 순서를 조정하여 exception에 보다 견고한 동시에 self-assignment problem 에서도 안전한 코드를 만들 수 있으며 크게 3가지 방식으로 이를 구현한다.

  • menually ordering statements.
  • "copy and swap" technique
  • variation of "copy and swap" technique

Summary

  • Make sure of operator= is well-behaved when an object is assigned to itself. Techniques include comparing addresses of source and target objects, careful staterment ordering, and copy-and-swap.
  • Make sure that any function operating on more than one object behaves correctly if two or more of the objects are the same.

Reference

  • Effective C++ by Scott Meyers

8월 23, 2016

8월 22, 2016

[EC++ 09] Never call virtual functions during construction or destruction.

  • virtual function 뿐만 아니라 dynamic_casttypeid 같은 language의 RTTI(runtime type information) 파트에서도 Derived object 의 base class construction 중에는 그 object의 타입이 base class 인 것으로 인식.
  • Derived object의 base class member 부분을 초기화하는 동안, 이 객체는 base class 객체인 것으로 인식됨. Derived object 의 derived class member 부분이 초기화 되기 시작한 후에야 이 객체는 derived class 로 인식됨.

  • ctor 내에서 virtual func.을 직접 부르는 것뿐만 아니라, init()과 같은 함수를 통해 간접적으로 부르는 것도 마찬가지 이유로 불가.

해결책

  • 유일한 해결책은 ctor 나 dtor에서 virtual func. 을 부르지 않는 것.
  • 다른 방법으로 해당 func. 을 virtual 대신 일반 func.으로 구현하고, construction 과정에서 필요한 정보를 ctor의 param.로 넘겨주는 방법을 사용.
  • 이 때, param. 를 넘겨주는 helper func.을 (private) static func.으로 구현하면 좋음. func. 을 static으로 구현함으로써 helper func. 내에서 아직 initialize 되지 않은 data member 를 부르는 실수를 막을 수 있음.


Summary

  • Don't call virtual functions during construction or destruction, because such calls will never go to a more derived class than that of the currently executing constructor or destructor.

Reference

  • Effective C++ by Scott Meyers

8월 18, 2016

[EC++ 08] Prevent exceptions from leaving destructors.


C++은 최대 하나의 exception만 처리할 수 있음. 만약 두개 이상의 active exception이 동시에 발생하면 당시의 program 상황에 따라 premature program termination 또는 undefined behavior를 일으킴. dtor가 exception을 처리하지 않고 생략하면, dtor 과정에서 예외가 동시에 발생한 경우 premature program termination이나 undefined behavior를 일으킬 수 있음. dtor가 exception을 생략하는 것은 프로그램 실행 중 언제든 premature program termination이나 undefined behavior가 발생할 수 있어 위험함.

dtor에서 예외를 다루는 방법

  1. Terminate the program
  2. Swallow the exception
  3. Better strategy: 관련된 interface를 따로 정의하여 client가 발생가능한 문제에 대해 직접 대처할 수 있게 함.
  4. ex) DBConn::close() member method와 bool closed member field 를 정의.
    dtor가 수행하던 작업을 non-dtor function에게 이관하여 exception이 dtor에서 발생하는 것을 피할 수 있음. class 의 책임을 client에게 떠넘기는 것이라 볼 수도 있지만, 이런 방법을 통해 client에게 더 넓은 선택권을 제공할 수 있기에 좋은 방법임. client는 operation을 직접 수행하고 예외까지 직접 처리하여 더 안정된 프로그램을 구현할 수도 있고, operation을 dtor에게 맡기는 대신 다소간 프로그램의 불안정성을 감수하는 방법을 선택할 수도 있음.


Summary

  • Destructors should never emit exceptions. If functions called in a destructor may throw, the destructor should catch any exceptions, then swallow them or termainate the program.
  • If class clients need to be able to react to exceptions thrown during an operation, the class should provide a regular (i.e., non-destuctor) function that performs the operation.

Reference

  • Effective C++ by Scott Meyers

8월 08, 2016

char * 타입으로 가르키는 string

char * str = "Hello!"
소스 코드 상에 위와 같이 표현된 string은 string의 내용(character)을 바꿀 수 없음.

위와 같이 표현된 string은 complile time 때 이에 대해 미리 알고 있어야하기 때문에, string이 executable 안에 직접 쓰여짐. Executable 안에 기록된 data는 읽기 전용이기 때문에 수정/변경될 수가 없으며, 수정하고 싶은 data는 executable이 아닌 stack memory나 heap memory에 기록해야 함. 따라서 char str[] = "string" 과 같이 local varialble로 char array를 선언해서 여기에 string을 기록하거나, malloc으로 heap memory 에 공간을 할당하고 여기에 string을 복사해서 사용해야 함.



Reference