Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
Effective C++
Chapter1. C++에 왔으면 C++의 법을 따릅시다.
Chapter2. 생성자, 소멸자 및 대입 연산자
NHNNEXT 2기
141078 정세빈
Chapter1. C++에 왔으면 C++의 법을 따릅시다.
• Item1. C++ 를 언어들의 연합체로 바라보는 안목은 필수
• Item2. #define을 쓰려거든 const, enum, inline을 떠올리자
• Item3. 낌새만 보이면 const 를 들이대 보자
• Item4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자
Item1. C++ 를 언어들의 연합체로 바라보는 안목은 필수
• C++는 다중패러다임 프로그래밍 언어
=여러 하위 언어들의 연합체
• 여러 하위 언어?
절차적 프로그래밍이 기본: C언어
객체 지향 프로그래밍: 클래스, 캡슐화, 상속, 다형성, 가상 함수
함수식 프로그래밍: FC++라이브러리
일반화 프로그래밍: 템플릿, STL
메타 프로그래밍: TMP(템플릿 메타 프로그래밍)
이것들의 연합체
• 그래서?
 위의 개념을 생각하면, C++가 가지고 있는 여러 언어들의 특징을 때에
맞춰 사용하여 효율적 프로그래밍을 할 수 있다.
Item2. #define을 쓰려거든 const, enum, inline을 떠올리자
• 상수를 선언할 때
의 문제
선행 처리자가 숫자 상수로 바꾸어 버리기 때문에 컴파일러에겐
ASPECT_RATIO라는 기호가 넘어가지 않는다.
 컴파일 에러가 났을 때 ASPECT_RATIO가 아닌 1.653으로 표시해주기 때문에
헷갈릴 수 있음
디버그 시에도 기호 테이블에 들어가있지 않기 때문에 기호로 확인이 불가하다.
• 위 현상을 해결하기 위해  const로 상수 만들기
const로 만든 상수는 기호 테이블에 들어갈 뿐더러 사본은 한번만 생성되기 때
문에 #define보다 크기도 작을 수 있다.
• 클래스 멤버로 상수를 정의할 때
 #define으로는 클래스 상수를 정의할 수도 없을 뿐더러
캡슐화의 혜택도 받을 수 없다.
• 이번에도 const를 쓰면?
 클래스 상수로 쓰일 수 있다. 물론 캡슐화의 기능도
사용 가능하다. 단, 선언과 정의를 헤더에서 동시에 할 수는 없다.
Item2. #define을 쓰려거든 const, enum, inline을 떠올리자
• enum은 언제?
 특별한 경우(선언과 동시에 그 클래스 상수를 사용해야 하여 정의도 미
리 되어있어야 하는 경우)가 존재한다.
이 때, enum을 사용하여 선언과 정의를 헤더에서 동시에 하면 된다.
• enum은 그럴때만?
 enum으로 선언한 정수의 주소를 얻는 것을 막아 보안성을 높여주고,
쓸데없는 메모리 할당을 줄여준다.
또, 실용적인 이유에서 코드에 자주 쓰이니 익숙해지는게 좋다.
Item2. #define을 쓰려거든 const, enum, inline을 떠올리자
• 매크로 함수를 정의할 때
결과에 따라 달라지는 함수 호출이 문제가 된다.
• 이 때 inline을 이용하자
정규 함수의 기본 방식과 타입의 안정성까지 보장해준다.
Item2. #define을 쓰려거든 const, enum, inline을 떠올리자
• const는 일반적으로 변경이 불가한 상수로 취급할 때 쓰임
• 함수 반환값에는 항상 const를!!
안정성과 효율을 증가시키면서 에러도 줄일 수 있다.
• operator의 반환값에도 항상 const를!!
operator의 반환값에 const를 써주면
와 같은 어이없는 실수에 에러를 호출할수 있다.
Item3. 낌새만 보이면 const 를 들이대 보자
• 멤버 함수에서의 const
 “상수 객체에 대해 호출될 함수이다."를 알려주는 역할
• 이것의 장점
 인터페이스의 원활함: 해당 클래스로 만들어진 객체를 변경할 수 있는
혹은 없는 함수는 무엇인지 사용자측으로 알려준다.
 상수 객체에 대한 참조자를 넘김으로써 해당 객체를 조작할 수 있게 한
다.
Item3. 낌새만 보이면 const 를 들이대 보자
• C++의 C부분만을 사용하면 값이 초기화 된다는 보장이 없다.
• C++의 STL부분을 사용하면 그러한 보장을 해준다.
가장 좋은 방법은 모든 객체를 사용 하기 전에 항상 초기화!
Item4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자
• 생성자에서의 대입
 대입으로 원하는 값으로 시작할 순 있다. (가짜 초기화)
 그러나 C++의 규칙에 의하면 객체는 데이터 멤버의 생성자의 본문이
호출 되기 전에 초기화 되어야 한다.
Item4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자
• 생성자에서의 초기화
|
위와 같은 것을 멤버 초기화 리스트라고 한다.
초기화 리스트를 사용하면, 사용된 인자들이
데이터 멤버에 대한 생성자의 인자로 사용되기 때문에
바로 초기화가 가능하다.
괄호 안에 아무 인자도 넣어 주지 않아도 자동으로 각 타입의 기본값으로 초기
화를 진행한다.
초기화 순서는 데이터 멤버의 선언 순서에 영향을 받는다.
Item4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자
• 비지역 정적 객체의 초기화 순서는
개별 번역 단위에서 정해진다.
-비지역 정적 객체: 전역 객체, 네임스페이스에 있는 객체,
클래스 혹은 파일에 있는 정적 객체
-번역 단위: 기본적으로 소스파일 하나(해당 소스파일에 들어있는 헤더파일도 포함)
번역단위가 다르면 비지역 정적 객체의 초기화 순서는 알 수가 없다.
번역 단위가 다른 소스에서 초기화 되지도 않은 멤버를 가져다가 사용
하는 에러가 생길 수 있다.
해결 방법: 비지역 정적 객체를 직접 가져다 쓰는 것을 방지하고 함수를 통해 해당 멤
버의 참조자를 가져다 쓰게 하면 된다.
비지역 정적 객체를 지역 정적 객체로 바꾸는 것
Item4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자
Chapter2. 생성자, 소멸자 및 대입 연산자
• Item5. c++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자
• Item6. 컴파일러가 만들어낸 함수가 필요 없으면
확실히 이들의 사용을 금해버리자
• Item7. 다형성을 가진 기본 클래스에서는
소멸자를 반드시 가상 소멸자로 선언하자
• Item8. 예외가 소멸자를 떠나지 못하도록 붙들어 놓자
• Item9. 객체 생성 및 소멸 과정 중에는
절대로 가상 함수를 호출하지 말자
• Item10. 대입 연산자는 *this의 참조자를 반환하게 하자
• Item11. operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자
• Item12. 객체의 모든 부분을 빠짐없이 복사하자.
• 어떤 멤버는 클래스 안에 선언 되어 있지 않으면 컴파일러가 기
본멤버로 자동 생성한다.
복사 생성자, 복사 대입 연산자,
소멸자 (물론 생성자도 선언이 되어있지 않다면 동일취급)
• 문제는 참조자 or 포인터를 복사하려 할 때
:C++의 참조자는 기존에 참조하고 있던 것과 다른 객체를 함께 참조 할
수 없기 때문에
이를 막기 위해 복사 생성자, 복사 대입 연산자를 직접 정의 하는
게 좋다.
(컴파일러가 기본으로 생성하여 생기는 오류를 막기 위해)
Item5. c++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자
• Item5에서 말했듯이 클래스에 선언되지 않은 몇몇 멤버를 컴파
일러가 생성하는 경우가 있다.
• 애초에 컴파일러가 생성할 필요가 없고, 이것을 막고 싶다면
클래스의 private으로 복사 생성자와 복사 대입 연산자를 선언하고 구현
부를 비워두면 복사 생성자, 복사 대입 연산자의 사용을 막을 수 있다.
Item6. 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해버리자
• 파생 클래스를 가지고 있는 기본 클래스의 소멸자가
비 가상 소멸자이면, 대개 그 객체의 파생 클래스는
소멸되지 않는 참사가 벌어진다.
해결법은 간단하다.
기본 클래스의 소멸자앞에 virtual 키워드만 붙여주면 된다.
(내가 알기론 파생 클래스들의 소멸자에도 virtual 키워드를
붙여야 하는걸로 안다.)
• 파생 클래스가 없는 기본 클래스의 소멸자에 무작정 virtual 키
워드를 붙이는건 에러를 호출 하진 않지만, vptr(virtual table
pointer)가 늘어나면서 파일의 크기를 늘린다.
좋지 않은 행위
Item7. 다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자
• 순수 가상 함수를 가진 추상 클래스에서는
순수 가상 소멸자를 두면 편하다.
단, 순수 가상 소멸자의 정의를 본문에서 안해주면
링커 에러를 호출할 수도 있으니 꼭 정의해줄것
Item7. 다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자
• 소멸자에서 예외가 발생하면?
 예외 처리하는 도중 또 다른 소멸이 이루어 지고
또 예외가 발생한다면 예외가 겹치면서 C++에
과부화가 발생한다.
 이 경우(소멸자에서 예외가 겹치는 경우) 에는 프로그램이
정의 되지 않은 동작을 보인다.  에러
Item8. 예외가 소멸자를 떠나지 못하도록 붙들어 놓자
• 소멸자에 close선언
close가 되면 아무 문제 없지만 close에서 예외가 나온다면
또 다시 같은 문제가 재발하는 것
• try ~ catch로 close감싸기
1. close에서 예외 발생시 std::abort()로 프로그램 종료
2. 예외를 삼켜버리기 = catch문에 아무것도 하지 않음
 둘다 그다지 좋지 않은 방법
그렇다면???
Item8. 예외가 소멸자를 떠나지 못하도록 붙들어 놓자
• close() 따로 선언하기
사용자가 사용할 수 있는 close()를 만들어 사용자가 직접
소멸자를 호출하게 한다.
예외 발생시의 오류는 사용자 탓이므로 괜찮다!!
Item8. 예외가 소멸자를 떠나지 못하도록 붙들어 놓자
• 생성자에서 가상 함수는 금물
 기본 클래스 생성자는 파생 클래스 생성자보다
앞서서 진행 되는데, 만약 기본 클래스 생성자 진행중에 나온
가상 함수가 파생 클래스에 접근하게 되면
아직 초기화 되지 않은 대상을 접근 하였으므로 오류를 범하게 된다.
• 소멸자에서도 금물
 파생 클래스의 소멸자가 호출 되고 나면 C++는 해당 클래스에 들어있던 가상
함수를 없는 코드 취급하게 된다.
만약, 기본 클래스가 이 상황에서 파생 클래스의 함수에 접근 하게 된다면 이도
마찬가지 오류를 범하게 될 것이다.
Item9. 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자
Item10. 대입 연산자는 *this 의 참조자를 반환하게 하자
• C++ 의 대입연산은 사슬처럼 엮일 수 있다.
 해석하면
• 위와 같은 코드를 다른 객체에도 적용 시키려면
 operator=의 정의에 return을 *this로 하여
참조자를 반환하게 하면 된다.
(+=, -=, *=도 마찬가지)
• 자기대입 : 어떤 객체가 자기 자신에 대해 대입 연산자를 적용 하는것
자기 대입 가능성이 가득한 코드
• 같은 객체가 사용될 가능성(중복참조)를 고려해야한다.
 *this 와 rhs가 같은 객체일 가능성이 있다.
그렇게 되면 삭제된 객체를 return해버리는 불상사가 발생한다.
Item11. operator= 에서는 자기대입에 대한 처리가 빠지지 않도록 하자
• operator= 앞머리에서 일치성 검사를 통해
중복 참조를 검사를 하면 된다.
• 하지만 new Bitmap()에서 예외가 난다면?
기존의 멤버가 삭제가 되어 삭제된 포인터만 가지고 있게 된다.
해결법: 기존의 멤버를 복사하여 임시 저장한 후에 new Bitmap()을 하
고, 잘 되었으면 복사했던 포인터를 삭제하여 기존의 멤버를
삭제한다.
Item11. operator= 에서는 자기대입에 대한 처리가 빠지지 않도록 하자
• 객체의 변수가 한 개라도 복사가 안되면 부분복사가 일어나게
된다.
이 때, 컴파일러는 아무 말도 해주지 않아서 문제
고로 우리는 멤버 변수 하나하나 복사할 복사 함수를 만들어 주어야
한다.
• 만약 파생 클래스를 복사할 때, 기본 클래스에 들어있는 멤버
는??
그렇다, 예상대로 컴파일러의 기본 복사 함수는 이것도 검토해주지 않는
다. 파생 클래스의 복사 함수를 만들 때 기본 클래스의 복사 함수를 호출
하는 구조로 만들어 주어야 한다.
Item12. 객체의 모든 부분을 빠짐없이 복사하자
• 복사 대입 연산자에서 복사 생성자를 호출하는 것은 금물!!
 기존의 멤버가 복사된 상태로 들어가 데이터의 손상가능성이 있다.
 그 반대(복사 생성자에서 복사 대입 연산자 호출)도 마찬가지로 위험
• 복사 대입 연산자, 복사 생성자의 다른 방법
 겹치는 멤버들을 대상으로 묶어서 별도의 멤버 함수를 만드는 것
 대체적으로 private에 선언하여 쓰고 init()이라는 이름으로 자주 쓰임
Item12. 객체의 모든 부분을 빠짐없이 복사하자

More Related Content

Effective c++ 1,2

  • 1. Effective C++ Chapter1. C++에 왔으면 C++의 법을 따릅시다. Chapter2. 생성자, 소멸자 및 대입 연산자 NHNNEXT 2기 141078 정세빈
  • 2. Chapter1. C++에 왔으면 C++의 법을 따릅시다. • Item1. C++ 를 언어들의 연합체로 바라보는 안목은 필수 • Item2. #define을 쓰려거든 const, enum, inline을 떠올리자 • Item3. 낌새만 보이면 const 를 들이대 보자 • Item4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자
  • 3. Item1. C++ 를 언어들의 연합체로 바라보는 안목은 필수 • C++는 다중패러다임 프로그래밍 언어 =여러 하위 언어들의 연합체 • 여러 하위 언어? 절차적 프로그래밍이 기본: C언어 객체 지향 프로그래밍: 클래스, 캡슐화, 상속, 다형성, 가상 함수 함수식 프로그래밍: FC++라이브러리 일반화 프로그래밍: 템플릿, STL 메타 프로그래밍: TMP(템플릿 메타 프로그래밍) 이것들의 연합체 • 그래서?  위의 개념을 생각하면, C++가 가지고 있는 여러 언어들의 특징을 때에 맞춰 사용하여 효율적 프로그래밍을 할 수 있다.
  • 4. Item2. #define을 쓰려거든 const, enum, inline을 떠올리자 • 상수를 선언할 때 의 문제 선행 처리자가 숫자 상수로 바꾸어 버리기 때문에 컴파일러에겐 ASPECT_RATIO라는 기호가 넘어가지 않는다.  컴파일 에러가 났을 때 ASPECT_RATIO가 아닌 1.653으로 표시해주기 때문에 헷갈릴 수 있음 디버그 시에도 기호 테이블에 들어가있지 않기 때문에 기호로 확인이 불가하다. • 위 현상을 해결하기 위해  const로 상수 만들기 const로 만든 상수는 기호 테이블에 들어갈 뿐더러 사본은 한번만 생성되기 때 문에 #define보다 크기도 작을 수 있다.
  • 5. • 클래스 멤버로 상수를 정의할 때  #define으로는 클래스 상수를 정의할 수도 없을 뿐더러 캡슐화의 혜택도 받을 수 없다. • 이번에도 const를 쓰면?  클래스 상수로 쓰일 수 있다. 물론 캡슐화의 기능도 사용 가능하다. 단, 선언과 정의를 헤더에서 동시에 할 수는 없다. Item2. #define을 쓰려거든 const, enum, inline을 떠올리자
  • 6. • enum은 언제?  특별한 경우(선언과 동시에 그 클래스 상수를 사용해야 하여 정의도 미 리 되어있어야 하는 경우)가 존재한다. 이 때, enum을 사용하여 선언과 정의를 헤더에서 동시에 하면 된다. • enum은 그럴때만?  enum으로 선언한 정수의 주소를 얻는 것을 막아 보안성을 높여주고, 쓸데없는 메모리 할당을 줄여준다. 또, 실용적인 이유에서 코드에 자주 쓰이니 익숙해지는게 좋다. Item2. #define을 쓰려거든 const, enum, inline을 떠올리자
  • 7. • 매크로 함수를 정의할 때 결과에 따라 달라지는 함수 호출이 문제가 된다. • 이 때 inline을 이용하자 정규 함수의 기본 방식과 타입의 안정성까지 보장해준다. Item2. #define을 쓰려거든 const, enum, inline을 떠올리자
  • 8. • const는 일반적으로 변경이 불가한 상수로 취급할 때 쓰임 • 함수 반환값에는 항상 const를!! 안정성과 효율을 증가시키면서 에러도 줄일 수 있다. • operator의 반환값에도 항상 const를!! operator의 반환값에 const를 써주면 와 같은 어이없는 실수에 에러를 호출할수 있다. Item3. 낌새만 보이면 const 를 들이대 보자
  • 9. • 멤버 함수에서의 const  “상수 객체에 대해 호출될 함수이다."를 알려주는 역할 • 이것의 장점  인터페이스의 원활함: 해당 클래스로 만들어진 객체를 변경할 수 있는 혹은 없는 함수는 무엇인지 사용자측으로 알려준다.  상수 객체에 대한 참조자를 넘김으로써 해당 객체를 조작할 수 있게 한 다. Item3. 낌새만 보이면 const 를 들이대 보자
  • 10. • C++의 C부분만을 사용하면 값이 초기화 된다는 보장이 없다. • C++의 STL부분을 사용하면 그러한 보장을 해준다. 가장 좋은 방법은 모든 객체를 사용 하기 전에 항상 초기화! Item4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자
  • 11. • 생성자에서의 대입  대입으로 원하는 값으로 시작할 순 있다. (가짜 초기화)  그러나 C++의 규칙에 의하면 객체는 데이터 멤버의 생성자의 본문이 호출 되기 전에 초기화 되어야 한다. Item4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자
  • 12. • 생성자에서의 초기화 | 위와 같은 것을 멤버 초기화 리스트라고 한다. 초기화 리스트를 사용하면, 사용된 인자들이 데이터 멤버에 대한 생성자의 인자로 사용되기 때문에 바로 초기화가 가능하다. 괄호 안에 아무 인자도 넣어 주지 않아도 자동으로 각 타입의 기본값으로 초기 화를 진행한다. 초기화 순서는 데이터 멤버의 선언 순서에 영향을 받는다. Item4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자
  • 13. • 비지역 정적 객체의 초기화 순서는 개별 번역 단위에서 정해진다. -비지역 정적 객체: 전역 객체, 네임스페이스에 있는 객체, 클래스 혹은 파일에 있는 정적 객체 -번역 단위: 기본적으로 소스파일 하나(해당 소스파일에 들어있는 헤더파일도 포함) 번역단위가 다르면 비지역 정적 객체의 초기화 순서는 알 수가 없다. 번역 단위가 다른 소스에서 초기화 되지도 않은 멤버를 가져다가 사용 하는 에러가 생길 수 있다. 해결 방법: 비지역 정적 객체를 직접 가져다 쓰는 것을 방지하고 함수를 통해 해당 멤 버의 참조자를 가져다 쓰게 하면 된다. 비지역 정적 객체를 지역 정적 객체로 바꾸는 것 Item4. 객체를 사용하기 전에 반드시 그 객체를 초기화하자
  • 14. Chapter2. 생성자, 소멸자 및 대입 연산자 • Item5. c++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자 • Item6. 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해버리자 • Item7. 다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자 • Item8. 예외가 소멸자를 떠나지 못하도록 붙들어 놓자 • Item9. 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자 • Item10. 대입 연산자는 *this의 참조자를 반환하게 하자 • Item11. operator=에서는 자기대입에 대한 처리가 빠지지 않도록 하자 • Item12. 객체의 모든 부분을 빠짐없이 복사하자.
  • 15. • 어떤 멤버는 클래스 안에 선언 되어 있지 않으면 컴파일러가 기 본멤버로 자동 생성한다. 복사 생성자, 복사 대입 연산자, 소멸자 (물론 생성자도 선언이 되어있지 않다면 동일취급) • 문제는 참조자 or 포인터를 복사하려 할 때 :C++의 참조자는 기존에 참조하고 있던 것과 다른 객체를 함께 참조 할 수 없기 때문에 이를 막기 위해 복사 생성자, 복사 대입 연산자를 직접 정의 하는 게 좋다. (컴파일러가 기본으로 생성하여 생기는 오류를 막기 위해) Item5. c++가 은근슬쩍 만들어 호출해 버리는 함수들에 촉각을 세우자
  • 16. • Item5에서 말했듯이 클래스에 선언되지 않은 몇몇 멤버를 컴파 일러가 생성하는 경우가 있다. • 애초에 컴파일러가 생성할 필요가 없고, 이것을 막고 싶다면 클래스의 private으로 복사 생성자와 복사 대입 연산자를 선언하고 구현 부를 비워두면 복사 생성자, 복사 대입 연산자의 사용을 막을 수 있다. Item6. 컴파일러가 만들어낸 함수가 필요 없으면 확실히 이들의 사용을 금해버리자
  • 17. • 파생 클래스를 가지고 있는 기본 클래스의 소멸자가 비 가상 소멸자이면, 대개 그 객체의 파생 클래스는 소멸되지 않는 참사가 벌어진다. 해결법은 간단하다. 기본 클래스의 소멸자앞에 virtual 키워드만 붙여주면 된다. (내가 알기론 파생 클래스들의 소멸자에도 virtual 키워드를 붙여야 하는걸로 안다.) • 파생 클래스가 없는 기본 클래스의 소멸자에 무작정 virtual 키 워드를 붙이는건 에러를 호출 하진 않지만, vptr(virtual table pointer)가 늘어나면서 파일의 크기를 늘린다. 좋지 않은 행위 Item7. 다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자
  • 18. • 순수 가상 함수를 가진 추상 클래스에서는 순수 가상 소멸자를 두면 편하다. 단, 순수 가상 소멸자의 정의를 본문에서 안해주면 링커 에러를 호출할 수도 있으니 꼭 정의해줄것 Item7. 다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로 선언하자
  • 19. • 소멸자에서 예외가 발생하면?  예외 처리하는 도중 또 다른 소멸이 이루어 지고 또 예외가 발생한다면 예외가 겹치면서 C++에 과부화가 발생한다.  이 경우(소멸자에서 예외가 겹치는 경우) 에는 프로그램이 정의 되지 않은 동작을 보인다.  에러 Item8. 예외가 소멸자를 떠나지 못하도록 붙들어 놓자
  • 20. • 소멸자에 close선언 close가 되면 아무 문제 없지만 close에서 예외가 나온다면 또 다시 같은 문제가 재발하는 것 • try ~ catch로 close감싸기 1. close에서 예외 발생시 std::abort()로 프로그램 종료 2. 예외를 삼켜버리기 = catch문에 아무것도 하지 않음  둘다 그다지 좋지 않은 방법 그렇다면??? Item8. 예외가 소멸자를 떠나지 못하도록 붙들어 놓자
  • 21. • close() 따로 선언하기 사용자가 사용할 수 있는 close()를 만들어 사용자가 직접 소멸자를 호출하게 한다. 예외 발생시의 오류는 사용자 탓이므로 괜찮다!! Item8. 예외가 소멸자를 떠나지 못하도록 붙들어 놓자
  • 22. • 생성자에서 가상 함수는 금물  기본 클래스 생성자는 파생 클래스 생성자보다 앞서서 진행 되는데, 만약 기본 클래스 생성자 진행중에 나온 가상 함수가 파생 클래스에 접근하게 되면 아직 초기화 되지 않은 대상을 접근 하였으므로 오류를 범하게 된다. • 소멸자에서도 금물  파생 클래스의 소멸자가 호출 되고 나면 C++는 해당 클래스에 들어있던 가상 함수를 없는 코드 취급하게 된다. 만약, 기본 클래스가 이 상황에서 파생 클래스의 함수에 접근 하게 된다면 이도 마찬가지 오류를 범하게 될 것이다. Item9. 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자
  • 23. Item10. 대입 연산자는 *this 의 참조자를 반환하게 하자 • C++ 의 대입연산은 사슬처럼 엮일 수 있다.  해석하면 • 위와 같은 코드를 다른 객체에도 적용 시키려면  operator=의 정의에 return을 *this로 하여 참조자를 반환하게 하면 된다. (+=, -=, *=도 마찬가지)
  • 24. • 자기대입 : 어떤 객체가 자기 자신에 대해 대입 연산자를 적용 하는것 자기 대입 가능성이 가득한 코드 • 같은 객체가 사용될 가능성(중복참조)를 고려해야한다.  *this 와 rhs가 같은 객체일 가능성이 있다. 그렇게 되면 삭제된 객체를 return해버리는 불상사가 발생한다. Item11. operator= 에서는 자기대입에 대한 처리가 빠지지 않도록 하자
  • 25. • operator= 앞머리에서 일치성 검사를 통해 중복 참조를 검사를 하면 된다. • 하지만 new Bitmap()에서 예외가 난다면? 기존의 멤버가 삭제가 되어 삭제된 포인터만 가지고 있게 된다. 해결법: 기존의 멤버를 복사하여 임시 저장한 후에 new Bitmap()을 하 고, 잘 되었으면 복사했던 포인터를 삭제하여 기존의 멤버를 삭제한다. Item11. operator= 에서는 자기대입에 대한 처리가 빠지지 않도록 하자
  • 26. • 객체의 변수가 한 개라도 복사가 안되면 부분복사가 일어나게 된다. 이 때, 컴파일러는 아무 말도 해주지 않아서 문제 고로 우리는 멤버 변수 하나하나 복사할 복사 함수를 만들어 주어야 한다. • 만약 파생 클래스를 복사할 때, 기본 클래스에 들어있는 멤버 는?? 그렇다, 예상대로 컴파일러의 기본 복사 함수는 이것도 검토해주지 않는 다. 파생 클래스의 복사 함수를 만들 때 기본 클래스의 복사 함수를 호출 하는 구조로 만들어 주어야 한다. Item12. 객체의 모든 부분을 빠짐없이 복사하자
  • 27. • 복사 대입 연산자에서 복사 생성자를 호출하는 것은 금물!!  기존의 멤버가 복사된 상태로 들어가 데이터의 손상가능성이 있다.  그 반대(복사 생성자에서 복사 대입 연산자 호출)도 마찬가지로 위험 • 복사 대입 연산자, 복사 생성자의 다른 방법  겹치는 멤버들을 대상으로 묶어서 별도의 멤버 함수를 만드는 것  대체적으로 private에 선언하여 쓰고 init()이라는 이름으로 자주 쓰임 Item12. 객체의 모든 부분을 빠짐없이 복사하자