본문 바로가기
Development/C|C++|C#

[Effective C++]4. 설계 및 선언

by True Life 2016. 10. 29.


설계 및 선언


항목18. (명언) "제대로 쓰기에는 쉽게, 엉터리로 쓰기에는 어렵게"

 - 단순 매개변수, var의 저장/TOSS시에도 간단한 wrapper type (ex. year, month, day)

 - "When in doubt, do as the ints do"

 - (아예)스마트 포인터를 반환하여 실수 방지

 - shared_ptr 은 auto_ptr와 돤리 삭제자를 엮을 수 있다

  std::tr::shared_ptr<Inve> pInv(static_cast<Inve>(0), getRidOfInvestment);

   *null 보다 '미리 할 수 있으면' 바로 pInv 생성자에 넘기는 것이 바람직.

    → (항목 26) "객체가 정말로 필요할 때까지 선언 대기!"

 - 교차 DLL 문제 : A DLL → new,  B DLL → delete시 런타임에러 

    → shared_ptr 해결 (할당한 DLL 에서)


항목19. 클래스 설계시 유의점

 - 마치 언어설계자의 Type 선언처럼 세심한 정성 要

 - 생성.소멸자의 설계

 - 초기화 vs 대입

 - 복사생성자시 값에 의한 전달

 - 값들의 유효성(범위)제한

 - 상속을 받을경우, 할경우 모두 가상함수 신경 (특히 소멸자)

 - 암시적 타입변환 or 비명시 호출 or 명시적 호출

 - 접근권한

 - 수행성능, 예외 안정성, 자원사용의 '보장'

 - 클래스 Template 염두

 - 꼭 필요한 타입인가 염두


항목20. 값에 의한 전달 < 상수 객체 참조자에 의한 전달

 - 객체 내부의 수 많은 멤버가 재생성되는 비효율성 초래

 - 상수 참조시 비효율 X, '상수'이므로 손실 걱정 X

 - 파생클래스→기본클래스의 값에 의한 전달시 : "복사손실문제" - 파생의 특성 사라짐

  * 참조자는 pointer를 써서 구현됨.   → 레지스터에 구현됨


항목21. 참조자 반환은 절대로 피하자

 - 참조자를 반환하기 위해서는 그 개체의 본체가 만들어져야 한다. (본체 = 스택(지역) or 힙(NEW))

 - 지역변수는 return 시 소멸됨, → 의미X

 - new(객체 생성자의 호출)의 객체는 delete할 사람이 없다.

  * ex. operator* → w=x*y*z; 사이의 new 된 객체들의 소멸 불가

 - 정적 객체의 참조자 반환 → (a*b) == (c*d) → True 고정

 - 새로운 객체 반환의 정석 → inline const Rational ~ {

                                                 return Rational(~)

                                              }




항목22. 데이터 멤버는 private 여야 한다. (protected가 아니다!)

 - 메서드 또는 변수의 문법 일관성

 - 접근 제어의 의무화 (세분화하여야 한다 : 단방향/양방향 - 게터, 세터)

 - 캡슐화 (계산 함수의 실시간성) → 클래스 불변속성 유지


항목23. 멤버 함수 보다는 비멤버,비프렌드 함수를 사용하자

 - "캡슐화의 Level : private 접근 권한의 메소드 수"로 정의된다

   → 멤버함수 : 접근 가능, (비멤버 & "비프렌드" == 전역 비프렌드) → 접근불가 (== 캡슐화)

 - 캡슐화의 장점 : 패키징 유연도 높음 & 컴파일 의존도가 낮음

 - 더욱 자연스러운 구현 : namespace 안에 위치 (편의함수 - 응용도가 높음)

  * STL들의 구현도 비멤버,비프렌드 같은 namespace(std) & 다양한 헤더

 - namespace 분할 가능 but, 클래싀 정의의 확장/수정 불가능

   → 확장성이 높아짐


항목24. 타입변환이 모든 매개변수에 대해 적용하려면 "비멤버"함수를 사용해야 한다.

 - explicit를 붙이지 않는 규칙의 예외

 - oneHalf * 2 는 oneHalf.operator*(2);    cf ) 2 * oneHalf

 - 비멤버 operator* 가 답이다        * 이게 가능함을 느껴보라


항목25-1. swap의 효율을 위한 응용/특수화 (swap이 중요한게 아니라 특수화에 대하여..)

 - swap의 원리는 복사 생성자 및 대입 연산자

 - PImpl →  객체의 경우 무의미한 비용이 들 수 있음 :: PImple : ptr(실제데이터)를 다루는 객체

   Pointer to Implementation                            →  pimpl 포인터만 맞바꾸는 '완전 템플릿 특수화'

   Template<> void swap<Widget> ( -- ) {  → std namespace 내

 - private 접근 불가 (pimpl 데이터 교환 불가) → public swap 선언 후 포인터 swap

 - 위에서 Widget이 Template 클래스일 경우,        template<typename T> void swap<Widget<T>>( - ) {

  → 함수 Template에 대한 부분특수화 : 오버로딩                  → 함수 Template에 대한 부분특수화 X

  → std namespace에는 class, method 추가 금지!

  → 현재 namepsace에 swap 선언으로 간단히 해결 (namespace가 다르므로 오버로딩 X) → 이름탐색 규칙에 의함


항목25-2. 클래스 템플릿 및 특수화에 대한 부연

 - 특수화란 Template<typename T>에 대하여 지정된 형식이 나올 경우 내용을 특수하게 변경하는것

    (함수 템플릿, 클래스 템플릿 모두 가능함)    template<> 후 메소드/클래스 정의

    cf, 비슷한 형태로 default template가 있음

        template<typename T=int, int len=7> 과 같이 사용 후 Widget<> wg;

 - 함수 템플릿 특수화는 '완전 특수화'만 가능하다. type 여러개시 전부 특수화 → overloading 해결

 - 클래시 템플릿 특후화도 '본문'을 특수 구성한다는 것임 → 클래스 전체 재구성 필요

  * 함수 선택시 순서 : 일반함수 → 기본함수템플릿 → 오버로딩 →  특수화 함수 템플릿

  * 완전 특수화 → 부분특수화 클래스 선택됨