more effective c++ chapter3 4

30
more Effective C++ Chapter 3, 4 ‘예외’ 그리고 ‘효율’ 131039 신동찬

Upload: dongchan-shin

Post on 23-Jun-2015

97 views

Category:

Engineering


1 download

TRANSCRIPT

more Effective C++ Chapter 3, 4‘예외’ 그리고 ‘효율’

131039 신동찬

“예외 발생 상황에서도의도한 대로 잘 동작하는 프로그램

그렇게 동작하는 이유는유연히 그렇게 동작한 것이 아니라

그렇게 동작하도록 설계되었기 때문이다”

예외 만들지 않는 것이 최고지만...

프로그래머의 숙명 중 하나

예외처리

그래서 우리는편하게(?!) 예외 처리를 선택합니다

Try – catch 등으로요

그러나...여러분의 메모리는 안녕하십니까?

스택 되감기(stack rewinding)수행Throw가 찾아가는 곳등에 대한 이해가 없으면 소멸자 호출이 안 될수 있음

200% 문제 함유 code

Class ALA{public:

virtual void processAdoption() = 0;};

class Puppy: public ALA {public:

virtual void processAdoption();};

class Kitten: public ALA{public:

virtual void processAdoption();};

void processAdoptions(istream& data source){

while (dataSourece){ALA* pa = readALA(dataSource);pa->processAdoption();delete pa;

}}

이게 왜 문제의 코드인가?

붉은 글씨 부분이 정상 작동하면 정상 코드!붉은 글씨 부분이 오류나면 pa 해제는 누가 해주나?

이렇게 고쳐볼까...

Class ALA{public:

virtual void processAdoption() = 0;};

class Puppy: public ALA {public:

virtual void processAdoption();};

class Kitten: public ALA{public:

virtual void processAdoption();};

void processAdoptions(istream& data source){

while (dataSourece){ALA* pa = readALA(dataSource);

try{pa->processAdoption();

}catch(…){

delete pa;throw;

}delete pa;

}}

뭐 가능은 합니다만

그리 효율적으로 보이지는 않습니다

아 뭔가 귀찮은데...

‘저절로’ 알아서 하는 녀석들 없나?

effective c++의 유산에서 뒤져보면 있다

자신의 유효범위를 벗어나면 자신이 가리키고 있는메모리를 삭제 할 수 있는 포인터처럼 동작하는 객체

거기에 더 나아가

auto_ptr

void processAdoptions(istream& data source){

while (dataSourece){auto_ptr<ALA> pa(readALA(dataSource));pa->processAdoption();

}}

함수에서는 그렇다 쳐도...

• 생성자에서 예외 발생하면?• 소멸자(자동이라도)에서 예외 발생하면?

그런데 그것이 실제로 일어났습니다...

생성자에서 객체에 대한 메모리 할당 실패는 왕왕 일어납니다

또, C++는 생성 완료된 객체만을 소멸하기 때문에소멸자를 만나지 않는 경우로 연결됩니다

소멸자가 불려지지 않는다 = 소멸자에 있는 것을 덜면?!예외를 별도로 처리하도록 해보자

class BookEntry{public:…private:

Image* initImage(const string& imageFileName);AudioClip* initAudioClip(const string& audioClipFileName);

};

BookEntry:: BookEntry(const string& name, const string& address, const string& imageFileName, const string& audioClipFileName):theName(name), theAddress(address), theImage(initImage(imageFileName)), theAudioClip(initAudioClip(audioClipFileName))

{}

Image* BookEntry::initImage(const string& imageFileName){

if(imageFileName != “”) return new Image(imageFileName);else return 0;

}

AudioClip* BookEntry:: initAudioClip(…{

try{if(audioClipFileName !=“”){

return new AudioClip(audioClipFileName);}else return 0;

}catch(…){

delete theImage;throw;

}}

우리가 흔히 생성자에 넣는 것을함수로 빼기만 해도 처리가 가능하다

여기에 앞서 본 auto_ptr을 적용한다면?

소멸자가 불려지지 않더라도리소스가 새어나가지 않을 것이다

소멸자는 생성자 보다 더 조심해야 합니다.

C++는‘어떤 예외에 대한 처리가 진행되는 동안또 다른 예외 때문에 프로그램 흐름이 소멸자를 떠나면terminate란 함수를 호출해 깨뜨린다’

정리하면소멸자에서 예외처리 잘못하면프로그램 자체가 죽어버린다는 것

에러에도 계층이 있다는 것 알고 계셨나요?

상위 에러 처리한다 하고선 하위 에러를 다시 두는 것!

작가 표현으로 ‘죽은 자식 X랄 만지기’(아래 catch문으로는 절대 안들어가 진다)

예외에 대해서는 이제 어느정도 알겠는데...

전달은 어떻게 해야 될까요?

• catch by *(포인터)• catch by Value(값)

• catch by &(참조자)

아니 포인터나 참조자나 그게 그거 아니었어?

그렇지 않습니다포인터를 사용하려면 객체를 힙에 만들어야 되고,힙에 만든 것은 언제고 지워야 합니다.

어디서 지울래요?

참조에 의한 객체 전달의 경우그런 고민은 바로 사라집니다.

뿐만 아니라 값에 의한 참조에서 해결 못하는변형된 객체에 따른 오류까지 같이 해결하네요

아... 답답해서 안되겠네예외를 내가 직접 지정해서 쓰겠다!

가능합니다

그런데요...예외 지정 리스트에 없는 예외를 발생하는 경우런타임 에러가 발생하며unexpected(이후 terminate를 호출)를 호출해 프로그램을죽여 버리는 수가 있어요

게다가 예외 처리에 드는 비용에 대해 파악하기 시작해볼까요?

예외 처리도 사실 모두 비용이거든요!(trade-off의 세상, 프로그래밍)

책에서도 나오듯리턴으로 함수 복귀하는 것 보다 throw로 복귀하는 것이 1000배 차이

결론: ‘예외 기능을 지원하지 않도록 컴파일하세요’

네... 여기까지 삽질입니다

이전 과제로 try-catch 분석해서 왜 쓰면 안되는지 SHE를 써야 한다 했던 저로선...책 읽는 과정부터 복장터지는 일이었죠

http://www.slideshare.net/Dong-chan_Shin/c-try-catch

삽질의 이유는 위 자료를 참고해주세요

효율은 C++ 개발자에겐 숙명이죠

얼마 전 ‘헉 getInstance()의 로드를 줄이다가 그만’이런 신문에 날 사건도 있었고요

그런데 결국 문제가 되는 건

전체의 20%가 80%의 문제를 일으킨다는 점입니다80-20법칙이라는 거죠

ex) 리소스의 80%는 전체 코드의 20%가 사용실행시간의 80%는 전체 코드의 20%가 소모 등

Bottleneck effect

문제가 되는 곳을 정확히 찾아 내고해당 부분에 정확한 처방을 내는 것이 중요

따라서프로파일러 등 도구의 힘을 빌려서 할 것

이미 다들 가지고 있어요‘프로파일러’

사용해 보시면 압니다

일반적인 병목현상의 원인은...지금 안 들어가도 될 녀석까지 줄 서있기 때문!

프로그램도 마찬가지!

Effective c++에서도...변수는 필요하기 바로 직선에 선언하라!가 있었죠

지연 평가도 유사한 말입니다정말 필요할 때 실행 하라는 것이죠

이런 경우 앞에서 만든 m3는 의미가 사라집니다(m1+m2는 사라져 버리는 거죠)

지연 평가시 불필요한 연산을 줄입니다

오히려 반대의 경우도 있는데

계속 반복되는 연산의 경우 미리 계산해놓으면 처리비용이 줄어요= ‘과도 선행 평가’

메모리 계층구조가 이런 형태를 잘 반영하고 있어요locality가 기억 나시나요?

메모리 hit 위치는 이전 위치 근처가 되는 건데,프로그램 연산의 특징도 이를 따릅니다.

참조 위치의 근접성이 과도선행 평가를 잘 보여줍니다

이제 눈에 보이지 않는 부분도 효율적으로 바꿔야 됩니다

프로그램 동작간 생기고 사라지는 임시 객체도 최소화 해야 됩니다

임시 객체는 소스코드에도 없는힙 이외의 공간에 생기는 이름없는 객체를 의미

임시 객체가 생기는 경우

• 함수 호출을 성사하기 위해 암시적 타입변환을 하는 경우• 함수 객체를 값으로 반환 할 때

당연히 이 둘을 막아야 임시 객체 처리 비용 감소

특히,상수 객체 참조를 전달하는 함수가 나오면임시 객체가 생성되었다 사라지는 부분

컴파일러가 알아서 동작하며생성/ 삭제하는 부분의 시간적/공간적 비용을 줄여보자

그런데 이미 우리도 모르게 효율화는 적용되어 있다

우리가 처음 공부할 때 무심코 사용한 값 반환(임시 객체 비용 있음)은현재 사용하지 않는 경우가 많다

또, 값 반환을 피할 수 없는 연산자도위와 같이 ‘반환값 최적화’가 적용되어 있다(컴파일러에서 지원)

같은 기능을 구현하는데 있어사용자가 무엇을 선택하느냐에 따라

효율이 달라지는 경우도 있다

• 단독 연산자 대신 =이 붙은 연산자를 사용• 더 좋은 라이브러리를 찾아서 사용할 것

라이브러리는 그렇다 쳐도 연산자는?

단독 형태 연산자는 새 객체를 반환해야 된다즉 return 과정에서 임시 객체를 생성하고 소멸

대입 형태 연산자는 이미 있는 왼쪽 인자에 처리 결과 기록생성/소멸 과정이 없다

단독 형태 연산

대입 형태 연산

C++ 기능상으로 확인해야 할 부분이 따로 있습니다.가상 함수, 다중 상속, 가상 기본 클래스 등에 들어가는 비용입니다.

C++는 다형성을 사용하는 동적 바인딩을 위해virtual 키워드를 사용하며 vittual table 등이 나온다

http://www.slideshare.net/bulbitain/virtual-function-tablevirtual functiontalbe 쪼개기 참고

이 부분은 책에 잘 정리되어 있습니다

결국 C++의 강력한 기능도 비용을 수반합니다.

효율 측면에서 우리는

‘Trade-off’라는 것을 잊지 말고 가장 합리적인 방법을

찾아야 할 것 같습니다.

more effective C++ 3, 4