SlideShare a Scribd company logo
1 of 59
Effective C++
Chapter 1, 2정리
Chapter 1
C++에 왔으면
C++의 법을 따릅시다
Item 1
C++을 언어의 연합체로 보자
Item 1 : C++을 언어의 연합체로 보자
- C++은 어렵다
- 그래도 C++의 산을 넘는 대원칙이 있다
- 바로 C++을 언어의 연합체로 보는 것
- C++의 여러 문제들과 맞닿게 되었을 때,
- C++이 4개의 하위 언어로 구성됨을 알고
- 문제가 어떤 하위 언어와 연관되어 있는지 알면, 더 쉽게 문제를 해결 할 수 있다.
Item 1 : C++을 언어의 연합체로 보자
C++을 구성하는 4개의 하위 언어들
1. C
- 기본이다. 블록, 선행처리자, 기본제공 데이터타입, 배열, 포인터 등이 C로부터 왔다
2. 객체지향의 C++
- 클래스를 쓰는 ‘C’ 포함, 캡슐화, 다형성, 가상 함수 등등
3. 템플릿 C++
- 뒤에서 다룰 템플릿 메타 프로그래밍과도 연관
4. STL
- 템플릿 라이브러리, 독특한 사용규약들을 알아야 한다
Item 2
#define을 쓰려거든 const, Inline,
enum을 떠올리자
Item 2 : #define을 쓰려거든 const, enum, inline
프로그래밍을 하다 보면 #define을 쓰는 경우가 부지기수다
그러나 #define에는 단점이 있다
대체 수단인 const, enum, inline을 사용하도록 하자. 되도록이면
Item 2 : #define을 쓰려거든 const, enum, inline
단점 1 : #define은 기호가 아니다
ex) #define ASPECT_RATIO 1.653
실제로 코드에 ASPECT_RATIO가 쓰이는 부분은 기호가 아닌 1.653이라는 수가 들어간다.
어떤 곳에서 에러가 나면 1.653이라는 메시지가 있을 뿐!
이게 우리가 쓴 그 ASPECT_RATIO인지 계산된 어떤 값인지 알 길이 없다
대체재 : const
const double AspectRatio = 1.653; // 기호이자 상수
Item 2 : #define을 쓰려거든 const, enum, inline
단점 2 : #define은 사본을 많이 만든다
ex) #define ASPECT_RATIO 1.653
우리가 ASPECT_RATIO라고 써 놓은 모든 코드코드마다 사본이 생긴다
그러나 대체할 것들로 사용한다면? 메모리는 한 번 사용될 뿐이다
대체재 : const
const double AspectRatio = 1.653; // 사본은 딱 한 개만 생긴다
Item 2 : #define을 쓰려거든 const, enum, inline
단점 3 : #define은 클래스 상수를 정의할 수도, 캡슐화 혜택을 받을 수도 없다
#define은 private 캡슐화가 되지 않는다. 전역으로 선언된 녀석이다.
대체재 : static const
위의 예에서 numTurns는 클래스 상수로, 유효범위가 클래스이고, 사본 개수가 한 개를
넘지 못한다.
Item 2 : #define을 쓰려거든 const, enum, inline
static const도 대체할 수 있는 대체재 : enum
컴파일러에 따라서 사용상의 주의가 필요할 수 있는 static const와 다른 간단한 enum.
#define처럼 작동 하면서(주소를 얻을 수 없다. 참조도.) 컴파일러에 따른 주의를 기울이
지 않아도 된다. 그리고 많은 코드가 이렇게 쓰여 있다.
Item 2 : #define을 쓰려거든 const, enum, inline
단점 4 : #define의 매크로 함수는 단점이 많다.
각 인자마다 괄호를 씌워줘야 하는 것 하며, 잘 모르고 사용할 때 발생하는 문제들에 대
한
정확한 인지가 필요하다는 것 등등.
대체재 : 인라인 템플릿
괄호를 많이 쓸 필요도 없고, 유효범위는 그대로 지켜지면서, 인자가 여러 번 평가 될 수
도 있는 상황이 만들어지지 않는다.
Item 3
낌새만 보이면 const를 들이대자
Item 3 : 낌새만 보이면 const를 들이대 보자
BIG 장점 : 1. 의미적인 제약 = 컴파일러에 의해 외부변경을 불가능하게 한다.
2. 약속 = 다른 프로그래머와 제작자가 의도를 나눌 수 있다.
const의 위치와 의미 = 포인터 왼쪽 const면 데이터를 상수화,
포인터 오른쪽 const면 포인터 자체를 상수화
STL과 const 위치간의 관계
const STL::iterator 라면 const가 포인터 오른쪽에 붙은 상황,
STL::const_iterator라면 const가 포인터 왼쪽에 붙은 상황이다
Item 3 : 낌새만 보이면 const를 들이대 보자
함수 선언과 const 위치간의 관계
1. 함수 반환 값에 붙는 경우
함수 계산 후 나오는 값이 const
2. const를 매개변수와 함께 사용하는 경우
지역 변수와 const를 같이 쓰는 경우와 같다.
Item 3 : 낌새만 보이면 const를 들이대 보자
함수 선언과 const 위치간의 관계
3. const가 멤버 함수 괄호 뒤에 붙는 경우
물리적 상수성을 지켜준다 = 클래스의 데이터 멤버 변수를 하나도 바꾸지 않는다
논리적 상수성을 지켜준다 = const 함수 안에서 객체의 데이터를 바꿀 수 있게 하되
사용자 측에서 알아차리지 못하게 하면 상수 멤버 자격이
있
게 하자
Item 3 : 낌새만 보이면 const를 들이대 보자
논리적 상수성(이어서)
mutable 선언은 논리적 상수성이 가능하도록 합니다
Item 3 : 낌새만 보이면 const를 들이대 보자
Item 4
객체를 사용하기 전에 반드시 그 객
체를 초기화 하자
제목이 내용이다. 사용하기 전에 모든 객체를 초기화 하자.
그런데 대입을 초기화와 헷갈리지 말자
어떤 클래스의 생성자에서 대입(=)을 하는 것은 초기화가 아니다.
//대입 //초기화(초기화 리스트 사용)
Item 4 : 객체를 사용하기 전에 반드시 그 객체를 초기화 하자
제목이 내용이다. 사용하기 전에 모든 객체를 초기화 하자.
그런데 대입을 초기화와 헷갈리지 말자
어떤 클래스의 생성자에서 대입(=)을 하는 것은 초기화가 아니다.
대입(기본생성자 + 복사대입연산자) 초기화(생성자 한번)
Item 4 : 객체를 사용하기 전에 반드시 그 객체를 초기화 하자
초기화 순서 : 기본 클래스는 파생 클래스보다 먼저 초기화 된다
클래스 데이터 멤버는 선언된 순서대로 초기화 된다
☞ 비지역 정적 객체의 초기화 순서는 개별 번역 단위에서 정해진다
정적 객체란?
1. 전역 객체
2. 네임스페이스 유효범위에서 정의된 객체
3. 클래스 안에서 static으로 정의된 객체
4. 함수 안에서 static으로 정의된 객체 ( 얘만 지역 정적 객체이다)
5. 파일 유효범위 안에서 stati으로 정의된 객체
번역 단위란?
- object file을 만드는 바탕이 되는 소스코드, 소스 파일 하나 + #include 코드 내용
결론 : 비지역정적 개체 초기화 순서는 알기 어렵다.
Item 4 : 객체를 사용하기 전에 반드시 그 객체를 초기화 하자
Chapter 2
생성자, 소멸자
그리고 대입 연산자
Item 5
C++가 은근슬쩍 만들어 호출해 버
리는 함수들을 주의하자
C++ 컴파일러가 사용자가 선언해 놓지 않았지만 필요한 경우 만드는 함수
- 생성자, 복사 생성자, 복사대입 연산자, 소멸자
- 모두 public 이면서 inline이다
- 여기서 필요한 경우란 다음과 같은 상황이다
- 이렇게 만들어진 함수들은 어떤 일을 하는가?
- 기본 생성자, 기본 소멸자 -> “배후의 코드"를 깔 수 있는 자리 마련,
- 여기서 배후의 코드란 데이터 멤버의 기본 클래스 및 비정적 데이터 멤버의 생성자와
소멸자를 호출하는 코드등을 말한다
- 복사 생성자, 복사대입연산자 -> 원본 객체의 비정적 데이터 사본을 사본 객체쪽으로
복사
- 예외 : 이미 어떤 생성자나 소멸자가 만들어져 있다면 컴파일러는 그에 해당하는 생성
Item 5 : C++가 은근슬쩍 만들어 호출해 버리는 함수들을 주의하자
C++ 컴파일러가 복사 생성자, 복사 대입 연산자를 만들지 않는 경우
- 컴파일러는 똑똑해서 자신이 만들 복사, 복사대입 생성자가 동작 후 최종 결과 코드가
‘적법’하고 ‘이치에 닿는’ 코드가 아니라면 자동 생성을 거부한다
- 예를 들어 Widget Class에 다음과 같은 멤버 변수가 있다고 하자
- 생성자를 통해서 다음과 같은 2개의 객체를 만들어 냈다고 하자
- 여기서 대입 연산을 한다면? (이어서 계속)
Item 5 : C++가 은근슬쩍 만들어 호출해 버리는 함수들을 주의하자
(이어서)C++ 컴파일러가 복사, 복사 대입 생성자를 만들지 않는 경우
- 먼저 w1에 있는 name이 참조이기 때문에 w2에 있는 name을 복사할 수 없다
- 왜냐하면 c++에서 참조는 한 번 참조한 대상을 바꿀 수 없게 해놓았기 때문이다
- 따라서 대입연산이 애매하다
- 또 w1의 value에 w2의 value를 넣을 수도 없다
- 왜냐하면 const로 선언되어있기 때문이다.
- 이런 내용을 컴파일러는 알고 있기 때문에 자동으로 복사대입 연산을 만들지 않는다
- 정리하면 컴파일러는 ‘적합성’을 가지고 있고 ‘이치에 맞는’ 코드인지를 살필 수 있다
- 그리고 맞지 않으면 해당 생성자를 만들지 않는다
- 구체적으로 참조 멤버를 가지고 있다면 항상 복사대입연산자를 개발자 스스로 만들어야
한다
Item 5 : C++가 은근슬쩍 만들어 호출해 버리는 함수들을 주의하자
Item 6
컴파일러가 만들어낸 함수가 필요
없으면 사용을 못하게 하자
앞서 컴파일러가 개발자도 모르게 만들어내는 함수가 있음을 공부했다
- 그런데 컴파일러가 만들어내는 기본 함수들을 사용하고 싶지 않다면?
- 그저 그 함수를 선언하지 않는 것으로 그만인가?
- 아니다. 컴파일러는 이내 선언 되지 않은 그 함수를 만들어낼 것이다
- 이 문제의 해결 방법은 무엇일까?
1번 : 함수를 private로 선언하고 오직 선언만 한다. no 정의
- 컴파일러가 만들어 내는 복사, 복사 대입생성자는 public이다
- 우리는 명시적으로 복사, 복사 대입생성자를 private에서 선언할 수 있다
- 이렇게 선언함으로써 외부로부터의 대입연산 호출을 막을 수 있다
- 하지만 해당 클래스가 friend 선언에 의해 함수 호출을 막을 수 없는 상황이라면?
- 이를 대비하기 위해 오직 헤더에서 선언만 하고 실제로 정의는 하지 않는 것이다
- 이것이 가능한 이유는 누군가 해당 함수를 사용하려고 할 때 정의되지 않았다면 에러가
나기 때문이다
Item 6 : 컴파일러가 만들어낸 함수가 필요 없으면 사용을 못하게 하자
2번 : 1번과 같은 방식으로 한다. 그런데 하나의 클래스를 그와 같이 만들고 이를 상속받아 사
용
하게 한다
- 말은 어렵지만 구체적으로 보면 쉽다.
- 일단 class를 하나 구현한다. 이 class는 오직 복사 방지만을 위해 존재한다
- 이제 이 class를 사용하고자 하는 클래스가 상속 받자
- 그러면 해당 class는 복사, 복사대입 연산자를 사용할 수 없게 된다
Item 6 : 컴파일러가 만들어낸 함수가 필요 없으면 사용을 못하게 하자
Item 7
다형성을 가진 기본 클래스에서는
소멸자를 반드시 가상 소멸자로!
Missile을 상속 받는 LinearMissle과 BulletMissile의 Class가 있다
- 사용자가 각 missile에 접근하고자 할 때가 있다면 다음과 같은 함수를 만들 수 있다
- 이 함수는 파생클래스에 대한 기본 클래스의 포인터를 반환하는 함수이다
- 실제로 이 함수를 사용한다면 missile을 객체의 포인터를 얻을 수 있을 것이고 이제
그 미사일이 필요치 않다면 해당 내용을 지워야 할 것이다
- 바로 아래처럼
Item 7 : 다형성을 가진 기본클래스에서는 소멸자를 반드시 가상 소멸자로!
(Missile 계통(Linear나 Bullet 같은) 클래스로부터 동적
으로 할당된 객체의 포인터)
- 이렇게 myMissile을 지웠을 때 생길 수 있는 문제는 무엇인가?
- 문제의 원인은 아래의 두 가지가 함께 나타나기 때문에 발생한다
- 1번 : 기본 클래스의 포인터를 가지고 있다(내부에 할당된 것은 파생 클래스)
- 2번 : 기본 클래스의 소멸자가 비가상 소멸자이다
- C++ 규정에 의하면 기본 클래스의 포인터를 통해 파생클래스가 삭제될 때 그 기본 클
래스에 비가상 소멸자가 들어있으면 프로그램의 동작은 미정의 사항이다
- 보통은 파생클래스만 있는 부분이 삭제되지 않는다
- 이로 인해 자원이 낭비되고, 디버깅은 어려워지며, 짜증은 유발된다
Item 7 : 다형성을 가진 기본클래스에서는 소멸자를 반드시 가상 소멸자로!
- 이 문제의 해결 방법 : 기본 클래스의 소멸자를 가상 소멸자로 선언 한다
- 그러면 기본 클래스의 포인터를 들고 있을지라도 그 내용이 파생 클래스라면 파생
클래스의 소멸자라 불린다
- 우리는 이제 가상소멸자가 없는 클래스를 다음과 같이 생각할 수 있고 해야 한다
- “이 클래스는 기본 클래스로 절대 쓰이지 않을 것입니다”
- 가상소멸자로 도배하는 것 혹은 전혀 사용하지 않는 것 모두 나쁜 습관이다
- 기준을 잡자
- 해당 클래스에 가상 함수가 하나라도 있다면 가상 소멸자를 선언한다
Item 7 : 다형성을 가진 기본클래스에서는 소멸자를 반드시 가상 소멸자로!
주의 할 것
- 어떤 기본 타입들은 가상 소멸자가 선언되어 있지 않다
- 따라서 상속에 주의가 필요하다
- 해당 타입은 std::string이나 STL 컨테이너등이 그것이다
- 위의 예처럼 가상소멸자가 선언되어 있지 않은 타입을 상속받아서 쓰는 일은 없어야
한다
마지막 포인트
- 어떤 Class를 추상 Class로 만들고 싶다면 소멸자를 순수 가상 소멸자로 선언하자
- 추상 Class의 목적 = 기본 Class로 사용 = 따라서 소멸자는 가상 소멸자여야 한다
- 추상 Class의 목적 + 기본 Class의 목적을 동시에 만족시키는 순수 가상 소멸자!!!
Item 7 : 다형성을 가진 기본클래스에서는 소멸자를 반드시 가상 소멸자로!
Item 8
예외가 소멸자를 떠나지 못하도록
하자
필자의 걱정 : 소멸자 좋지. 그런데 소멸자가 불려서 작동하는 동안, 즉 그 Class가 없어
질 때 오류가 발생하면 어쩌지? 이미 Class도 사라져서 다시 호출할 수도 없는데? 그리
고 C++은 소멸자에서 오류가 발생하면 그 예외가 던져지도록(별다른 조치가 취해지지
않게) 놔두는데? 흐음
해결방식 1 : 소멸자 도중에 예외가 발생하면 프로그램을 끝내자!
단점 : 에러 발생시 프로그램을 종료시켜도 된다면 가능, 하지만 프로그램을 계속 가동
되게
해야 한다면?
Item 8 : 예외가 소멸자를 떠나지 못하도록 하자
해결방식 2 : 소멸자 도중에 예외가 발생하면 예외를 삼키자
단점 : 문제는 해결되지 않는다. 이렇게 하면 이후의 실행이 보장되도록 해야 하는데 쉽지
않다
Item 8 : 예외가 소멸자를 떠나지 못하도록 하자
해결방식 3 : 소멸자에서 예외가 일어나서는 안된다. 예외가 일어난다면 그건 다른 함수에
서 이미 일어났어야 한다. 해결방식 3을 한마디로 정의하면 확인 사살이다
ex)
WidgetManager와 Widget Class가 있다
Widget Class에서 close()함수를 호출한다. 그리고 close()가 잘 적용 되었는지 bool 값을로
저장해 놓는다.
WidgetManager Class에서는 소멸자에서 Widget Class의 bool 값을 확인한다. 만약 bool 값
이 false라면 다시 하번 close() 함수를 호출한다.
만약 WidgetManager에서도 오류가 발생하면 해결방식1이나 2로 다시 가야 한다. 하지만
이 방법은 적어도 2번의 오류 체크를 하기 때문에 해결방식 1,2보다는 더 나은 방식이라 할
수 있다.
Item 8 : 예외가 소멸자를 떠나지 못하도록 하자
Item 9
객체의 생성 및 소멸 과정 중에는 절
대로 가상 함수를 호출하지 말자
객체 생성 및 소멸 과정 중에 절대로 가상 함수를 호출하면 안 되는 이유
1. 호출 결과가 원하는 대로 돌아가지 않을 것이다
2. 이번에 원하는 대로 돌아간다고 해도 다음 번에도 원하는 대로 돌아간다는 보장은 없
기 때문이다.
Item 9 : 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자
- Missile Class와 이를 상속 받은 파생 Class인 LinearMissile이 있다
- LinearMissile의 생성자에서 sayMyType을 호출하면 어떤 일이 일어날까?
- 사실 화면에 찍히길 바라는 것은 “LinearMissile”이다
- 하지만 정작 찍히는 내용은 “Missile”이다
- 왜 이런 일이 일어날까?
- 원칙은 파생 Class에서 기본 Class 부분이 생성되고 있을 때 파생 Class의 가상 함수가
호출 되지 않는다는 것이다.
Item 9 : 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자
- 이유는 파생 Class가 생성될 때 먼저 기본 Class 생성자가 실행 된다
- 이 때 가상 함수가 호출 된다면 파생 Class의 멤버 변수는 초기화 되어 있지 않다
- 따라서 초기화 되어 있지 않은 파생 Class의 멤버 변수를 건드리고 있을 수도 있는 파생
Class의 가상 함수는 호출 되지 않는 것이다
- 만약 건드린다면 이는 프로그램을 미정의 동작으로 몰아넣는 일이 될 것이다
핵심 : 파생 Class의 기본 Class 부분이 생성되는 동안 파생 Class 객체의 타입은 기본 Class
타입이다
핵심 2 : 파생 Class 객체의 소멸자가 불리면 그 즉시 파생 Class는 기본 Class Type으로 취
급 된다. 기본 Class의 소멸자가 호출되면 그 때는 그냥 기본 Class Type이다
핵심 3 : 생성자와 소멸자에서는 어떤 함수가 내부적으로 가상 함수를 불러와서는 안된다
Item 9 : 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자
Item 9의 문제를 해결하는 코드
- 기본 Class인 Missile에 sayMyType을 선언하고 Missile 정보를 매개변수로 설정한다
- 아이템의 type이름을 함수에서 보여준다
- 이렇게 하면 비가상 함수이기 때문에 생성자에서 문제가 생기지 않는다
Item 9 : 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자
Item 10
대입 연산자는 *this를 반환하게 하
자
C++ 대입 연산은 사슬처럼 엮일 수 있습니다
(ex. x = y = z = 15)
다른 특징은 우측 연관 연산인데 위의 예가 다음과 같이 계산 된다
x = (y = (z = 15))
이렇게 좌측으로 좌측으로 대입이 진행 되기 위해서는 대입 연산자가 좌측 타입의 참조자
를
반환해야 한다
이러한 C++ 스타일을 지켜주기 위해서 우리가 어떤 Class에 대입연산자를 선언 했다면
`ClassType&를 반환하자`가 이번 Item에서 하고자 하는 말이다
Item 10 : 대입 연산자는 *this를 반환하게 하자
대입 연산자 구현 코드
선언
구현
기본 대입 연산자뿐만 아니라 operator+= 이나 -=, *= 도 이런 식으로 구현 해주자
Item 10 : 대입 연산자는 *this를 반환하게 하자
Item 11
operator=에서 자기 대입에 대한 처
리가 빠지지 않도록 하자
Item 11 : operator= 에서 자기 대입에 대한 처리가 빠지지 않도록 하자
자기 대입 = 어떤 객체가 자기 자신에 대해 대입연산자를 적용 하는 것
너무 대놓고 써서 이런 일이 안 벌어질 것 같지만 사실 벌어지기 쉽다
// 여기서 i와 j가 같다면?
// 여기서 pM1과 pM2가 가리키는 대상이 같았다면?
Item 11 : operator= 에서 자기 대입에 대한 처리가 빠지지 않도록 하자
operator= 구현을 살펴보면서 이야기를 진행하도록 하자
delete 지점이 자기 대입 연산에서 문제가 되는 이유는 무엇인가?
- 만약 rhs와 this가 같은 것을 가리키고 있다면
- m_W를 delete하는 시점에 rhs의 멤버 m_W가 가진 내용도 delete 된다
Item 11 : operator= 에서 자기 대입에 대한 처리가 빠지지 않도록 하자
대책 1 : 일치성 검사
- this와 일치하는지 살피고 바로 *this를 리턴 하는 방법
단점 : 일치성 검사를 통과 했다고 해도 new에서 문제가 발생한다면?(메모리 부족이라든
가)
m_W는 뭔가 문제가 있는 녀석의 pointer가 된다. 짜증나는 디버깅의 시작
Item 11 : operator= 에서 자기 대입에 대한 처리가 빠지지 않도록 하자
대책 2 : 포인터가 가리키는 객체를 복사한 후 삭제
- new에서 문제가 발생하더라도 m_W가 가리키고 있던 내용은 유지 되어 있으므로(복사
를 통해서) delete 시 문제가 발생하지 않음
Item 11 : operator= 에서 자기 대입에 대한 처리가 빠지지 않도록 하자
대책 3-1 : copy and swap
- tmp로 rhs를 복사한다음 swap하는 형식인데 자세한 내용은 item 29에서 다룬다
- 여기에서는 어떻게 구현하는지만 알아 도록 한다
Item 11 : operator= 에서 자기 대입에 대한 처리가 빠지지 않도록 하자
대책 3-2 : copy and swap
- 대책 3-1과 다른 점은 값에 의한 전달이 이루어진다는 것이다
- 이도 역시 차후에 자세히 다루도록 하고 여기서는 구현 방식만 알아두기로 하자
Item 12
객체의 모든 부분을 빠짐없이 복사
하자
Item 12 : 객체의 모든 부분을 빠짐 없이 복사하자
Item 5에서 다루었던 것처럼 때에 따라서 컴파일러는 복사 생성자나 복사 대입 생성자를
스스로 만들기도 한다. 이 때 컴파일러는 다음의 대원칙을 지킨다
`복사 되는 객체가 가지고 있는 데이터를 빠짐없이 복사 한다`
우리가 복사 생성자나 복사 대입 연산자를 만든다는 것은 컴파일러가 만들어 주는 복사,
복사 대입 생성자의 어느 부분이 마음에 들지 않아서이다. 그러면 컴파일러도 우리를 마
음에 들어 하지 않는다. 우리가 잘못된 복사 생성자나 복사 대입 연산자를 만들어 거의 확
실히 틀린 경우에도 컴파일러는 이를 잡아주지 않는다
그래서 우리는 멤버 변수를 하나 추가할 때마다 복사 생성자, 복사 대입 연산자에도 해당
멤버를 복사하는 내용을 추가해줘야 한다
Item 12 : 객체의 모든 부분을 빠짐 없이 복사하자
새롭게 생긴 멤버 변수들의 대입 부분을 복사 생성자, 복사 대입 연산자에 빠짐 없이 넣었
다고 생각 해도 완전하게 만든 것은 아니다. 상속과 관련된 부분에서 문제가 생길 수 있는
데 다음 코드를 살펴 보자
Missile Class를 상속 받은 LinearMissile의 복사 생성자, 복사 대입 연산자이다
문제가 없어 보입니다만?
문제는 상속 받은 기본 Class가 갖는 데이터에 대한 복사가 이루어지지 않았다는 것이다
Item 12 : 객체의 모든 부분을 빠짐 없이 복사하자
(이어서)
이렇게 되면 복사 생성자를 마치면 LinearMissile 객체가 갖는 Missile Class의 데이터 부분
은 기본 값으로 초기화 된다
복사 대입 생성자는 그렇지 않은데 왜냐하면 복사 대입 생성자는 기본 Class 데이터를 건
드리지 않기 때문이다(초기화 시도를 하지 않는다). 따라서 기존에 저장된 데이터들이 남
아 있게 된다. 이 또한 rhs의 데이터를 다 받아오는 것이 아니기 때문에 의도한 대로 대입이
이루어지고 있는 것이 아니다
이 문제를 해결하기 위해서 코드에 다음 페이지와 같은 내용을 추가 하도록 하자
Item 12 : 객체의 모든 부분을 빠짐 없이 복사하자
기본 클래스의 복사 생성자, 복사 대입 연산자를 초기화 리스트에서 호출
이로써 상속 관계에서도 우리가 원했던 데이터의 빠짐없는 복사 할 수 있게 됨

More Related Content

What's hot

Effective c++ 정리 chapter 4
Effective c++ 정리 chapter 4Effective c++ 정리 chapter 4
Effective c++ 정리 chapter 4연우 김
 
이펙티브 C++ (7~9)
이펙티브 C++ (7~9)이펙티브 C++ (7~9)
이펙티브 C++ (7~9)익성 조
 
이펙티브 C++ 스터디
이펙티브 C++ 스터디이펙티브 C++ 스터디
이펙티브 C++ 스터디quxn6
 
Effective C++ 정리 chapter 3
Effective C++ 정리 chapter 3Effective C++ 정리 chapter 3
Effective C++ 정리 chapter 3연우 김
 
Effective c++ 정리 chapter 6
Effective c++ 정리 chapter 6Effective c++ 정리 chapter 6
Effective c++ 정리 chapter 6연우 김
 
Effective c++ 챕터 2 정리
Effective c++ 챕터 2 정리Effective c++ 챕터 2 정리
Effective c++ 챕터 2 정리연우 김
 
Effective c++ 1~8장
Effective c++ 1~8장 Effective c++ 1~8장
Effective c++ 1~8장 Shin heemin
 
이펙티브 C++ 5,6 장 스터디
이펙티브 C++ 5,6 장 스터디이펙티브 C++ 5,6 장 스터디
이펙티브 C++ 5,6 장 스터디quxn6
 
Effective c++ Chapter1,2
Effective c++ Chapter1,2Effective c++ Chapter1,2
Effective c++ Chapter1,2문익 장
 
More effective c++ chapter1 2_dcshin
More effective c++ chapter1 2_dcshinMore effective c++ chapter1 2_dcshin
More effective c++ chapter1 2_dcshinDong Chan Shin
 
Effective c++ 1,2
Effective c++ 1,2Effective c++ 1,2
Effective c++ 1,2세빈 정
 
Effective c++ 4
Effective c++ 4Effective c++ 4
Effective c++ 4현찬 양
 
Effective c++ chapter1 2_dcshin
Effective c++ chapter1 2_dcshinEffective c++ chapter1 2_dcshin
Effective c++ chapter1 2_dcshinDong Chan Shin
 
More effective c++ 항목30부터
More effective c++ 항목30부터More effective c++ 항목30부터
More effective c++ 항목30부터Dong Chan Shin
 
More effective c++ 2
More effective c++ 2More effective c++ 2
More effective c++ 2현찬 양
 
Effective c++ chapter3, 4 요약본
Effective c++ chapter3, 4 요약본Effective c++ chapter3, 4 요약본
Effective c++ chapter3, 4 요약본Dong Chan Shin
 
More effective c++ chapter1,2
More effective c++ chapter1,2More effective c++ chapter1,2
More effective c++ chapter1,2문익 장
 
More effective c++ 1
More effective c++ 1More effective c++ 1
More effective c++ 1현찬 양
 

What's hot (20)

Effective c++ 정리 chapter 4
Effective c++ 정리 chapter 4Effective c++ 정리 chapter 4
Effective c++ 정리 chapter 4
 
이펙티브 C++ (7~9)
이펙티브 C++ (7~9)이펙티브 C++ (7~9)
이펙티브 C++ (7~9)
 
이펙티브 C++ 스터디
이펙티브 C++ 스터디이펙티브 C++ 스터디
이펙티브 C++ 스터디
 
Effective C++ 정리 chapter 3
Effective C++ 정리 chapter 3Effective C++ 정리 chapter 3
Effective C++ 정리 chapter 3
 
Effective c++ 정리 chapter 6
Effective c++ 정리 chapter 6Effective c++ 정리 chapter 6
Effective c++ 정리 chapter 6
 
Effective c++ 챕터 2 정리
Effective c++ 챕터 2 정리Effective c++ 챕터 2 정리
Effective c++ 챕터 2 정리
 
Effective c++ 1~8장
Effective c++ 1~8장 Effective c++ 1~8장
Effective c++ 1~8장
 
이펙티브 C++ 5,6 장 스터디
이펙티브 C++ 5,6 장 스터디이펙티브 C++ 5,6 장 스터디
이펙티브 C++ 5,6 장 스터디
 
Effective c++ Chapter1,2
Effective c++ Chapter1,2Effective c++ Chapter1,2
Effective c++ Chapter1,2
 
More effective c++ chapter1 2_dcshin
More effective c++ chapter1 2_dcshinMore effective c++ chapter1 2_dcshin
More effective c++ chapter1 2_dcshin
 
Effective c++ 1,2
Effective c++ 1,2Effective c++ 1,2
Effective c++ 1,2
 
Effective c++ 4
Effective c++ 4Effective c++ 4
Effective c++ 4
 
5 6 1
5 6 15 6 1
5 6 1
 
Effective c++ chapter1 2_dcshin
Effective c++ chapter1 2_dcshinEffective c++ chapter1 2_dcshin
Effective c++ chapter1 2_dcshin
 
More effective c++ 항목30부터
More effective c++ 항목30부터More effective c++ 항목30부터
More effective c++ 항목30부터
 
Exception&log
Exception&logException&log
Exception&log
 
More effective c++ 2
More effective c++ 2More effective c++ 2
More effective c++ 2
 
Effective c++ chapter3, 4 요약본
Effective c++ chapter3, 4 요약본Effective c++ chapter3, 4 요약본
Effective c++ chapter3, 4 요약본
 
More effective c++ chapter1,2
More effective c++ chapter1,2More effective c++ chapter1,2
More effective c++ chapter1,2
 
More effective c++ 1
More effective c++ 1More effective c++ 1
More effective c++ 1
 

Viewers also liked

Modern effective c++ 항목 3
Modern effective c++ 항목 3Modern effective c++ 항목 3
Modern effective c++ 항목 3ssuser7c5a40
 
Effective Modern C++ MVA item 18 Use std::unique_ptr for exclusive-ownership ...
Effective Modern C++ MVA item 18 Use std::unique_ptr for exclusive-ownership ...Effective Modern C++ MVA item 18 Use std::unique_ptr for exclusive-ownership ...
Effective Modern C++ MVA item 18 Use std::unique_ptr for exclusive-ownership ...Seok-joon Yun
 
TCP가 실패하는 상황들
TCP가 실패하는 상황들TCP가 실패하는 상황들
TCP가 실패하는 상황들ssuser7c5a40
 
Effective c++ 2
Effective c++ 2Effective c++ 2
Effective c++ 2현찬 양
 
C++정리 스마트포인터
C++정리 스마트포인터C++정리 스마트포인터
C++정리 스마트포인터fefe7270
 
Stagefright recorder part1
Stagefright recorder part1Stagefright recorder part1
Stagefright recorder part1fefe7270
 
실전프로젝트 정서경 양현찬
실전프로젝트 정서경 양현찬실전프로젝트 정서경 양현찬
실전프로젝트 정서경 양현찬현찬 양
 
GPG 1.2 템플릿 메타프로그래밍을 이용한 빠른 수학 연산
GPG 1.2 템플릿 메타프로그래밍을 이용한 빠른 수학 연산GPG 1.2 템플릿 메타프로그래밍을 이용한 빠른 수학 연산
GPG 1.2 템플릿 메타프로그래밍을 이용한 빠른 수학 연산Taeung Ra
 
Dos and Don'ts on the road to Mobility
Dos and Don'ts on the road to MobilityDos and Don'ts on the road to Mobility
Dos and Don'ts on the road to MobilityRuben Goncalves
 
Букмекерское ремесло:
Букмекерское ремесло:Букмекерское ремесло:
Букмекерское ремесло:TopBukmeker
 
Benevole e newsletter jan 2015
Benevole e newsletter jan 2015Benevole e newsletter jan 2015
Benevole e newsletter jan 2015Ramabhau Patil
 
Usability session @ SEI Universidade do Minho
Usability session @ SEI Universidade do MinhoUsability session @ SEI Universidade do Minho
Usability session @ SEI Universidade do MinhoRuben Goncalves
 

Viewers also liked (18)

Modern effective c++ 항목 3
Modern effective c++ 항목 3Modern effective c++ 항목 3
Modern effective c++ 항목 3
 
Effective Modern C++ MVA item 18 Use std::unique_ptr for exclusive-ownership ...
Effective Modern C++ MVA item 18 Use std::unique_ptr for exclusive-ownership ...Effective Modern C++ MVA item 18 Use std::unique_ptr for exclusive-ownership ...
Effective Modern C++ MVA item 18 Use std::unique_ptr for exclusive-ownership ...
 
Modern effective cpp 항목1
Modern effective cpp 항목1Modern effective cpp 항목1
Modern effective cpp 항목1
 
TCP가 실패하는 상황들
TCP가 실패하는 상황들TCP가 실패하는 상황들
TCP가 실패하는 상황들
 
Effective c++ 2
Effective c++ 2Effective c++ 2
Effective c++ 2
 
C++정리 스마트포인터
C++정리 스마트포인터C++정리 스마트포인터
C++정리 스마트포인터
 
Stagefright recorder part1
Stagefright recorder part1Stagefright recorder part1
Stagefright recorder part1
 
Titanic with r
Titanic with rTitanic with r
Titanic with r
 
실전프로젝트 정서경 양현찬
실전프로젝트 정서경 양현찬실전프로젝트 정서경 양현찬
실전프로젝트 정서경 양현찬
 
GPG 1.2 템플릿 메타프로그래밍을 이용한 빠른 수학 연산
GPG 1.2 템플릿 메타프로그래밍을 이용한 빠른 수학 연산GPG 1.2 템플릿 메타프로그래밍을 이용한 빠른 수학 연산
GPG 1.2 템플릿 메타프로그래밍을 이용한 빠른 수학 연산
 
Emisoft
EmisoftEmisoft
Emisoft
 
My Assignment.pptx
My Assignment.pptxMy Assignment.pptx
My Assignment.pptx
 
Dos and Don'ts on the road to Mobility
Dos and Don'ts on the road to MobilityDos and Don'ts on the road to Mobility
Dos and Don'ts on the road to Mobility
 
развеселый торг
развеселый торгразвеселый торг
развеселый торг
 
Presentation11
Presentation11Presentation11
Presentation11
 
Букмекерское ремесло:
Букмекерское ремесло:Букмекерское ремесло:
Букмекерское ремесло:
 
Benevole e newsletter jan 2015
Benevole e newsletter jan 2015Benevole e newsletter jan 2015
Benevole e newsletter jan 2015
 
Usability session @ SEI Universidade do Minho
Usability session @ SEI Universidade do MinhoUsability session @ SEI Universidade do Minho
Usability session @ SEI Universidade do Minho
 

Similar to Effective c++chapter1 and2

Effective c++chapter4
Effective c++chapter4Effective c++chapter4
Effective c++chapter4성연 김
 
Effective cpp
Effective cppEffective cpp
Effective cppTonyCms
 
Effective c++ 3
Effective c++ 3Effective c++ 3
Effective c++ 3현찬 양
 
Effective C++ Chapter 1 Summary
Effective C++ Chapter 1 SummaryEffective C++ Chapter 1 Summary
Effective C++ Chapter 1 SummarySeungYeonChoi10
 
Effective c++ chapter7_8_9_dcshin
Effective c++ chapter7_8_9_dcshinEffective c++ chapter7_8_9_dcshin
Effective c++ chapter7_8_9_dcshinDong Chan Shin
 
Chapter5 ~ 6
Chapter5 ~ 6Chapter5 ~ 6
Chapter5 ~ 6Injae Lee
 
More effective c++ 챕터3~4ppt
More effective c++ 챕터3~4pptMore effective c++ 챕터3~4ppt
More effective c++ 챕터3~4pptInjae Lee
 
Working Effectively With Legacy Code - xp2005
Working Effectively With Legacy Code - xp2005Working Effectively With Legacy Code - xp2005
Working Effectively With Legacy Code - xp2005Ryan Park
 
Api design for c++ ch3 pattern
Api design for c++ ch3 patternApi design for c++ ch3 pattern
Api design for c++ ch3 patternjinho park
 
Api design for c++ pattern
Api design for c++ patternApi design for c++ pattern
Api design for c++ patternjinho park
 
More effective c++ chapter4 이후 항목 29까지
More effective c++ chapter4 이후 항목 29까지More effective c++ chapter4 이후 항목 29까지
More effective c++ chapter4 이후 항목 29까지Dong Chan Shin
 
Effective C++ Chapter 2 Summary
Effective C++ Chapter 2 SummaryEffective C++ Chapter 2 Summary
Effective C++ Chapter 2 SummarySeungYeonChoi10
 
More effective c++ chapter3 4
More effective c++ chapter3 4More effective c++ chapter3 4
More effective c++ chapter3 4Dong Chan Shin
 
Tcpl 12장 파생클래스
Tcpl 12장 파생클래스Tcpl 12장 파생클래스
Tcpl 12장 파생클래스재정 이
 
More Effective C++ 4주차
More Effective C++ 4주차More Effective C++ 4주차
More Effective C++ 4주차Injae Lee
 
Effective c++ chapter5 6_ 131039 신동찬
Effective c++ chapter5 6_ 131039 신동찬Effective c++ chapter5 6_ 131039 신동찬
Effective c++ chapter5 6_ 131039 신동찬Dong Chan Shin
 
카사 공개세미나1회 W.E.L.C.
카사 공개세미나1회  W.E.L.C.카사 공개세미나1회  W.E.L.C.
카사 공개세미나1회 W.E.L.C.Ryan Park
 

Similar to Effective c++chapter1 and2 (20)

Effective c++chapter4
Effective c++chapter4Effective c++chapter4
Effective c++chapter4
 
Effective cpp
Effective cppEffective cpp
Effective cpp
 
Effective c++ 3
Effective c++ 3Effective c++ 3
Effective c++ 3
 
Effective C++ Chapter 1 Summary
Effective C++ Chapter 1 SummaryEffective C++ Chapter 1 Summary
Effective C++ Chapter 1 Summary
 
Effective c++ chapter7_8_9_dcshin
Effective c++ chapter7_8_9_dcshinEffective c++ chapter7_8_9_dcshin
Effective c++ chapter7_8_9_dcshin
 
Chapter5 ~ 6
Chapter5 ~ 6Chapter5 ~ 6
Chapter5 ~ 6
 
More effective c++ 챕터3~4ppt
More effective c++ 챕터3~4pptMore effective c++ 챕터3~4ppt
More effective c++ 챕터3~4ppt
 
Working Effectively With Legacy Code - xp2005
Working Effectively With Legacy Code - xp2005Working Effectively With Legacy Code - xp2005
Working Effectively With Legacy Code - xp2005
 
Api design for c++ ch3 pattern
Api design for c++ ch3 patternApi design for c++ ch3 pattern
Api design for c++ ch3 pattern
 
Api design for c++ pattern
Api design for c++ patternApi design for c++ pattern
Api design for c++ pattern
 
More effective c++ chapter4 이후 항목 29까지
More effective c++ chapter4 이후 항목 29까지More effective c++ chapter4 이후 항목 29까지
More effective c++ chapter4 이후 항목 29까지
 
1 2 1
1 2 11 2 1
1 2 1
 
MEC++ 5
MEC++ 5MEC++ 5
MEC++ 5
 
Effective C++ Chapter 2 Summary
Effective C++ Chapter 2 SummaryEffective C++ Chapter 2 Summary
Effective C++ Chapter 2 Summary
 
EC 789
EC 789EC 789
EC 789
 
More effective c++ chapter3 4
More effective c++ chapter3 4More effective c++ chapter3 4
More effective c++ chapter3 4
 
Tcpl 12장 파생클래스
Tcpl 12장 파생클래스Tcpl 12장 파생클래스
Tcpl 12장 파생클래스
 
More Effective C++ 4주차
More Effective C++ 4주차More Effective C++ 4주차
More Effective C++ 4주차
 
Effective c++ chapter5 6_ 131039 신동찬
Effective c++ chapter5 6_ 131039 신동찬Effective c++ chapter5 6_ 131039 신동찬
Effective c++ chapter5 6_ 131039 신동찬
 
카사 공개세미나1회 W.E.L.C.
카사 공개세미나1회  W.E.L.C.카사 공개세미나1회  W.E.L.C.
카사 공개세미나1회 W.E.L.C.
 

Effective c++chapter1 and2

  • 2. Chapter 1 C++에 왔으면 C++의 법을 따릅시다
  • 3. Item 1 C++을 언어의 연합체로 보자
  • 4. Item 1 : C++을 언어의 연합체로 보자 - C++은 어렵다 - 그래도 C++의 산을 넘는 대원칙이 있다 - 바로 C++을 언어의 연합체로 보는 것 - C++의 여러 문제들과 맞닿게 되었을 때, - C++이 4개의 하위 언어로 구성됨을 알고 - 문제가 어떤 하위 언어와 연관되어 있는지 알면, 더 쉽게 문제를 해결 할 수 있다.
  • 5. Item 1 : C++을 언어의 연합체로 보자 C++을 구성하는 4개의 하위 언어들 1. C - 기본이다. 블록, 선행처리자, 기본제공 데이터타입, 배열, 포인터 등이 C로부터 왔다 2. 객체지향의 C++ - 클래스를 쓰는 ‘C’ 포함, 캡슐화, 다형성, 가상 함수 등등 3. 템플릿 C++ - 뒤에서 다룰 템플릿 메타 프로그래밍과도 연관 4. STL - 템플릿 라이브러리, 독특한 사용규약들을 알아야 한다
  • 6. Item 2 #define을 쓰려거든 const, Inline, enum을 떠올리자
  • 7. Item 2 : #define을 쓰려거든 const, enum, inline 프로그래밍을 하다 보면 #define을 쓰는 경우가 부지기수다 그러나 #define에는 단점이 있다 대체 수단인 const, enum, inline을 사용하도록 하자. 되도록이면
  • 8. Item 2 : #define을 쓰려거든 const, enum, inline 단점 1 : #define은 기호가 아니다 ex) #define ASPECT_RATIO 1.653 실제로 코드에 ASPECT_RATIO가 쓰이는 부분은 기호가 아닌 1.653이라는 수가 들어간다. 어떤 곳에서 에러가 나면 1.653이라는 메시지가 있을 뿐! 이게 우리가 쓴 그 ASPECT_RATIO인지 계산된 어떤 값인지 알 길이 없다 대체재 : const const double AspectRatio = 1.653; // 기호이자 상수
  • 9. Item 2 : #define을 쓰려거든 const, enum, inline 단점 2 : #define은 사본을 많이 만든다 ex) #define ASPECT_RATIO 1.653 우리가 ASPECT_RATIO라고 써 놓은 모든 코드코드마다 사본이 생긴다 그러나 대체할 것들로 사용한다면? 메모리는 한 번 사용될 뿐이다 대체재 : const const double AspectRatio = 1.653; // 사본은 딱 한 개만 생긴다
  • 10. Item 2 : #define을 쓰려거든 const, enum, inline 단점 3 : #define은 클래스 상수를 정의할 수도, 캡슐화 혜택을 받을 수도 없다 #define은 private 캡슐화가 되지 않는다. 전역으로 선언된 녀석이다. 대체재 : static const 위의 예에서 numTurns는 클래스 상수로, 유효범위가 클래스이고, 사본 개수가 한 개를 넘지 못한다.
  • 11. Item 2 : #define을 쓰려거든 const, enum, inline static const도 대체할 수 있는 대체재 : enum 컴파일러에 따라서 사용상의 주의가 필요할 수 있는 static const와 다른 간단한 enum. #define처럼 작동 하면서(주소를 얻을 수 없다. 참조도.) 컴파일러에 따른 주의를 기울이 지 않아도 된다. 그리고 많은 코드가 이렇게 쓰여 있다.
  • 12. Item 2 : #define을 쓰려거든 const, enum, inline 단점 4 : #define의 매크로 함수는 단점이 많다. 각 인자마다 괄호를 씌워줘야 하는 것 하며, 잘 모르고 사용할 때 발생하는 문제들에 대 한 정확한 인지가 필요하다는 것 등등. 대체재 : 인라인 템플릿 괄호를 많이 쓸 필요도 없고, 유효범위는 그대로 지켜지면서, 인자가 여러 번 평가 될 수 도 있는 상황이 만들어지지 않는다.
  • 13. Item 3 낌새만 보이면 const를 들이대자
  • 14. Item 3 : 낌새만 보이면 const를 들이대 보자 BIG 장점 : 1. 의미적인 제약 = 컴파일러에 의해 외부변경을 불가능하게 한다. 2. 약속 = 다른 프로그래머와 제작자가 의도를 나눌 수 있다. const의 위치와 의미 = 포인터 왼쪽 const면 데이터를 상수화, 포인터 오른쪽 const면 포인터 자체를 상수화
  • 15. STL과 const 위치간의 관계 const STL::iterator 라면 const가 포인터 오른쪽에 붙은 상황, STL::const_iterator라면 const가 포인터 왼쪽에 붙은 상황이다 Item 3 : 낌새만 보이면 const를 들이대 보자
  • 16. 함수 선언과 const 위치간의 관계 1. 함수 반환 값에 붙는 경우 함수 계산 후 나오는 값이 const 2. const를 매개변수와 함께 사용하는 경우 지역 변수와 const를 같이 쓰는 경우와 같다. Item 3 : 낌새만 보이면 const를 들이대 보자
  • 17. 함수 선언과 const 위치간의 관계 3. const가 멤버 함수 괄호 뒤에 붙는 경우 물리적 상수성을 지켜준다 = 클래스의 데이터 멤버 변수를 하나도 바꾸지 않는다 논리적 상수성을 지켜준다 = const 함수 안에서 객체의 데이터를 바꿀 수 있게 하되 사용자 측에서 알아차리지 못하게 하면 상수 멤버 자격이 있 게 하자 Item 3 : 낌새만 보이면 const를 들이대 보자
  • 18. 논리적 상수성(이어서) mutable 선언은 논리적 상수성이 가능하도록 합니다 Item 3 : 낌새만 보이면 const를 들이대 보자
  • 19. Item 4 객체를 사용하기 전에 반드시 그 객 체를 초기화 하자
  • 20. 제목이 내용이다. 사용하기 전에 모든 객체를 초기화 하자. 그런데 대입을 초기화와 헷갈리지 말자 어떤 클래스의 생성자에서 대입(=)을 하는 것은 초기화가 아니다. //대입 //초기화(초기화 리스트 사용) Item 4 : 객체를 사용하기 전에 반드시 그 객체를 초기화 하자
  • 21. 제목이 내용이다. 사용하기 전에 모든 객체를 초기화 하자. 그런데 대입을 초기화와 헷갈리지 말자 어떤 클래스의 생성자에서 대입(=)을 하는 것은 초기화가 아니다. 대입(기본생성자 + 복사대입연산자) 초기화(생성자 한번) Item 4 : 객체를 사용하기 전에 반드시 그 객체를 초기화 하자
  • 22. 초기화 순서 : 기본 클래스는 파생 클래스보다 먼저 초기화 된다 클래스 데이터 멤버는 선언된 순서대로 초기화 된다 ☞ 비지역 정적 객체의 초기화 순서는 개별 번역 단위에서 정해진다 정적 객체란? 1. 전역 객체 2. 네임스페이스 유효범위에서 정의된 객체 3. 클래스 안에서 static으로 정의된 객체 4. 함수 안에서 static으로 정의된 객체 ( 얘만 지역 정적 객체이다) 5. 파일 유효범위 안에서 stati으로 정의된 객체 번역 단위란? - object file을 만드는 바탕이 되는 소스코드, 소스 파일 하나 + #include 코드 내용 결론 : 비지역정적 개체 초기화 순서는 알기 어렵다. Item 4 : 객체를 사용하기 전에 반드시 그 객체를 초기화 하자
  • 24. Item 5 C++가 은근슬쩍 만들어 호출해 버 리는 함수들을 주의하자
  • 25. C++ 컴파일러가 사용자가 선언해 놓지 않았지만 필요한 경우 만드는 함수 - 생성자, 복사 생성자, 복사대입 연산자, 소멸자 - 모두 public 이면서 inline이다 - 여기서 필요한 경우란 다음과 같은 상황이다 - 이렇게 만들어진 함수들은 어떤 일을 하는가? - 기본 생성자, 기본 소멸자 -> “배후의 코드"를 깔 수 있는 자리 마련, - 여기서 배후의 코드란 데이터 멤버의 기본 클래스 및 비정적 데이터 멤버의 생성자와 소멸자를 호출하는 코드등을 말한다 - 복사 생성자, 복사대입연산자 -> 원본 객체의 비정적 데이터 사본을 사본 객체쪽으로 복사 - 예외 : 이미 어떤 생성자나 소멸자가 만들어져 있다면 컴파일러는 그에 해당하는 생성 Item 5 : C++가 은근슬쩍 만들어 호출해 버리는 함수들을 주의하자
  • 26. C++ 컴파일러가 복사 생성자, 복사 대입 연산자를 만들지 않는 경우 - 컴파일러는 똑똑해서 자신이 만들 복사, 복사대입 생성자가 동작 후 최종 결과 코드가 ‘적법’하고 ‘이치에 닿는’ 코드가 아니라면 자동 생성을 거부한다 - 예를 들어 Widget Class에 다음과 같은 멤버 변수가 있다고 하자 - 생성자를 통해서 다음과 같은 2개의 객체를 만들어 냈다고 하자 - 여기서 대입 연산을 한다면? (이어서 계속) Item 5 : C++가 은근슬쩍 만들어 호출해 버리는 함수들을 주의하자
  • 27. (이어서)C++ 컴파일러가 복사, 복사 대입 생성자를 만들지 않는 경우 - 먼저 w1에 있는 name이 참조이기 때문에 w2에 있는 name을 복사할 수 없다 - 왜냐하면 c++에서 참조는 한 번 참조한 대상을 바꿀 수 없게 해놓았기 때문이다 - 따라서 대입연산이 애매하다 - 또 w1의 value에 w2의 value를 넣을 수도 없다 - 왜냐하면 const로 선언되어있기 때문이다. - 이런 내용을 컴파일러는 알고 있기 때문에 자동으로 복사대입 연산을 만들지 않는다 - 정리하면 컴파일러는 ‘적합성’을 가지고 있고 ‘이치에 맞는’ 코드인지를 살필 수 있다 - 그리고 맞지 않으면 해당 생성자를 만들지 않는다 - 구체적으로 참조 멤버를 가지고 있다면 항상 복사대입연산자를 개발자 스스로 만들어야 한다 Item 5 : C++가 은근슬쩍 만들어 호출해 버리는 함수들을 주의하자
  • 28. Item 6 컴파일러가 만들어낸 함수가 필요 없으면 사용을 못하게 하자
  • 29. 앞서 컴파일러가 개발자도 모르게 만들어내는 함수가 있음을 공부했다 - 그런데 컴파일러가 만들어내는 기본 함수들을 사용하고 싶지 않다면? - 그저 그 함수를 선언하지 않는 것으로 그만인가? - 아니다. 컴파일러는 이내 선언 되지 않은 그 함수를 만들어낼 것이다 - 이 문제의 해결 방법은 무엇일까? 1번 : 함수를 private로 선언하고 오직 선언만 한다. no 정의 - 컴파일러가 만들어 내는 복사, 복사 대입생성자는 public이다 - 우리는 명시적으로 복사, 복사 대입생성자를 private에서 선언할 수 있다 - 이렇게 선언함으로써 외부로부터의 대입연산 호출을 막을 수 있다 - 하지만 해당 클래스가 friend 선언에 의해 함수 호출을 막을 수 없는 상황이라면? - 이를 대비하기 위해 오직 헤더에서 선언만 하고 실제로 정의는 하지 않는 것이다 - 이것이 가능한 이유는 누군가 해당 함수를 사용하려고 할 때 정의되지 않았다면 에러가 나기 때문이다 Item 6 : 컴파일러가 만들어낸 함수가 필요 없으면 사용을 못하게 하자
  • 30. 2번 : 1번과 같은 방식으로 한다. 그런데 하나의 클래스를 그와 같이 만들고 이를 상속받아 사 용 하게 한다 - 말은 어렵지만 구체적으로 보면 쉽다. - 일단 class를 하나 구현한다. 이 class는 오직 복사 방지만을 위해 존재한다 - 이제 이 class를 사용하고자 하는 클래스가 상속 받자 - 그러면 해당 class는 복사, 복사대입 연산자를 사용할 수 없게 된다 Item 6 : 컴파일러가 만들어낸 함수가 필요 없으면 사용을 못하게 하자
  • 31. Item 7 다형성을 가진 기본 클래스에서는 소멸자를 반드시 가상 소멸자로!
  • 32. Missile을 상속 받는 LinearMissle과 BulletMissile의 Class가 있다 - 사용자가 각 missile에 접근하고자 할 때가 있다면 다음과 같은 함수를 만들 수 있다 - 이 함수는 파생클래스에 대한 기본 클래스의 포인터를 반환하는 함수이다 - 실제로 이 함수를 사용한다면 missile을 객체의 포인터를 얻을 수 있을 것이고 이제 그 미사일이 필요치 않다면 해당 내용을 지워야 할 것이다 - 바로 아래처럼 Item 7 : 다형성을 가진 기본클래스에서는 소멸자를 반드시 가상 소멸자로!
  • 33. (Missile 계통(Linear나 Bullet 같은) 클래스로부터 동적 으로 할당된 객체의 포인터) - 이렇게 myMissile을 지웠을 때 생길 수 있는 문제는 무엇인가? - 문제의 원인은 아래의 두 가지가 함께 나타나기 때문에 발생한다 - 1번 : 기본 클래스의 포인터를 가지고 있다(내부에 할당된 것은 파생 클래스) - 2번 : 기본 클래스의 소멸자가 비가상 소멸자이다 - C++ 규정에 의하면 기본 클래스의 포인터를 통해 파생클래스가 삭제될 때 그 기본 클 래스에 비가상 소멸자가 들어있으면 프로그램의 동작은 미정의 사항이다 - 보통은 파생클래스만 있는 부분이 삭제되지 않는다 - 이로 인해 자원이 낭비되고, 디버깅은 어려워지며, 짜증은 유발된다 Item 7 : 다형성을 가진 기본클래스에서는 소멸자를 반드시 가상 소멸자로!
  • 34. - 이 문제의 해결 방법 : 기본 클래스의 소멸자를 가상 소멸자로 선언 한다 - 그러면 기본 클래스의 포인터를 들고 있을지라도 그 내용이 파생 클래스라면 파생 클래스의 소멸자라 불린다 - 우리는 이제 가상소멸자가 없는 클래스를 다음과 같이 생각할 수 있고 해야 한다 - “이 클래스는 기본 클래스로 절대 쓰이지 않을 것입니다” - 가상소멸자로 도배하는 것 혹은 전혀 사용하지 않는 것 모두 나쁜 습관이다 - 기준을 잡자 - 해당 클래스에 가상 함수가 하나라도 있다면 가상 소멸자를 선언한다 Item 7 : 다형성을 가진 기본클래스에서는 소멸자를 반드시 가상 소멸자로!
  • 35. 주의 할 것 - 어떤 기본 타입들은 가상 소멸자가 선언되어 있지 않다 - 따라서 상속에 주의가 필요하다 - 해당 타입은 std::string이나 STL 컨테이너등이 그것이다 - 위의 예처럼 가상소멸자가 선언되어 있지 않은 타입을 상속받아서 쓰는 일은 없어야 한다 마지막 포인트 - 어떤 Class를 추상 Class로 만들고 싶다면 소멸자를 순수 가상 소멸자로 선언하자 - 추상 Class의 목적 = 기본 Class로 사용 = 따라서 소멸자는 가상 소멸자여야 한다 - 추상 Class의 목적 + 기본 Class의 목적을 동시에 만족시키는 순수 가상 소멸자!!! Item 7 : 다형성을 가진 기본클래스에서는 소멸자를 반드시 가상 소멸자로!
  • 36. Item 8 예외가 소멸자를 떠나지 못하도록 하자
  • 37. 필자의 걱정 : 소멸자 좋지. 그런데 소멸자가 불려서 작동하는 동안, 즉 그 Class가 없어 질 때 오류가 발생하면 어쩌지? 이미 Class도 사라져서 다시 호출할 수도 없는데? 그리 고 C++은 소멸자에서 오류가 발생하면 그 예외가 던져지도록(별다른 조치가 취해지지 않게) 놔두는데? 흐음 해결방식 1 : 소멸자 도중에 예외가 발생하면 프로그램을 끝내자! 단점 : 에러 발생시 프로그램을 종료시켜도 된다면 가능, 하지만 프로그램을 계속 가동 되게 해야 한다면? Item 8 : 예외가 소멸자를 떠나지 못하도록 하자
  • 38. 해결방식 2 : 소멸자 도중에 예외가 발생하면 예외를 삼키자 단점 : 문제는 해결되지 않는다. 이렇게 하면 이후의 실행이 보장되도록 해야 하는데 쉽지 않다 Item 8 : 예외가 소멸자를 떠나지 못하도록 하자
  • 39. 해결방식 3 : 소멸자에서 예외가 일어나서는 안된다. 예외가 일어난다면 그건 다른 함수에 서 이미 일어났어야 한다. 해결방식 3을 한마디로 정의하면 확인 사살이다 ex) WidgetManager와 Widget Class가 있다 Widget Class에서 close()함수를 호출한다. 그리고 close()가 잘 적용 되었는지 bool 값을로 저장해 놓는다. WidgetManager Class에서는 소멸자에서 Widget Class의 bool 값을 확인한다. 만약 bool 값 이 false라면 다시 하번 close() 함수를 호출한다. 만약 WidgetManager에서도 오류가 발생하면 해결방식1이나 2로 다시 가야 한다. 하지만 이 방법은 적어도 2번의 오류 체크를 하기 때문에 해결방식 1,2보다는 더 나은 방식이라 할 수 있다. Item 8 : 예외가 소멸자를 떠나지 못하도록 하자
  • 40. Item 9 객체의 생성 및 소멸 과정 중에는 절 대로 가상 함수를 호출하지 말자
  • 41. 객체 생성 및 소멸 과정 중에 절대로 가상 함수를 호출하면 안 되는 이유 1. 호출 결과가 원하는 대로 돌아가지 않을 것이다 2. 이번에 원하는 대로 돌아간다고 해도 다음 번에도 원하는 대로 돌아간다는 보장은 없 기 때문이다. Item 9 : 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자
  • 42. - Missile Class와 이를 상속 받은 파생 Class인 LinearMissile이 있다 - LinearMissile의 생성자에서 sayMyType을 호출하면 어떤 일이 일어날까? - 사실 화면에 찍히길 바라는 것은 “LinearMissile”이다 - 하지만 정작 찍히는 내용은 “Missile”이다 - 왜 이런 일이 일어날까? - 원칙은 파생 Class에서 기본 Class 부분이 생성되고 있을 때 파생 Class의 가상 함수가 호출 되지 않는다는 것이다. Item 9 : 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자
  • 43. - 이유는 파생 Class가 생성될 때 먼저 기본 Class 생성자가 실행 된다 - 이 때 가상 함수가 호출 된다면 파생 Class의 멤버 변수는 초기화 되어 있지 않다 - 따라서 초기화 되어 있지 않은 파생 Class의 멤버 변수를 건드리고 있을 수도 있는 파생 Class의 가상 함수는 호출 되지 않는 것이다 - 만약 건드린다면 이는 프로그램을 미정의 동작으로 몰아넣는 일이 될 것이다 핵심 : 파생 Class의 기본 Class 부분이 생성되는 동안 파생 Class 객체의 타입은 기본 Class 타입이다 핵심 2 : 파생 Class 객체의 소멸자가 불리면 그 즉시 파생 Class는 기본 Class Type으로 취 급 된다. 기본 Class의 소멸자가 호출되면 그 때는 그냥 기본 Class Type이다 핵심 3 : 생성자와 소멸자에서는 어떤 함수가 내부적으로 가상 함수를 불러와서는 안된다 Item 9 : 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자
  • 44. Item 9의 문제를 해결하는 코드 - 기본 Class인 Missile에 sayMyType을 선언하고 Missile 정보를 매개변수로 설정한다 - 아이템의 type이름을 함수에서 보여준다 - 이렇게 하면 비가상 함수이기 때문에 생성자에서 문제가 생기지 않는다 Item 9 : 객체 생성 및 소멸 과정 중에는 절대로 가상 함수를 호출하지 말자
  • 45. Item 10 대입 연산자는 *this를 반환하게 하 자
  • 46. C++ 대입 연산은 사슬처럼 엮일 수 있습니다 (ex. x = y = z = 15) 다른 특징은 우측 연관 연산인데 위의 예가 다음과 같이 계산 된다 x = (y = (z = 15)) 이렇게 좌측으로 좌측으로 대입이 진행 되기 위해서는 대입 연산자가 좌측 타입의 참조자 를 반환해야 한다 이러한 C++ 스타일을 지켜주기 위해서 우리가 어떤 Class에 대입연산자를 선언 했다면 `ClassType&를 반환하자`가 이번 Item에서 하고자 하는 말이다 Item 10 : 대입 연산자는 *this를 반환하게 하자
  • 47. 대입 연산자 구현 코드 선언 구현 기본 대입 연산자뿐만 아니라 operator+= 이나 -=, *= 도 이런 식으로 구현 해주자 Item 10 : 대입 연산자는 *this를 반환하게 하자
  • 48. Item 11 operator=에서 자기 대입에 대한 처 리가 빠지지 않도록 하자
  • 49. Item 11 : operator= 에서 자기 대입에 대한 처리가 빠지지 않도록 하자 자기 대입 = 어떤 객체가 자기 자신에 대해 대입연산자를 적용 하는 것 너무 대놓고 써서 이런 일이 안 벌어질 것 같지만 사실 벌어지기 쉽다 // 여기서 i와 j가 같다면? // 여기서 pM1과 pM2가 가리키는 대상이 같았다면?
  • 50. Item 11 : operator= 에서 자기 대입에 대한 처리가 빠지지 않도록 하자 operator= 구현을 살펴보면서 이야기를 진행하도록 하자 delete 지점이 자기 대입 연산에서 문제가 되는 이유는 무엇인가? - 만약 rhs와 this가 같은 것을 가리키고 있다면 - m_W를 delete하는 시점에 rhs의 멤버 m_W가 가진 내용도 delete 된다
  • 51. Item 11 : operator= 에서 자기 대입에 대한 처리가 빠지지 않도록 하자 대책 1 : 일치성 검사 - this와 일치하는지 살피고 바로 *this를 리턴 하는 방법 단점 : 일치성 검사를 통과 했다고 해도 new에서 문제가 발생한다면?(메모리 부족이라든 가) m_W는 뭔가 문제가 있는 녀석의 pointer가 된다. 짜증나는 디버깅의 시작
  • 52. Item 11 : operator= 에서 자기 대입에 대한 처리가 빠지지 않도록 하자 대책 2 : 포인터가 가리키는 객체를 복사한 후 삭제 - new에서 문제가 발생하더라도 m_W가 가리키고 있던 내용은 유지 되어 있으므로(복사 를 통해서) delete 시 문제가 발생하지 않음
  • 53. Item 11 : operator= 에서 자기 대입에 대한 처리가 빠지지 않도록 하자 대책 3-1 : copy and swap - tmp로 rhs를 복사한다음 swap하는 형식인데 자세한 내용은 item 29에서 다룬다 - 여기에서는 어떻게 구현하는지만 알아 도록 한다
  • 54. Item 11 : operator= 에서 자기 대입에 대한 처리가 빠지지 않도록 하자 대책 3-2 : copy and swap - 대책 3-1과 다른 점은 값에 의한 전달이 이루어진다는 것이다 - 이도 역시 차후에 자세히 다루도록 하고 여기서는 구현 방식만 알아두기로 하자
  • 55. Item 12 객체의 모든 부분을 빠짐없이 복사 하자
  • 56. Item 12 : 객체의 모든 부분을 빠짐 없이 복사하자 Item 5에서 다루었던 것처럼 때에 따라서 컴파일러는 복사 생성자나 복사 대입 생성자를 스스로 만들기도 한다. 이 때 컴파일러는 다음의 대원칙을 지킨다 `복사 되는 객체가 가지고 있는 데이터를 빠짐없이 복사 한다` 우리가 복사 생성자나 복사 대입 연산자를 만든다는 것은 컴파일러가 만들어 주는 복사, 복사 대입 생성자의 어느 부분이 마음에 들지 않아서이다. 그러면 컴파일러도 우리를 마 음에 들어 하지 않는다. 우리가 잘못된 복사 생성자나 복사 대입 연산자를 만들어 거의 확 실히 틀린 경우에도 컴파일러는 이를 잡아주지 않는다 그래서 우리는 멤버 변수를 하나 추가할 때마다 복사 생성자, 복사 대입 연산자에도 해당 멤버를 복사하는 내용을 추가해줘야 한다
  • 57. Item 12 : 객체의 모든 부분을 빠짐 없이 복사하자 새롭게 생긴 멤버 변수들의 대입 부분을 복사 생성자, 복사 대입 연산자에 빠짐 없이 넣었 다고 생각 해도 완전하게 만든 것은 아니다. 상속과 관련된 부분에서 문제가 생길 수 있는 데 다음 코드를 살펴 보자 Missile Class를 상속 받은 LinearMissile의 복사 생성자, 복사 대입 연산자이다 문제가 없어 보입니다만? 문제는 상속 받은 기본 Class가 갖는 데이터에 대한 복사가 이루어지지 않았다는 것이다
  • 58. Item 12 : 객체의 모든 부분을 빠짐 없이 복사하자 (이어서) 이렇게 되면 복사 생성자를 마치면 LinearMissile 객체가 갖는 Missile Class의 데이터 부분 은 기본 값으로 초기화 된다 복사 대입 생성자는 그렇지 않은데 왜냐하면 복사 대입 생성자는 기본 Class 데이터를 건 드리지 않기 때문이다(초기화 시도를 하지 않는다). 따라서 기존에 저장된 데이터들이 남 아 있게 된다. 이 또한 rhs의 데이터를 다 받아오는 것이 아니기 때문에 의도한 대로 대입이 이루어지고 있는 것이 아니다 이 문제를 해결하기 위해서 코드에 다음 페이지와 같은 내용을 추가 하도록 하자
  • 59. Item 12 : 객체의 모든 부분을 빠짐 없이 복사하자 기본 클래스의 복사 생성자, 복사 대입 연산자를 초기화 리스트에서 호출 이로써 상속 관계에서도 우리가 원했던 데이터의 빠짐없는 복사 할 수 있게 됨