31
EFFECTIVE C++ 정정 Chapter 2 정정정 정정정 정정정정정

Effective c++ 챕터 2 정리

  • Upload
    -

  • View
    237

  • Download
    2

Embed Size (px)

Citation preview

Page 1: Effective c++ 챕터 2 정리

EFFECTIVE C++ 정리Chapter 2

생성자 소멸자 대입연산자

Page 2: Effective c++ 챕터 2 정리

C++ 이 몰래 만든 함수들ITEM 5

Page 3: Effective c++ 챕터 2 정리

Item 5: C++ 이 몰래 만든 함수들

• 직접 선언하지 않으면 저절로 선언하는 함수들• 기본 생성자

• 복사 생성자

• 복사 대입 연산자

• 소멸자

• 위의 함수가 필요한 경우 컴파일러가 선별하여 생성

Page 4: Effective c++ 챕터 2 정리

Item 5: C++ 이 몰래 만든 함수들

• 기본 생성자와 소멸자• 컴파일러의 뒷 작업 ( 배후의 코드 ) 을 처리하는 공간

• 기본 클래스 , 비정적 데이터 멤버의 생성자 / 소멸자 호출

• 부모 클래스의 소멸자가 가상 소멸자가 아니라면• 자식 클래스의 소멸자도 비 가상 소멸자로 만들어 진다

• 유저가 만든 생성자가 하나라도 있다면• 컴파일러는 기본 생성자를 만들지 않는다 .

Page 5: Effective c++ 챕터 2 정리

Item 5: C++ 이 몰래 만든 함수들

• 복사 생성자 / 복사 대입 연산자• 원본 객체의 비정적 데이터를 사본 객체에 복사하는 기능

• 자동으로 만들어지는 복사기능• 멤버 변수가 사용자 정의 타입이면 복사 생성자 / 대입 연산자 호출

• 멤버 변수가 Built-in 타입이면 비트를 그대로 복사

• 상수 / 참조 타입 멤버변수가 있으면 대입 연산자 안 만들어줌

• 부모의 대입 연산자가 private 면 대입 연산자 안 만들어 줌

Page 6: Effective c++ 챕터 2 정리

자동 생성 함수 봉인 방법ITEM 6

Page 7: Effective c++ 챕터 2 정리

Item 6: 자동 생성 함수 봉인 방법• 복사되지 않는 객체 만드는 방법

• 복사 생성자 , 대입 연산자의 자동 생성을 봉인한다 .

• private 로 선언하고 , 정의하지 않는다 .

• private 이므로 외부에서 접근 불가능 ! (friend 는 ?)

• 정의하지 않았으므로 복사 사용하면 , 링킹 에러

• 컴파일 시점에 원천 봉쇄하고 싶다면 ?

• 복사 생성자 / 대입 연산자를 private 로 하는 복사 방지 클래스를 만든다 .

• 복사 방지 클래스를 상속받는다 .

• 최적화 / 다중상속 이슈는 뒤로 미룸 ^^

Page 8: Effective c++ 챕터 2 정리

부모님께 가상 소멸자 하나 놔드려야 겠어요

ITEM 7

Page 9: Effective c++ 챕터 2 정리

Item 7: 부모클래스에 가상 소멸자 놓기

• C++ 규정 saying

• “ 부모 클래스 포인터를 통해서 자식 클래스가 삭제될 때 ,

부모 클래스에 가상 소멸자가 없다면 Undefined Behavior”

• 일반적인 상황 • 부모 클래스의 소멸자가 호출

• 자식 클래스의 소멸자 호출되지 않음

• 자식 클래스의 종료 처리 및 해제 동작 안함 ERROR & LEAK

Page 10: Effective c++ 챕터 2 정리

Item 7: 부모클래스에 가상 소멸자 놓기

• 부모님께 가상 소멸자를 넣어드립시다• 가상함수는 동적 타입을 추적하여 올바른 함수를 호출하게 한다 .

• 가상 소멸자도 동적 타입에 올바른 동작을 수행한다 .

• 자식 소멸자 부터 상속 순으로 최상위 부모 소멸자 까지 호출됨

Page 11: Effective c++ 챕터 2 정리

Item 7: 부모클래스에 가상 소멸자 놓기

• 그렇다고 아무 때나 가상함수를 남발하면 안 된다 .

• 부모가 아니거나 , 부모 역할 (virtual 함수 ) 이 필요 없는 경우

• virtual 을 쓰는 순간 클래스에는 가상함수 테이블 ,

인스턴스에는 가상함수 포인터가 생긴다 .

• 불필요한 메모리 사용

• 부모 클래스에 가상함수가 있을 때 , 가상 소멸자를 사용하자 .

Page 12: Effective c++ 챕터 2 정리

Item 7: 부모클래스에 가상 소멸자 놓기

• 가상 소멸자가 없는 클래스를 상속받지 말자 .

• STL 이나 std::string 등에는 가상 소멸자가 없으므로 주의

• 추상 클래스를 만들 때 이렇다할 가상함수가 없다면 ?

• 순수 가상 소멸자만 만들어 두면 된다 .

• 가상 소멸자는 최상위까지 반드시 호출되므로 정의도 구현해야한다 .

• 모든 부모클래스가 다형성을 지원하는 것은 아니다 .

• 앞에서도 말했지만 , 가상 소멸자를 선택할 때 주의를 기울여라

Page 13: Effective c++ 챕터 2 정리

소멸자에서 try catchITEM 8

Page 14: Effective c++ 챕터 2 정리

Item 8: 소멸자에서 try catch

• 만약 여러 개의 소멸자에서 동시에 예외를 던진다면 ?

• 클래스 컨테이너에서 반복자를 통해 소멸자를 호출하는 경우

• Catch 는 저 멀리 , 활성화된 예외가 축적된다 .

• C++ 에서 감당하기 어려운 상태가 되어버림 .

• 불완전 종료 or Undefine Behavior…

Page 15: Effective c++ 챕터 2 정리

Item 8: 소멸자에서 try catch

• 소멸자 내부에서 try 하려면 catch 도 안에서 해라 .

• 오류나면 그대로 종료하는 방법 std::abort()

• 문제는 없지만 try catch 를 쓰는 의미가 ?

• 오류나면 로그만 찍고 계속 진행하는 방법• 무엇이 잘못되었는지 확실히 알기 어렵다 .

• 오류 이후에도 프로그램이 올바르게 실행 되는 것이 보장돼야 한다 .

• Catch 는 오류에 대해서 적절한 대처를 하기 위함인데 어느 쪽도 적절해보이지 않는다 .

Page 16: Effective c++ 챕터 2 정리

Item 8: 소멸자에서 try catch

• 예외를 처리하는 부분을 소멸자에 넣지 마라• 다른 함수로 빼서 사용자에게 맡긴다 .

• Ex) Delete 하기 전에 Release 를 반드시 하도록 요구 .

• 에러나면 사용자 탓 ^^

Page 17: Effective c++ 챕터 2 정리

생성 / 소멸자에서 가상함수ITEM 9

Page 18: Effective c++ 챕터 2 정리

Item 9: 생성 / 소멸자에서 가상함수

• 생성자에서 가상함수를 사용하면 안 된다 !

• 상속받은 클래스의 생성자 / 소멸자를 호출하면• 최상위 부모클래스부터 해당 클래스까지 정해진 순서대로 생성 / 소멸자가 호출

• 상위 클래스의 생성자 / 소멸자 호출시점에서• 해당 클래스는 현재 진행중인 호출 스택의 부모 타입 처럼 인식된다 .

• 런타임 타입 추론을 이용하는 함수들에서도 동일하게 처리됨

• dynamic_cast , typeid 등

• 때문에 이 시점에 가상함수를 쓰면 해당 부모 타입의 가상함수가 호출된다 .

Page 19: Effective c++ 챕터 2 정리

Item 9: 생성 / 소멸자에서 가상함수

• 생성 / 소멸자 호출 시점에서 객체의 타입을 변경시키는 이유• 생성자의 경우

• 자식 클래스의 멤버는 아직 초기화되지 않은 상태 .

• 소멸자의 경우• 자식 클래스의 멤버 변수는 이미 해제된 상태

• 여기서 멤버 함수를 호출하는 것은 위험하다 .

Page 20: Effective c++ 챕터 2 정리

Item 9: 생성 / 소멸자에서 가상함수

• 대처 방법• 생성자에서는 비 가상 함수만 사용한다 .

• 필요한 초기화 정보를 자식클래스에서 부모클래스로 올려준다 .

class ChildClass : public ParentClass {public: ChildClass(int arg) : ParentClass(createLogString(arg))// 초기화 리스트를 통해서 부모 생성자에 인자 전달 {...} protected: static std::string createLogString(int arg); /* 정적 함수를 사용하면 멤버변수와 독립적으로 사용가능 , 초기화 문제 해결 . 생성자 호출 전에 호출가능 */};

Page 21: Effective c++ 챕터 2 정리

대입 연산자의 리턴형식ITEM 10

Page 22: Effective c++ 챕터 2 정리

Item 10: 대입 연산자의 리턴 형식

• 대입 연산은 chaining 이 가능 ( 일종의 convention)

• int x, y, z;

• x = y = z = 15; //x = (y = (z = 15));

• 대입 연산의 결과 ( 좌변 ) 가 반환 되어야 가능• T& operator=(const T& rhs){ … return *this }

• 모든 형태의 대입 연산자에서 동일하게 적용• +=, -=, *= …

Page 23: Effective c++ 챕터 2 정리

자기 대입 처리ITEM 11

Page 24: Effective c++ 챕터 2 정리

Item 11: 자기 대입 처리

• 자기 대입 • 어떤 객체가 자기 자신에 대해 대입 연산자를 적용하는 것

• a[i] = a[j]; *px = *py; // 충분히 자기대입 가능한 상황

• 중복참조에서 발생하는 자기대입의 가능성을 고려하자• 자원 관리 객체를 사용할 때 특히 주의

Widget& operator=( const Widget& rhs ) // 위험한 대입연산자의 예{ delete m_Data; m_Data = new Data(*rhs.m_Data); ... return *this; }

Page 25: Effective c++ 챕터 2 정리

Item 11: 자기 대입 처리

• 대입연산의 예외에 대한 안정성을 구축하자 .

• 상세한 내용은 item 29 로 가시오

• 올바른 순서를 갖추는 것 만으로 자기대입 처리를 가능하게 !

Widget& operator=( const Widget& rhs ) { Data* tempData = m_Data; // 원래 데이터를 임시 저장 m_Data = new Data(*rhs.m_Data); // 사본으로 멤버변수 대체 delete tempData; // 임시 데이터 삭제 ... return *this; }

Page 26: Effective c++ 챕터 2 정리

Item 11: 자기 대입 처리

• 같은 객체인지 체크하고 싶다고 ?

• 쓸데없는 복사가 일어나는 건 맞다 .

• 하지만 자기 대입이 일어날 확률은 vㅔ리 낮다 .

• 동일성 체크는 대입 연산 할 때마다 해야 한다 .

• 코드 비대 , 연산 처리 분기발생 , CPU 선행처리 , 파이프라이닝 효율 저하…

• 체크의 비용 >>>> 가끔 복사 비용

Page 27: Effective c++ 챕터 2 정리

Item 11: 자기 대입 처리

• 비기 copy & swap

• void Swap(Widget& rhs) // *this 와 rhs 의 데이터 스왑

• Swap 에서 큰 비용 없이 데이터 전환이 가능하다면• 문제 없고 깔끔한 자기대입 처리가능

Widget& operator=( const Widget& rhs ) { Widget temp(rhs); //rhs 의 사본을 생성하고 Swap(temp); // 사본과 this 의 데이터를 스왑 ... return *this; }

Page 28: Effective c++ 챕터 2 정리

복사는 확실하게ITEM 12

Page 29: Effective c++ 챕터 2 정리

Item 12: 복사는 확실하게

• 복사 생성자 / 대입 연산자를 직접 만들어 쓰는 경우• 복사하지 않은 멤버변수가 있어도 컴파일러가 말해주지 않는다 .

• 아래 항목을 충족시켰는지 체크할 것• 해당 클래스의 데이터 맴버를 모두 복사했는가 ?

• 상속한 모든 부모클래스의 복사 생성자 / 대입 연산자를 호출했는가 ?

Page 30: Effective c++ 챕터 2 정리

Item 12: 복사는 확실하게

• 상속받은 부모 클래스의 복사 생성자 / 대입연산자 호출 방법• 초기화 리스트로 부모 객체의 복사 생성자 호출

• 부모 객체의 대입 연산자 직접 호출ChildClass(const ChildClass& rhs) : ParentClass(rhs) { //Do something }

ChildClass& operator=( const ChildClass& rhs ) { ParentClass::operator=( rhs ); //Do Something return *this; }

Page 31: Effective c++ 챕터 2 정리

Item 12: 복사는 확실하게

• 복사 생성자 대입 연산자 서로 사용할 수 있을까 ?

• 비슷한 코드니까 둘 중 하나만 만들고 가져다 쓰면 안될까 ?

• 대입 연산자에서 복사 생성자를 호출하는 경우• 이미 만들어진 객체를 다시 생성한다 ?

• 복사 생성자에서 대입 연산자를 호출하는 경우• 대입 연산은 이미 초기화된 객체에만 사용가능

• 아직 초기화 되지 않은 객체를 대입 연산한다 ?

• 겹치는 부분을 별도의 멤버함수로 처리할 수는 있다 .