0
We are!
Digging Effective C++
Chapter 1
Chapter 2
NEXT 1기
131039 신동찬
We are!
항목 1. United State of C++!
C++는 더 이상 단순 C with Classes가 아니며, 여러 개의 하위 언어를 포함하는 개념
쓰는 영역에 따라 프로그래밍도 다변화
C++
객체 지향 개...
We are!
항목 2. 낭비벽을 가진 #define
전처리에서 사용하는 #define을 버리고 상수를 사용하자
이유1) 코드 전체 영역에서 변경이 발생하는 #define은 낭비
이유2) #define에는 유효 범위가 ...
We are!
항목 2. 낭비벽을 가진 #define
#define MAX_BUFF 1024 const int MAX_BUFF = 1024;
• 컴파일 에러시 확인이 어려움
컴파일에서는 이미 숫자 상수로 대체
• 코드의...
We are!
항목 2. 낭비벽을 가진 #define
클래스 내부 상수 생성 및 활용 방법
//in header file
class CostEstimate {
private:
Static const double Fudg...
We are!
항목 2. 낭비벽을 가진 #define
#define 매크로의 위험성을 피하라
일반 사례)
#define CALL_WITH_MAX(a, b) f( (a) > (b) ? (a) : (b) )
Int a = ...
We are!
항목 3. 라면스프 같은 const
Const는 그 어떤 외부 변경도 막아냄(변경 불가)
컴파일러가 제약을 확인하고 지켜 절대 변경되지 않음
용도1) 컴파일러가 사용상의 에러를 대신 확인
용도2) 어느 곳...
We are!
항목 3. 라면스프 같은 const
정말 다 괜찮은 const, 다만 의미가 조금 달라질 뿐
Char *p = greeting;
const char *p = greeting;
char * const p =...
We are!
항목 3. 라면스프 같은 const
비트 수준 상수성은 허용되나 문제가 있는 경우
Class CTextBlock {
Public:
…
char& operator[]( std::size_t position ...
We are!
항목 3. 라면스프 같은 const
코드가 중복되면 비상수 버전이 상수 버전을 불러 오도록 함
Const char& operator[](std::size_t position) const
{
//pseudo...
We are!
항목 4. 새제품은 초기화를 하고 쓰자
초기화 되지 않은 값을 읽도록 내버려 두면
쓰레기 값을 읽고 정의되지 않은 동작(오류) 발생
모든 객체를 사용하기 전에 항상 초기화!
We are!
항목 4. 새제품은 초기화를 하고 쓰자
대입은 초기화가 아니다
ABEntry::ABEntry(const std::string& name,
const std::string& address,
const std...
We are!
항목 4. 새제품은 초기화를 하고 쓰자
비지역 정적 객체(서로 다른 Class의 참조)들의 초기화 순서에 따라 발생하는 문제
‘초기화 되기도 전에 다른 객체가 초기화 전 변수 요청‘ = 오류
Singlet...
We are!
항목 5. 컴파일러가 알아서 만드는 숨겨진 함수
비어 있는 클래스를 만들더라도 이미 숨겨져 있는 함수가 존재한다
class Empty {
public:
Empty() { ... }
Empty(const E...
We are!
항목 6. 알아서 만드는 함수를 금지 시키자
복사 방지 등의 목적을 위해 컴파일러가 만드는 함수를 금지하려면?
컴파일러에게 이미 그 함수가 있음을 알려줌
Class HomeForSale {
Public:
...
We are!
항목 7. 다형성은 부모 클래스의 가상 소멸자가 필요해
동적 바인딩시 가상 소멸자가 없으면 자식 클래스에서 메모리 leak이 발생
기본 클래스 포인터를 통해 자식 클래스 객체가 삭제 될 때,
가상 소멸자가...
We are!
항목 7. 다형성은 부모 클래스의 가상 소멸자가 필요해
가상 소멸자가 없는 경우 부모 클래스가 되면 절.대. 안 됨
ex) std::string 타입은 가상 소멸자가 없음
STL 컨테이너 타입은 가상 소멸...
We are!
항목 8. 소멸자에서 예외가 터진다면 막아라!
예외 좋아하는 사람은 당연히 없겠지만,
C++는 예외를 내는 소멸자를 두고 보지 않음
예외 처리 해결 방법
방법1) 프로그램을 바로 끝낸다
방법2) 예외를 삼...
We are!
항목 8. 소멸자에서 예외가 터진다면 막아라!
사용자에 넘기는 것이 중요해 보이지만,
소멸자에 예외를 두지 않는 것이 포인트!
Class DBConn {
Public:
…
Void close()
{
db....
We are!
항목 9. 생성과 소멸 과정에서 가상함수 호출 금지
앞서 생성 순서가 기억 난다면...
생성자, 소멸자에 가상 함수가 있다면 부모, 자식 클래스 생성/소멸이 비정상 작동
부모 클래스 생성자는 자식 클래스 ...
We are!
항목 10. 대입 연산자는 *this를 반환한다
대입 연산자는 좌변 인자를 *this로 반환 하도록 함
ex)
X = y = z = 15;
동치
X = (y = (z = 15));
일종의 컨벤션(관례)!
...
We are!
항목 11. operator=에서 자기 대입을 꼭 처리하라
이상한 코드? 정상 코드!
Class Widget { … };
Widget w;
…
w = w;
자기 대입의 가능성을 가진 경우가 많음
Widge...
We are!
항목 12. 객체 복사는 호적을 싹 뒤져서 완전히 복사
객체를 복사하는 것은 절대 단순하지 않다
1) 해당 클래스의 데이터 멤버를 모두 복사하고
2) 클래스가 상속한 부모 클래스의 복사 함수도 호출해야 함...
We are!
Thank you :)
2014. 03. 27
Upcoming SlideShare
Loading in...5
×

Effective c++ chapter1 2_dcshin

192

Published on

This content wrote for Advanced C++ Class in NHN NEXT.

2014. spring-summer semester

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
192
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
2
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Transcript of "Effective c++ chapter1 2_dcshin"

  1. 1. We are! Digging Effective C++ Chapter 1 Chapter 2 NEXT 1기 131039 신동찬
  2. 2. We are! 항목 1. United State of C++! C++는 더 이상 단순 C with Classes가 아니며, 여러 개의 하위 언어를 포함하는 개념 쓰는 영역에 따라 프로그래밍도 다변화 C++ 객체 지향 개념의 C++ 템플릿 C++ STL •절차적 프로그래밍 지원 •객체지향 프로그래밍 지원 •메타프로그래밍 지원 •객체 지향 개념 추가 •클래스, 캡슐화, 상속으로 구현 •다형성, 가상 함수로 구체화 •템플릿 메타프로그래밍 •컴파일 과정에서 코드를 생성 •용도에 맞춰 템플릿 구문 사용 •자주 사용하는 라이브러리 •컨테이너, 반복자, 알고리즘 등 C •기본제공 데이터타입 •전처리기, 배열, 포인터 등 •대부분의 기본 문법의 원천
  3. 3. We are! 항목 2. 낭비벽을 가진 #define 전처리에서 사용하는 #define을 버리고 상수를 사용하자 이유1) 코드 전체 영역에서 변경이 발생하는 #define은 낭비 이유2) #define에는 유효 범위가 없다 이유3) #define 매크로를 잘못 써 그만... 해결부터 보고 가실께요 방안1) 상수 타입(const)와 친해질 것 방안2) 클래스에서는 정적 멤버로 만들 것 방안3) inline 함수를 쓸 것
  4. 4. We are! 항목 2. 낭비벽을 가진 #define #define MAX_BUFF 1024 const int MAX_BUFF = 1024; • 컴파일 에러시 확인이 어려움 컴파일에서는 이미 숫자 상수로 대체 • 코드의 크기가 늘어나는 경우 존재 해당 단어를 기계적으로 대치 함 • 유효범위 개념이 없음 • 캡슐화 불가 • 컴파일러 확인 및 기호 테이블 입력 • 상수 타입은 여러 번 쓰이더라도 사본은 하나만 생성 • 클래스 내부에서 정적(static) 멤버로 만들어 클래스 영역에서만 사용 • 클래스의 상수 데이터 멤버는 캡슐화 됨
  5. 5. We are! 항목 2. 낭비벽을 가진 #define 클래스 내부 상수 생성 및 활용 방법 //in header file class CostEstimate { private: Static const double FudgeFactor; … } // in implement file const double CostEstimate::FudgeFactor = 1.35; 방법1) //in header file class CostEstimate { private: Static const double FudgeFactor = 1.35; … } // in implement file const double CostEstimate::FudgeFactor; 방법2) 방법3) //in header file class CostEstimate { private: Enum { NumTurns = 5 }; Int scores[NumTurns]; … } • 정적 상수 생성 후 구현 부에서 정의 • 구식 컴파일러에서의 선언과 정의 • 컴파일 과정에서 상수가 먼저 필요한 경우 • Enum hack 기법으로 상수 확보 • Enum hack 특징 ‖ 동작 방식이 직관적(#define 유사) ‖ 쓸데 없는 메모리 할당이 없음
  6. 6. We are! 항목 2. 낭비벽을 가진 #define #define 매크로의 위험성을 피하라 일반 사례) #define CALL_WITH_MAX(a, b) f( (a) > (b) ? (a) : (b) ) Int a = 5, b = 0; CALL_WITH_MAX (++a, b); //1번 CALL_WITH_MAX (++a, b+10); //2번 • 1번과 2번의 행동이 다름 • 1번은 ++a가 2회 실행 최초 비교 단계에서 1회 (a) 리턴 단계에서 1회 • 2번은 ++a가 1회 실행 Inline함수로 안정성 확보) template<typename T> Inline void callWithMax( const T& a, const T& b ) { f( a > b ? A : b ); } • 임의의 T에 대해 크기 비교 함수 • 동작 방식과 타입 안정성까지 확보
  7. 7. We are! 항목 3. 라면스프 같은 const Const는 그 어떤 외부 변경도 막아냄(변경 불가) 컴파일러가 제약을 확인하고 지켜 절대 변경되지 않음 용도1) 컴파일러가 사용상의 에러를 대신 확인 용도2) 어느 곳에서든 사용 할 수 있음 용도3) 함수 반환 값에 사용해 안전성,효율을 확보하며 에러 감소 ex) 잘못된 대입 연산 등 용도4) 클래스의 인터페이스를 이해하기 좋게 함 용도5) 실행 성능 향상에 기여(reference-to-const) 주의 사항 주의1) 비트수준 상수성은 모든걸 보장하지 않음 주의2) 상수 멤버, 비상수 멤버 함수가 기능적으로 같은 경우 비상수 버전이 상수 버전을 호출하도록 함
  8. 8. We are! 항목 3. 라면스프 같은 const 정말 다 괜찮은 const, 다만 의미가 조금 달라질 뿐 Char *p = greeting; const char *p = greeting; char * const p = greeting; const char * const p = greeting; void f1 ( const Widget * pw ); void f2 (Widget const *pw); std::vector<int> vec; ... const std::vector<int>::iterator iter = vec.begin(); *iter = 10; ++iter; std::vector<int>::const_iterator cIter = vec.begin(); *cIter = 10; ++cIter • 비상수 포인터, 비상수 데이터 • 비상수 포인터, 상수 데이터(데이터 변경시 오류) • 상수 포인터, 비상수 데이터(포인터 주소 변경시 오류) • 상수 포인터, 상수 데이터(전부 변경 금지) • 의미 차이 없음 • Widget 객체가 const • Iter가 상수 • 데이터는 변경 가능하나 ++iter에서 오류 • 데이터가 상수 • 데이터는 변경시 오류, ++iter 가능
  9. 9. We are! 항목 3. 라면스프 같은 const 비트 수준 상수성은 허용되나 문제가 있는 경우 Class CTextBlock { Public: … char& operator[]( std::size_t position ) const { return pText[position]; } Private: Char *pText; }; //main 실행 Const CTextBlock cctb( “Hello” ); Char *pc = &cctb[0]; *pc = ‘J’ • 상수 객체 선언 • 상수 버전 operator[] 호출로 cctb의 포인터 획득 상수 버전이라 안전할 것으로 예상 • 그러나 직접 접근으로 ‘Hello’가 ‘Jello’로 변경 -> 오류! -> mutable을 사용해 처리 -> 비록 상수 멤버 함수에서도 수정 가능
  10. 10. We are! 항목 3. 라면스프 같은 const 코드가 중복되면 비상수 버전이 상수 버전을 불러 오도록 함 Const char& operator[](std::size_t position) const { //pseudo코드 경계 검사(); 접근 데이터 로깅(); 자료 무결성 검증(); } char& operator[](std::size_t position) { //pseudo코드 경계 검사(); 접근 데이터 로깅(); 자료 무결성 검증(); } Const char& operator[](std::size_t position) const { //pseudo코드 경계 검사(); 접근 데이터 로깅(); 자료 무결성 검증(); } char& operator[](std::size_t position) { return const_cast<char&> ( static_cast<const TextBlock&>(*this)[position] ); } • 중복을 제거 • 입력 자료형과 출력 자료형을 강제 형변환으로 일치
  11. 11. We are! 항목 4. 새제품은 초기화를 하고 쓰자 초기화 되지 않은 값을 읽도록 내버려 두면 쓰레기 값을 읽고 정의되지 않은 동작(오류) 발생 모든 객체를 사용하기 전에 항상 초기화!
  12. 12. We are! 항목 4. 새제품은 초기화를 하고 쓰자 대입은 초기화가 아니다 ABEntry::ABEntry(const std::string& name, const std::string& address, const std::list<PhoneNumber>& phones) { theName = name; theAddress = address; thePhones = phones; numTimesconsulted = 0; } • 객체에 들어온 전달인자를 대입하고 있음 • 이미 대입 전에 초기화가 되어 있었어야 함 ABEntry::ABEntry(const std::string& name, const std::string& address, const std::list<PhoneNumber>& phones) theName( name ), theAddress( address ), thePhones( phones ), numTimesconsulted( 0 ) { } • 멤버 초기화 리스트를 활용해 초기화 • 그에 앞서 메모리초기화 후 함수에서 넣을 수 있음
  13. 13. We are! 항목 4. 새제품은 초기화를 하고 쓰자 비지역 정적 객체(서로 다른 Class의 참조)들의 초기화 순서에 따라 발생하는 문제 ‘초기화 되기도 전에 다른 객체가 초기화 전 변수 요청‘ = 오류 Singleton Pattern으로 해결! • 비지역 정적 객체를 맡는 함수 제작 후 각 객체 넣기 • 함수에서 정적 객체 선언 후 참조자 반환 • 직접 접근에서 함수 호출로 대체 FileSystem& tfs(); Directory& tempDir(); 등 Class FileSystem { … }; FileSystem& tfs() { Static FileSystem fs; Return fs; } Class Directory { … }; Dirctory::Directory( params ) { … Std::size_t disks = tfs().numDisks(); } Directory& tempDir() { Static Dirctory td; Return td; }
  14. 14. We are! 항목 5. 컴파일러가 알아서 만드는 숨겨진 함수 비어 있는 클래스를 만들더라도 이미 숨겨져 있는 함수가 존재한다 class Empty { public: Empty() { ... } Empty(const Empty& rhs) { ... } ~Empty() { ... } Empty& operator=( const Empty& rhs) { ... } }; • 기본 생성자 • 복사 생성자 • 소멸자 • 복사 대입 연산자 컴파일러가 알아서 만들지만... 사용자가 우선 선언하면 만들지 않음 동작의 최종 코드가 적법하고 이치에 닿아야 자동 생성
  15. 15. We are! 항목 6. 알아서 만드는 함수를 금지 시키자 복사 방지 등의 목적을 위해 컴파일러가 만드는 함수를 금지하려면? 컴파일러에게 이미 그 함수가 있음을 알려줌 Class HomeForSale { Public: … Private: ... HomeForSale ( const homeForSale& ); HomeForSale& operator= ( const HomeForSale& ); } 방법1) 자동으로 생성하는 함수에 대해 선언(기능은 구현 안 함) 방법2) 복사 방지 부모 클래스 사용 Class Uncopyable { Protected: Uncopyable() {} ~Uncopyable() {} Private: Uncopyable( const Uncopyable& ); Uncopyable& operator=( const Uncopyable& ); }; Class HomeForSale : private Uncopyable{ Public: … }
  16. 16. We are! 항목 7. 다형성은 부모 클래스의 가상 소멸자가 필요해 동적 바인딩시 가상 소멸자가 없으면 자식 클래스에서 메모리 leak이 발생 기본 클래스 포인터를 통해 자식 클래스 객체가 삭제 될 때, 가상 소멸자가 없으면 부모 클래스만 삭제 • 생성시 순서 1. 부모 클래스 2. 자식 클래스 • 가상 소멸자가 없는 경우 삭제 시 1. 부모 클래스 2. 연결고리 상실 • 가상 소멸자 있는 경우 삭제 1. 자식 클래스 2. 부모 클래스 Class TimeKeeper { Public: TimeKeeper(); Virtual ~TimeKeeper(); }; TimeKeeper *ptk = getTimeKeeper(); … Delete ptk;
  17. 17. We are! 항목 7. 다형성은 부모 클래스의 가상 소멸자가 필요해 가상 소멸자가 없는 경우 부모 클래스가 되면 절.대. 안 됨 ex) std::string 타입은 가상 소멸자가 없음 STL 컨테이너 타입은 가상 소멸자가 없음 vector, list, set tr1::unordered_map 등
  18. 18. We are! 항목 8. 소멸자에서 예외가 터진다면 막아라! 예외 좋아하는 사람은 당연히 없겠지만, C++는 예외를 내는 소멸자를 두고 보지 않음 예외 처리 해결 방법 방법1) 프로그램을 바로 끝낸다 방법2) 예외를 삼켜 버린다 가장 좋은 방법! -사용자에게 맡기고 실패하면 삼킨다
  19. 19. We are! 항목 8. 소멸자에서 예외가 터진다면 막아라! 사용자에 넘기는 것이 중요해 보이지만, 소멸자에 예외를 두지 않는 것이 포인트! Class DBConn { Public: … Void close() { db.close(); closed = true; } ~DBConn() { If( !closed ) Try { db.close(); } Catch ( … ) { closeLogging(); } } Private: DBConnection db; Bool closed; }; • 사용자가 우선 close() 함수 실행으로 처리 • 사용자가 연결을 안 닫았으면 소멸자에서 재확인 후 삭제 • 소멸자에서 직접 예외 처리가 있으면 안됨 • 사용자가 함수로 호출하면 관리 가능
  20. 20. We are! 항목 9. 생성과 소멸 과정에서 가상함수 호출 금지 앞서 생성 순서가 기억 난다면... 생성자, 소멸자에 가상 함수가 있다면 부모, 자식 클래스 생성/소멸이 비정상 작동 부모 클래스 생성자는 자식 클래스 생성자보다 앞서서 실행 부모의 가상 함수는 자식 클래스 데이터가 초기화 되기 전에 요청 쓰레기 값을 활용한 함수 실행 가능성 문제는 호출 순서! 호출 순서에 따라 전혀 다른 객체를 사용하게 됨
  21. 21. We are! 항목 10. 대입 연산자는 *this를 반환한다 대입 연산자는 좌변 인자를 *this로 반환 하도록 함 ex) X = y = z = 15; 동치 X = (y = (z = 15)); 일종의 컨벤션(관례)! “좌변 객체의 참조자를 반환하게 만들자”
  22. 22. We are! 항목 11. operator=에서 자기 대입을 꼭 처리하라 이상한 코드? 정상 코드! Class Widget { … }; Widget w; … w = w; 자기 대입의 가능성을 가진 경우가 많음 Widget& Widget::operator=(const Widget& rhs) { Bitmap *pOrig = pb; pb = new Bitmap(*rhs.pb); Delete pOrig; Return *this; } 안전코드1) Widget& Widget::operator=(const Widget& rhs) { Widget temp(rhs); Swap(temp); Return *this; } 안전코드2)
  23. 23. We are! 항목 12. 객체 복사는 호적을 싹 뒤져서 완전히 복사 객체를 복사하는 것은 절대 단순하지 않다 1) 해당 클래스의 데이터 멤버를 모두 복사하고 2) 클래스가 상속한 부모 클래스의 복사 함수도 호출해야 함 PriorityCustomer::PriorityCustomer( const PriorityCustomer& rhs) : Customer(rhs), priority(rhs.priority) { logCall(“PriorityCustomer copy constructor”); } PriorityCustomer& PriorityCustomer::operator=( const PriorityCustomer& rhs ) { logCall(“PriorityCustomer copy assignment operator”); Customer::operator = (rhs); Priority = rhs.priority; Return *this; } • 코드 중복보다 정확한 복사가 더 중요하다 • 부모클래스의 데이터가 초기화 되도록 함 • 대입 연산자를 통해 기본 클래스 부분을 대입함
  24. 24. We are! Thank you :) 2014. 03. 27
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×