5. 그런데 코드도 복잡해지고
정상과 예외상황 둘다 delete를 써줘야함
유지보수는 힘들어짐
-> 스마트 포인터를 쓰면 쉽게 문제 해결
6. 생성자에서 자원을 만들고
소멸자에서 해제하는 역할을 하는 더미 클래스를 만들면
예외 발생으로 리소스가 새는 일이 방지된다.
!
그런데 정작 저 생성자 소멸자에서 문제가 발생하면?
7. 생성자에서는 리소스 누수가 일어나지 않게 하자.
생성자에서 예외가 발생해서 정상 생성되지 못한 객체는
소멸자가 호출이 되지도 않는다.
!
C++에서 책임안짐 프로그래머가 알아서 해야함
8. 간단한 방법
또Try catch를 써서 예외를 처리해주자.
!
하지만 또 코드 복잡해지고 중복생기고 유지복수 힘들어짐…
!
!
!
스마트 포인터를 활용해보자.
!
->
!
누수 걱정 없고, 소멸자에서 직접 리소스해제하지 않아도 되고,
상수 멤버 포인터도 비상수 포인터와 똑같이 처리 가능
9. 소멸자에서는 예외가 탈출하지 못하게 하자.
소멸자는 정상적으로 객체 유효범위를 벗어날때와
객체가 직접 삭제될 때와
!
예외처리 방법에 의해 객체가 소멸되는 것인데
소멸자 안에서 어떤 방법으로 소멸되려는 건지 알수가 없음
이라는데 이제는 uncaught_exception이라는
함수로 가능하다고 함
10. 예외가 소멸자에서 처리되지 않고 다른곳으로 전파되 버리면
소멸자는 실행을 마치지 못한 상태로 있게된다.
도중에 terminate가 호출될수도 있다.
!
-> try catch로 예외를 묶어두자.
11. 예외 발생이 매개변수 전달 혹은
가상 함수 호출과 어떻게 다른지를 이해하자.
예외를 참조로 받든(catch by reference) 값으로 받던(catch by value)
예외로 발생되는 객체에 대해 사본을 만든다.
!
아니면 유효범위를 넘는순간 소멸되 버리니…
!
-> 복사 속도…
18. 예외 지정 (exception specification) 기능은 냉철하게 사용하자
예외 지정을 하면 어떤 함수가 어떤 예외를 발생시키는지 보임
예외 지정에 일관성이 없으면 컴파일 ‘워닝’도 발생시켜줌
게다가 예외 지정 리스트에 없는 예외를 발생시키면
unexpected함수도 자동 호출해준다.
!
근데 unexpected는 terminate를 호출…
19. 템플릿에는 예외 지정하기가 무의미
-> 템플릿이 받는 매개변수는 엄청 많아서
!
예외 지정이 안 된 함수를 호출할 가능성을 가진 함수는
예외 지정을 두지말자. (특히 콜백)
20. 예외지정은?
!
예외지정의 일치성 검사는 부분만 해줌
예외지정 어기면 Unexpected->terminate
예외처리 코드가 있어도 예기치 않은 문제가 발생할 수 있음
!
정말 쓸건지 다시 한번 생각해보자.
21. 예외 처리에 드는 비용에 대해 정확히 파악하자
try catch를 할때 try없애고 catch를 찾고 들어가고…
예외 지정 조건 한번 찾고… 등등등
사실 다 비용이 들어가는 일
22. 예외처리를 하려고 하면?
!
어떤 객체가 생성 과정을 완료하였는지 체크하는 자료구조 메모리와
이 자료구조를 업데이트하는 시간이 필요
예외 처리를 안쓰면 코드 최적화에 큰 효과
!
try만 써도 코드 크기늘고 속도 잡아먹음
!
예외 지정도 try와 마찬가지로 예외 지정을 위한 코드가 생성됨
!
그런데 예외라는건 매번 실행되는게 아니여서 큰 문젠 아니나
만약 자주 예외가 발생하면?
23. 예외 비용을 최소화 하려면?
!
-가능하면 예외 기능을 지원하지 않도록 컴파일 하기
-try블록과 예외지정은 꼭 필요한 부분에만 사용
-예외를 최소로 사용했는데도 예외 때문에 느려진것 같으면
프로파일링으로 예외가 정말 성능이슈인지 확인하고
예외 처리 기능이 더 효율적인 컴파일러로 바꾸라…
강조의 밑줄…
30. 효율 향상에 있어 지연 평가(lazy evaluation)는
충분히 고려해 볼 만하다.
어떤 처리 결과가 진짜로 필요해질 때까지 그 처리를 미룬다.
lazy evaluation
31. 참조 카운팅(Reference Counting)
진짜 필요해지기 전까지는 자기만의 데이터 사본을 만들지 않고,
남이 만들어 놓은 사본을 빌어다 쓰자.
!
데이터 읽기와 쓰기를 구분하기
!
지연 방식의 데이터 가져오기(Lazy Fetching)
큰 객체를 가져올때 다 가져오지 말고 필요한 것만 가져온다.
!
지연 방식의 표현식 평가(Lazy Expression Evaluation)
불필요한 수치 계산 피하기
지연 평가를 사용하면 오히려 메모리 사용이 증가할 수 있으나
계산을 바로 하지 않아도 되는 경우가 잦은 소프트웨어에 유용
32. 예상되는 계산 결과를 미리 준비하면 처리비용 깎을 수 있다
상당히 자주 요구될 것 같은 계산이 있으면
그 요구를 효율적으로 처리할 수 있는
자료구조를 설계하여 비용을 낮추자
33. 캐시를 만들어서 사용하자
!
미리 가져오기 (Prefetching)
요청 받은 내용말고도 바로 다음에 쓸것 같은 데이터도 같이 가져오자.
34. 임시 객체의 원류를 정확히 이해하자
C++에는 힙 이외의 공간에 생성되는 이름없는 임시 객체가 존재
!
- 함수 호출을 성사시키기 위해 암시적 타입 변환이 적용될 때
- 함수가 객체를 값으로 반환 할 때
35. 함수는 스트링을 받고
주기는 캐릭터배열을 주면 변환을 위한 임시 객체가 생성됨
!
해결하기 위해 설계부터 잘 하거나
타입변환이 불필요하도록 소프트웨어를 수정
36. 반환값 최적화(return value optimization)가 가능하게 하자
생성자 인자를 반환하면 컴파일러가 임시 객체 비용을 없애준다.
최적화를 해준다…
37. 오버로딩은 불필요한 암시적 타입변환을 막는 한 방법이다.
암시적 타입변환이 문제가 되면
오버로딩해서 다 해당하게 해서 자동 변환없이
받아들이면 된다.
그래서 얼마나 좋아질까?
를 생각하자
38. 단독 연산자(op) 대신에 =이 붙은 연산자(op=)를
사용하는 것이 좋을 때가 있다
x = x + y; x = x - y;
가 되면
!
X += y; x -= y;
도 될것 같지만 사용자 정의 타입이면?
당연한건 없다.
39. 연산자의 대입 형태(ex operator+=)를 사용해서
단독 형태를 구현하면 효율적이고 편리하다.
!
!
!
!
!
!
일반적으로 대입 형태 연산자는 단독 형태 연산자보다 효율적이다.
(임시객체 X) 유지보수에도 좋다.
!
40. 정 안 되면 다른 라이브러리를 사용하자!
모두가 만족하는 완벽한 라이브러리는 없다.
한쪽을 중시하면 한쪽이 떨어지기 마련
!
(C++의 iostream은 C의 stdio보다 안정적이지만
속도는 느리다…)
!
만약 라이브러리가 문제가 되면
바꾸는 것도 한 방법
라이브러리의 철학에 따라…
41. 가상 함수, 다중 상속, 가상 기본 클래스,
RTTI에 들어가는 비용을 제대로 파악하자
C++스펙은 하나지만
구현은 컴파일러마다 다 다르다.
!
그 구현의 차이에 큰 성능을 미치는 것중 하나가
가상함수(virtual function)
44. vbtl은 클래스마다 생성이 되는데
클래스의 가상 함수의 개수가 많을 때에는
무시 못할 정도의 메모리 부담이 들어간다…
45. RTTI는 실행 중에 객체와 클래스의 정보를
알아낼 수 있게 하는 꽤 쓸만한 기능
!
그런데 이 정보는
type_info라는 타입 의 객체에 저장하고,
이 type_info 객체는 type_id 연산자를 써서 액세스할 수 있다.
값은 vbtl의 인덱스 0번째에 저장 된다.
type_info와 vbtl에 포인터 하나 추가되지만
메모리 걱정할 필요는 없다…
46. C++만의 특징 객체의 크기 증가 클래스별 데이터
증가
인라이닝 감소
가상 함수 Y Y Y
다중상속 Y Y N
가상 기본 클래스 자주 가끔 N
RTTI N Y N
이런 특징들이 ‘지원되지 않는’ 환경에서 ‘꼭’ 써야 한다면
직접 구현해서 쓰세요…(정말 잘 만들수 있을까?)
!
직접 구현하는 것이 더 편할 상황이 오는데…
(정말 잘 만들수 있을까?)
가상 함수, 다중 상속, 가상 기본 클래스 RTTI에 들어가는 비용