mec++ 3,4

32
항목 9 : 리소스 누수를 피하는 방법의 정공은 소멸자이다

Upload: gyeongwook-choi

Post on 19-Jun-2015

53 views

Category:

Education


1 download

TRANSCRIPT

Page 1: MEC++ 3,4

항목 9 : 리소스 누수를 피하는 방법의 정공은 소멸자이다

Page 2: MEC++ 3,4

예외 상황이 발생하면 진행되던 작업이 중단된다.

이때 동적 할당된 리소스 누수를 막는 좋은 방법은 scope를 이용하는 것이다.

어떤 작업을 하는 동안 생성되는 리소스를 스마트 포인터를 이용해서 관리하면

중간에 예외가 발생하더라도 해당 scope를 벗어나면서 스마트 포인터들이 해제되므로 동적 할당 된 리소스들도 해제된다.

Page 3: MEC++ 3,4

항목 10 : 생성자에서는 리소스 누수가 일어나지 않게 하자

Page 4: MEC++ 3,4

C++는 생성이 정상적으로 완료된 객체에 대해서만 소멸자 호출을 하므로

생성자 단계에서 예외가 발생해서 제대로 생성이 되지 않으면 제대로 해제되지 않으므로 프로그래머가 직접 관리해주어야 한다.

이를 관리하는 방법으로는 생성하는 단계에서 발생하는 모든 예외를 catch해서 모든 리소스들을 직접해제하고,

받은 예외를 다시 throw하는 방법이다.

하지만 상수 포인터를 사용할 때는 try-catch라는 문장을 쓸 수 없고 단지 표현식만 사용할 수 있으므로,

try-catch를 각각의 상수 포인터 생성단계를 감싸는 형태로 배치해서 관리해야 한다.

더 쉬운 방법은 역시 스마트 포인터를 이용해서 관리하는 것이다.

Page 5: MEC++ 3,4

항목 11 : 소멸자에서는 예외가 탈출하지 못하게 하자

Page 6: MEC++ 3,4

C++에서 소멸자가 불리는 것은 두 가지 경우

1. 객체가 유효범위를 벗어나거나 직접 delete를 통해 삭제할 때

2. 예외 전파 과정에서 스택 되감기 과정

소멸자가 불릴 때 두 가지 경우 중 어떤 상황인지 알 수 없다.

문제는 예외 상황 때문에 소멸자가 실행되는 과정에 다시 예외가 발생하면 C++은 프로그램을 종료하는 terminate를 호출하는 점

그리고 그렇다는 것은 진행되던 예외 처리가 정상적으로 종료될 수 없다는 의미

이 문제를 막기 위해서는 소멸자에서 예외가 발생하면 빠져나가지 못하게 try-catch를 이용해서 묶어두고

해당 소멸자의 작업을 완전히 끝내도록 해야한다.

Page 7: MEC++ 3,4

항목 12 : 예외 발생이 매개변수 전달 혹은 가상 함수 호출과 어떻게 다른지를 이해하자

Page 8: MEC++ 3,4

예외 객체를 전달하는 것과 함수 매개변수를 전달하는 것은 비슷해 보이지만 차이점이 존재한다.

1. 예외는 무조건 객체 복사가 한 번은 일어난다.

- 예외의 처리는 해당 scope를 벗어나서 일어나므로 scope를 벗어나서도 같은 데이터를 유지하기 위해서는 참조로는 불가능하므로

- 그래서 예외가 전달되면 일단 복사가 한 번 일어나고, 그 다음 전달 방식에 따라서 추가적인 복사가 일어난다(값에 의한 전달)

2. 매개변수와 달리 타입변환의 가지 수가 적다.

- 예외 전달 과정에서 일어나는 타입 변환에는 우선 상속관계에 의한 변환이 있다. 하위 예외는 상위 예외로 타입 변환이 될 수 있다.

- 포인터의 경우 어떤 포인터이든 void*로 타입 변환 될 수 있다.

3. catch의 실행은 가상 함수와 달리 등장 순서에 따라 실행

- 해당 예외에 가장 적합한 catch문이 실행되는 것이 아니라 코드에 적힌 순서대로 실행된다.

- 그러므로 더 좁은 영역을 처리하는 catch문에 앞에 있어야 한다.

Page 9: MEC++ 3,4

항목 13 : 발생한 예외는 참조자로 받아내자

Page 10: MEC++ 3,4

예외를 전달하는 방법에 있어서 포인터, 값, 참조에 의한 전달 세 가지 경우를 고려할 수 있다.

1. 포인터에 의한 전달

- 예외 객체를 해제할 지 두어야 할 지 판단하기 힘들다

- C++의 표준 예외들은 객체이므로 이들을 사용할 수 없다

2. 값에 의한 전달

- 항상 두 번씩 복사된다.

- sliced off 가 발생할 수 있다.

3. 참조에 의한 전달

- 위의 문제들에서 자유롭다.

Page 11: MEC++ 3,4

항목 14 : 예외 지정 기능은 냉철하게 사용하자

Page 12: MEC++ 3,4

예외를 지정하는 것에는 몇 가지 장점이 있다.

컴파일 단계에서 잘못 지정된 예외를 검출할 수 있고, 함수가 예외 지정 리스트에 없는 예외를 발생시키면

런타임 단계에서 unexpected 함수를 호출해준다.

하지만 컴파일러가 잘못된 예외 지정을 완벽하게 할 수는 없고, 예기치 않은 예외가 발생할 수도 있다

그러므로 장단점을 파악하고 적절하게 써야 한다.

Page 13: MEC++ 3,4

항목 15 : 예외 처리에 드는 비용에 대해 정확히 파악하자

Page 14: MEC++ 3,4

try-catch를 사용하는데 필요한 비용이 분명 존재한다.

비록 하나도 사용하지 않더라도 link되는 다른 obj 파일들 중에 사용한 것이 하나라도 있다면 이 비용은 지불해야 한다.

컴파일러들은 이 기능을 사용하는 것에 대한 옵션을 제공하고 있다.

그러므로 꼭 필요한 곳에만 사용하고,

성능에 민감하다면 프로파일러를 이용해서 사용 여부 혹은 더 효율적인 컴파일러를 찾는 것이 좋다.

Page 15: MEC++ 3,4

항목 16 : 뼛속까지 잊지 말자, 80-20 법칙

Page 16: MEC++ 3,4

실제 프로그램의 성능에 영향을 주는 것은 전체 코드의 일부분이다.

그러므로 성능 향상을 위해서는 이 부분의 개선이 필요하고, 이 부분이 어디인지 판단할 수 있는 것이 중요하다.

다양한 프로파일러를 이용해서 판단할 수 있다.

주의할 점은 프로파일러를 사용할 때 실제 프로그램이 많이 사용되는 조건(데이터)를 이용하는 것이다.

Page 17: MEC++ 3,4

항목 17 : 효율 향상에 있어 지연 평가는 충분히 고려해 볼 만하다

Page 18: MEC++ 3,4

이 개념의 핵심은 당장 필요하지 않은 연산은 최대한 미루었다가, 연산이 필요해지면 필요한 것만 한다는 것이다.

하지만 항상 이 방법이 좋은 것은 아니다.

지연 평가를 위해 유지해야 할 자료 구조들이 있을 수 있고, 즉시 값들이 필요한 경우가 많을 때는 추가적인 성능 비용이 필요하게 된다.

Page 19: MEC++ 3,4

항목 18 : 예상되는 계산 결과를 미리 준비하면 처리비용을 깎을 수 있다

Page 20: MEC++ 3,4

앞에서 본 지연 평가와 반대로 미리 계산을 해두고 그 결과를 이용해서 바로 응답할 수 있게 하는 것이다.

일종의 캐시 개념으로 이해할 수 있는 것으로써 지연 평가와 마찬가지로 모든 경우에서 좋은 결과를 얻는 것은 아니다.

지연 평가와 같이 불필요한 연산은 최대한 줄이자 라는 기본 개념은 비슷하다.

다만 유용하게 사용되는 상황이 지연 평가와는 반대이다.

Page 21: MEC++ 3,4

항목 19 : 임시 객체의 원류를 정확히 이해하자

Page 22: MEC++ 3,4

C++에서 임시 객체에 대한 비용은 적지 않다. 하지만 이를 없애기는 어렵다.

대신 이 임시 객체들이 생성되는 상황을 잘 이해하고 가능한 임시 객체가 생성되지 않도록 하는 것이 중요하다.

예를 들어 매개 변수로 상수 참조자를 사용하면 원래의 데이터가 바뀌면 안 되므로 임시 객체가 생성되지만,

비상수 참조자는 임시 객체 없이 사용된다.

Page 23: MEC++ 3,4

항목 20 : 반환값 최적화가 가능하게 하자

Page 24: MEC++ 3,4

함수에서 반환값을 리턴할 때 생성자인자를 반환하면 컴파일러가 최적화해 줄 확률이 높다.

즉 임시 객체를 생성하지 않고 결과값이 저장되는 곳에 직접 생성자에 의해 생성되는 데이터가 저장된다.

Page 25: MEC++ 3,4

항목 21 : 오버로딩은 불필요한 암시적 타입변환을 막는 한 방법이다

Page 26: MEC++ 3,4

암시적 타입변환이 일어나면 타입 변환한 결과로 임시 객체가 생긴다.

이것도 비용이므로 모든 암시적 타입 변환의 가능성이 있는 연산을 오버로딩 해두면 이런 비용을 줄일 수 있다.

Page 27: MEC++ 3,4

항목 22 : 단독 연산자 대신에 =이 붙은 연산자를 사용하는 것이 좋을 때가 있다

Page 28: MEC++ 3,4

일반적으로 =+ 연산은 임시객체가 필요 없고

+는 =+를 호출함으로써 구현되는 과정에서 임시 객체를 필요로 하게 되므로 성능에 차이가 있다.

Page 29: MEC++ 3,4

항목 23 : 정 안 되면 다른 라이브러리를 사용하자

Page 30: MEC++ 3,4

라이브러리마다 중요하게 생각하는 가치가 다르기 때문에

성능이라는 관점에서 보면 비슷한 기능을 하는 라이브러리 사이에서도 성능 차이가 존재한다.

프로젝트의 성격에 맞춰서 적합한 라이브러리를 사용하는 것이 좋다.

Page 31: MEC++ 3,4

항목 24 : 가상 함수, 다중 상속, 가상 기본 클래스, RTTI에 들어가는 비용을 제대로 파악하자

Page 32: MEC++ 3,4

가상 함수

- 가상 테이블 때문에 클래스의 크기가 증가

- 가상 테이블 포인터 때문에 객체의 크기도 증가

- 가상 함수가 작동하는 것은 런타임, 컴파일 타임에 결정되는 인라인은 사용할 수 없다

다중 상속

- 가상 테이블과 가상 테이블 포인터 모두 더 필요하므로 클래스 객체 모두 크기 증가

가상 기본 클래스

- 데이터 멤버의 중복을 피하기 위해서 가상 기본 클래스 부분에 대한 포인터가 필요하게 되므로 크기 증가

RTTI (runtime type identification)

- 런타임 중에 객체의 클래스 정보를 얻기 위한 정보를 저장해 두어야 하는데, 클래스 안에만 저장하면 되므로 클래스 크기 증가