모어이펙티브 c++ 3,4장 예외, 효율 스터디

19
More Effective C++ 3,4

Upload: quxn6

Post on 29-Jun-2015

123 views

Category:

Software


4 download

DESCRIPTION

모어 이펙티브 C++ 3,4장 예외, 효율 스터디

TRANSCRIPT

Page 1: 모어이펙티브 C++ 3,4장 예외, 효율 스터디

More Effective C++ 3,4

Page 2: 모어이펙티브 C++ 3,4장 예외, 효율 스터디

예외

Page 3: 모어이펙티브 C++ 3,4장 예외, 효율 스터디

메모리 누수를 피하는 정공법은 소멸자함수 수행 도중에 예외를 뱉을 경우 delete 가 무시될 수 있다 .

수행 부분을 try-catch 감싸서 함수 수행이 실패할 경우 delete 되도록 할 수 있다 .

but 이렇게 하면 delete 를 catch 문에도 넣어주고 정상 종료 시에도 수행하도록 만들어야 하므로 코드가 중복된다 .

따라서 자원 관리 객체 (smart pointer) 등을 사용하여자원 관리 객체의 소멸자에서 delete 를 해주자 .

Page 4: 모어이펙티브 C++ 3,4장 예외, 효율 스터디

생성자에서는 메모리 누수가 일어나지 않게 하자 .

생성되는 도중에 예외가 발생할 경우 그 객체는 소멸자가 불리지 않는다 .

(C++ 은 생성 과정이 완료된 객체만을 안전하게 소멸시키기 때문 )

따라서 이 부분에 try-catch 를 사용하여 delete 하는 부분을 catch 에서 처리한 후 해당 생성자를 호출한 상위 객체로 예외를 전파하기 위해 throw 를 사용하면 된다 .

정상적인 동작을 할 경우도 있으므로 delete 하는 부분들을 모아서 cleanUp() 등의 함수에 넣은 다음 이를 필요한 곳 (catch 나 소멸자 ) 에서 사용하는 구조가 바람직하다 .

const 포인터를 초기화하는 경우에는 초기화 list 에서 바로 new 하지 말고 init() 함수를 만들어서 그 안에서 try-catch 형태로 구현한 후 포인터를 return 하는 형태로 만들면 좋다 .

Page 5: 모어이펙티브 C++ 3,4장 예외, 효율 스터디

소멸자에서는 예외가 탈출하지 못하게 하자 .

스택 되감기 동작 중 terminate 가 호출되는 것을 막고

소멸자의 동작을 완전히 끝내기 위해서 ( 소멸자가 발생한 예외가 전파되면 소멸자는 실행이 끝나지 않은 상태로 남게 되므로 ..)

Page 6: 모어이펙티브 C++ 3,4장 예외, 효율 스터디

예외 발생이 매개변수 전달 , 가상함수 호출등과는 다른 점은 ?

예외는 원래 객체의 사본으로 전달 된다 . 단 throw 는 기존 예외를 중계한다는 뜻으로 예외를 새로 만들지 않기 때문에 효율적

1. Catch 문으로 전달되는 예외는 항상 복사가 기본적으로 한 번씩 일어나게 되어있음

2. catch 문으로 넘길 때 암시적 타입변환은 이루어지지 않음 .

하지만 상속관계 기반의 타입변환과 , 타입이 있는 포인터에서 타입이 없는 포인터로 바꾸는 경우는 됨

3. catch 문은 등장한 순서대로 사용된다 .( 가상함수는 그 객체의 동적 타입과 가장 가까운 클래스에 정의된 함수를 선택하지만 catch 는 가장 첫 번째를 선택한다는 것이 다르다 .)

Page 7: 모어이펙티브 C++ 3,4장 예외, 효율 스터디

발생한 예외는 참조자로 받아내자 .

예외 객체를 catch 문으로 전달할 때 , 값으로 전달하면 값 복사가 2 번 일어난다 . 발생시에는 파생클래스 객체였는데 catch 문에 들어가면서 기본 클래스로 싹뚝 짤릴 수도 있다 .

포인터로 전달하는 경우에는 그냥 던지면 그 포인터가 가르키는 객체가 함수를 벗어나면서 사라지므로 안된다 . 그래서 new exception 을 만들어서 던지는 방법이 있는데 exception 을 삭제할 것이냐 말 것이냐에 대한 문제가 있다 .(exception 을 받은 함수에서 이놈이 new 로 만들어진 예외인지 아닌지 모르므로 )

결국 남은 것은 참조자로 예외를 받는 것 뿐이다 .

Page 8: 모어이펙티브 C++ 3,4장 예외, 효율 스터디

예외 처리에 드는 비용에 대해 정확히 파악하자예외처리를 지원하지 않도록 컴파일하면 컴파일타임과 런타임 모두에서 이득을 볼 수 있다 .(

프로그램의 어느 한 부분이라도 예외를 사용하면 다 지원해줘야함 ..)

try 블록을 쓸 경우 추가되는 비용은 5~10% 정도이다 .

예외가 발생할 때의 비용은 함수 복귀의 속도와 비교하여 1000 배 정도 느리다 .

하지만 예외가 발생할 때만 지불하는 비용이므로 없다고 봐도 무방

Page 9: 모어이펙티브 C++ 3,4장 예외, 효율 스터디

효율

Page 10: 모어이펙티브 C++ 3,4장 예외, 효율 스터디

80 : 20 의 법칙병목현상의 80% 는 전체코드의 20% 에서 발생한다 .

나머지 80% 의 코드는 20% 정도밖에 성능에 영향을 주지 않는 것 .

Page 11: 모어이펙티브 C++ 3,4장 예외, 효율 스터디

지연 평가 방식을 사용하자최고로 효율적인 코드는 아무 계산도 하지 않는 것 . 아무 계산도 하지 않을 수 없다면 계산을 최대한 뒤로 미룬다면 어떨까 ? 운이 좋으면 계산을 안하고 넘어갈 수도 있으니까 !!

참조 카운팅string s1 = s2 같이 대상을 복사해서 사용하는 경우를 생각해볼 때 , 복사된 대상인 s1 이 특별히 값을 저장하거나 하지 않는 이상 실제로 복사하지 않고 참조만 해서 쓰는 것 .

데이터 읽기와 쓰기를 구분하기cout << s1[3] ; 읽기 s[3] = ‘x’; 쓰기사실 [] 만으로 쓰기와 읽기를 구별하긴 어렵다 . 나중에 배운다고 하니 참아보자

지연 방식의 데이터 가져오기데이터를 가진 객체를 사용할 때 , 통으로 가져오기보다는 필요한 부분만 가져올 수 있도록 인터페이스를 설계할 것 .

지연 방식의 표현식 평가불필요한 수치계산을 피하는 것 . m3 = m1+m2 일 때 , 나중에 m3 를 사용할지 안 할지 모르니까 얘는 그 둘의 합을 저장하는 놈 정도로 기억만 하는 것 . ec++ 에서 초기화는 최대한 늦게할 것 이라는 항목과 일맥상통하는 느낌 !!

Page 12: 모어이펙티브 C++ 3,4장 예외, 효율 스터디

예상되는 계산 결과를 미리 준비하면 처리비용은 줄어든다 .

많이 사용되는 작업을 미리 해둠으로써 나중에 편하게 가자 !!

캐싱 (caching)

특정 데이터를 가져올 때 ( 계산할 때 ), 그 결과를 모아두었다가 데이터를 가져올 때 , 모아둔 곳(캐시 ) 를 먼저 살펴보는 것 . 책에서는 std::map 에 데이터를 가져올 때마다 저장해두고 ,

읽어올때는 그 곳을 먼저 뒤져보는 방식으로 썻음

하드에서 데이터를 가져올 때 , 뭉탱이로 가져온다 .( 컴퓨터 아키텍쳐에서 배운 로컬~~근접성 원리 )

배열 같은게 있다고 할 때 그 크기를 늘릴 때마다 new 하면 비용이 크니까 미리 크게 늘려놓고 많이 가져온다 .

** new 는 힙에서 메모리를 떼어오기 위해 system call 을 사용하므로 일반 함수호출보다 비쌈 !!

Page 13: 모어이펙티브 C++ 3,4장 예외, 효율 스터디

임시 객체의 원류를 이해하자 .

임시 객체라는 놈이 있다 .

우리가 int temp 로 선언하는 그런 변수가 아니다 .

예를 들면D3DXVECTOR3 의 참조자를 매개변수로 받는 함수 A 가 있다고 할 때

A( &D3DXVECTOR3(3.0F, 3.0F, 3.0F) )

라고 쓰면 위험하다 . 왜냐하면 저 자체로 임시 객체이기 때문에 언제 사라질 지 모르기 때문에 !!

(+ 만드는 데 비용도 든다 .)

물론 반환 값이 임시 객체일 때 피할 수 있는 방법은 없지만 생성하는데 비용이 들며 그러므로 언제 생기는 구나 하는 정도는 알아야 한다 . 이러한 통찰력을 기르도록 훈련을 하자 !!

Page 14: 모어이펙티브 C++ 3,4장 예외, 효율 스터디

반환값 최적화함수를 만들었을 때 , 반드시 값으로만 반환할 수 있는 결과도 있다 . 이러한 놈들은 반환할 때 임시 객체를 생성하므로 비용이 든다 .

BUT 이러한 놈이라도 임시 객체의 비용이 들지 않게 할 수 있다 .

INLINE 함수를 통해서 !!

이를 반환값 최적화라 한다 .

Page 15: 모어이펙티브 C++ 3,4장 예외, 효율 스터디

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

UPINT UPI3, UPI1;

UPI3 = UPI1 + 10;

이 있다고 할 때 , UPINT 에 OPERATOR overloading 된 타입 중 int 가 없다면 10 을 컴파일러가 암시적 타입변환을 통해서 UPINT 형으로 변환한 뒤 계산할 것이다 .

UPINT 형으로 변환하는 부분에서 임시 객체가 생성되는데 이 또한 비용이므로UPINT 에서 int 타입 또한 계산할 수 있도록 operator overloading 해둔다면 임시객체는 생성되지 않는다 .

이 때 조심할 것은 lhs든 rhs든 하나는 자기 자신을 타입으로 가져야 한다는 것

UPINT 형의 operator 를 만드는데 매개변수가 int, int 이면 컴파일러는 오류를 뱉는다 .

Page 16: 모어이펙티브 C++ 3,4장 예외, 효율 스터디

단독 연산자 대신에 = 이 붙은 연산자를 사용하는 것이 좋을 때가 있다대입 형태 연산자 (-=, += 같은 거 ) 는 단독 연산자보다 비용이 저렴하다 .

왜냐하면 임시 객체를 생성하지 않기 때문 ,

result = a + b + c + d 로 짜면 임시객체가 여러 개 생성되지만 (3 개 ?)

result = a

result += b

result += c

result += d

로 짜면 임시 객체 없이 짤 수 있다 .

추가로 책에는 이름 없는 객체를 반환으로 쓰는 것이 좋다고 하나 번역자의 말에 따르면 이름있는 객체나 이름없는 객체나 반환값 최적화가 먹힌다고 하니 큰 상관은 없을 듯 하다 .

Page 17: 모어이펙티브 C++ 3,4장 예외, 효율 스터디

정 효율이 떨어지면 다른 라이브러리를 사용할 것 .

iostream 과 stdio 는 같은 입출력을 하지만 성능 측면에서 많이 다르다 .

printf, scanf 등을 사용한 벤치마크를 돌릴 경우 cout , cin 등을 사용할 때 보다 20% 이상 빠르다 .

but 타입 안정성과 확장성은 iostream 함수를 사용할 때 훨씬 좋다 .

나에게 맞는 라이브러리를 사용할 수 있는 유연성을 키우자

Page 18: 모어이펙티브 C++ 3,4장 예외, 효율 스터디

가상 함수 , 다중 상속 , 가상 기본 크랠스 RTTI 에 들어가는 비용가상함수 테이블 vtbl

가상 테이블 포인터 vptr

vtbl 의 크기는 클래스에 선언된 가상함수의 수에 비례

vtbl 이 어디 있냐하면 클래스의 inline 도 아니고 순수 가상 함수도 아닌 , 함수 중 가장 첫 번째 것의 정의부분 (cpp

겠지 ?)

SO, 모든 가상함수가 inline 으로 선언되어 있으면 이 규칙을 못 적용하니모든 obj 파일에 그 클래스의 vtbl 사본을 저장하는 사태가 벌어짐 -> 애초에 말이 안됨 ..(inline

은 컴파일 타임에 꾸겨넣는 것이고 가상함수는 런타임에 결정되니까 .. so 가상함수는 inline 을 쓸 수 없다 !)

Page 19: 모어이펙티브 C++ 3,4장 예외, 효율 스터디

가상 함수 , 다중 상속 , 가상 기본 크랠스 RTTI 에 들어가는 비용class A 와 이를 상속받는 B 가 있다고 할 때 , B 객체에 대해 어떤 vtbl 을 사용할 것인가 ? vptr

이 알려줌vptr 이 어디 숨겨져 있는지는 컴파일러마다 다름

B b; b->f1() 일 때 , f1() 이 누구의 것인지 판단하는 절차는1. b 가 가리키는 객체의 vptr 을 따라 vtbl 로 감2. vtbl 을 뒤져서 f1() 의 함수 포인터를 찾아옴3. 함수 호출

다중 상속을 할 때는 가상 기본 클래스가 따라옴 .( 상속 계통이 다이아몬드 구조일 때 기본 클래스를 중복 생성하지 않기 위해 ..)