728x90

동적으로 메모리 할당에 new를 사용하려면 이중포인터를 사용해야 한다.

 

이중 포인터는 포인터 연산자 *를 두 번 씀으로써 사용이 가능하다. 삼중 포인터라면 *를 세 개 붙이면 된다. 이제 여기에 동적할당을 할 차례이다. 동적할당은 가운데 있는 50칸짜리 공간을 할당한 후, 반복문을 이용해서 50칸에 각각 100칸짜리 공간을 할당하는 방식을 쓴다. 코드1은 50칸짜리 공간을 할당하는 코드이다.


char** text = new char*[50];

코드1. 이중 포인터를 이용한 배열의 동적할당 #1


코드1을 보면 text가 이중 포인터이기 때문에 new 연산자 오른쪽에 위치한 코드가 char[50]이 아닌 char*[50]으로 써있다. 이 부분에 주의하자. 그럼 50칸을 생성했으니 100칸을 생성해보자.


char** text = new char*[50];

for(int i = 0; i < 50; i++)
text[i] = new char[100];

코드2. 이중 포인터를 이용한 배열의 동적할당 #2


이 50개의 칸에 for문을 50번 돌리면서 100칸짜리 방을 만드는 것이다. 이렇게 할당이 끝났으면 text는 또 다시 완벽한 2차원 배열처럼 사용이 가능하다. 참고로 for문 내의 char[100]부분의 100이라는 숫자를 i에 따라 다르게 쓰면 각 배열의 크기가 다른 2차원 배열도 만들 수 있다.

할당을 했으니 다 사용한 후에 해제하는 방법을 알아보자. 해제의 순서는 할당과 반대로 오른쪽에 있는 100칸짜리들부터 해제한다. 50칸을 먼저 해제해버리면 100칸짜리들의 주소가 어딘지 알 수 없게 되버리니까 조심하자.


for(i = 0; i < 50; i++)
delete [] text[i];

delete [] text;

코드3. 이중 포인터를 이용한 배열의 해제

 

메모리 해제를 제대로 안시키면 런타임 에러가 발생할수 있으니 유의하자.

 

출처 : http://blog.naver.com/alsmaker?Redirect=Log&logNo=120033075870

728x90
728x90

Pointer에는 '값이 NULL일 때에는 아무것도 하지 않는다'는 규칙이 있다. 메모리 배열에서 0번만은 사용하면 안된다고 정해져 있는 것이다.

 

int* p = NULL;

*p = 4;      // 쓰기 오류.

int a = *p;  // 읽기 오류.

 

따라서 앞에서와 같은 코드는 규칙을 위반했기 때문에 프로그램이 멈추게 된다. delete한 포인터에 NULL을 넣는 이유는 이런 성질을 디버그에 활용하려는 것이다. delete해도 포인터에는 값이 남아 있는데 그 값을 이용하면 메모리에 접근 할 수 있다. 이것을 방지하기 위해 delete를 한 이후에는 NULL을 넣는 버릇을 들이도록 하자.

728x90
728x90

41. 템플릿 프로그래밍의 천릿길도 암시적 인터페이스와 컴파일 타임 다형성 부터.

   - 클래스 및 템플릿은 모두 인터페이스와 다형성을 지원한다.

   - 클래스의 경우, 인터페이스는 명시적이며 함수의 시그너처를 중심으로 구성되어 있다.

   - 다형성은 프로그램 실행 중에 가상 함수를 통해 나타난다.

   - 템플릿 매개변수의 경우, 인터페이스는 암시적이며 유효 표현식에 기반을 두어 구성된다.

   - 다형성은 컴파일 중에 템플릿 인스턴스화와 함수 오버로딩 모호성 해결을 통해 나타난다.

 

42. typename의 두 가지 의미를 제대로 파악하자.

   - 템플릿 매개 변수를 선언할 때의 typename은 class와 같은 용도로 사용된다.

   - 중첩 의존 타입 이름을 식별하는 용도에는 반드시 typename을 사용한다.

   - 단, 중첩 의존 이름이 기본 클래스 리스트에 있거나 멤버 초기화 리스트 내의 기본 클래스 식별자로 있는 경우에는 예외이다.

 

   ex)

   template<typename C>

   void print2nd(const C& container)

   {

        C::const_iterator * x;

   }

   C::const_itorator가 type이라는 것은 인간인 우리만 아는 것이지, 컴파일러가 구문 분석할 때 저것을 type이라고 보지 못하기 때

   문에 그냥 C::const_iterator와 x를 곱해버리는 경우가 생긴다.

   이런 모호성을 해결하기 위해 반드시 저 C::const_iterator는 타입이라는 것을 알려주기 위해 앞에

   typename C::const_iterator * x;  라고 해야 제대로 인식하게 된다.

 

43. 템플릿으로 만들어진 기본 클래스 안의 이름에 접근하는 방법을 알아두자.

   - 파생클래스 템플릿에서 기본 클래스 템플릿의 이름을 참조할 때는 this-> 를 접두사로 붙이거나 기본 클래스 한정문 using 

      AA<int>::ParentFunc()을 명시적으로 써주는 것으로 해결하자.

 

44. 매개 변수에 독립적인 코드는 템플릿으로 부터 분리시키자.

   - 템플릿을 사용하면 비슷비슷한 클래스와 함수가 여러개 만들어진다. 따라서 템플릿 매개 변수에 종속되지 않은 템플릿 코드는 

     비대화의 원인이 된다.

   - 비타입 템플릿 매개변수로 생기는 코드 비대화의 경우, 템플릿 매개변수를 함수 매개변수 혹은 클래스 데이터 멤버로 대체함으

     로써, 비대화를 없앨 수 있다.

   - 타입 매개변수로 생기는 코드 비대화의 경우, 동일한 이진 표현 구조를 가지고 인스턴스화되는 타입들이 한 가지 함수 구현을 

     공유하게 만듬으로써 비대화를 없앨 수 있다.

     template<typename T, std::size_t n>

     여기서 타입 매개변수는 T 비타입 템플릿 매개 변수는 n

 

45. 호환되는 모든 타입을 받아들이는 데는 멤버 함수 템플릿이 좋다.

   - 멤버 함수 템플릿 : 어떤 클래스의 멤버 함수를 찍어내는 템플릿.

   - 일반화된 복사 생성연산과 일반화된 대입 연산을 위해 멤버 템플릿을 선언했다 하더라도, 보통의 복사 생성자와 복사 대입 연산

     자는 여전히 직접 선언해야 한다.

 

46. 타입 변환이 바람직할 경우에는 비멤버 함수를 클래스 템플릿 안에 정의 해 두자.

   - 모든 매개변수에 대해 암시적 타입 변환을 지원하는 템플릿과 관계가 있는 함수를 제공하는 클래스 템플릿을 만들려고 한다면, 

     이런 함수는 클래스 템플릿 안에 프렌드 함수로써 정의하자.

     class A

     {

          public :

          friend const A operator*(const A& lhs, const A& rhs)

          {

               return (lhs.GetNumber() * rhs.GetNumber());

          }

     }

 

47. 타입에 대한 정보가 필요하다면 특성 정보 클래스를 사용하자.

   - 특성 정보 클래스는 컴파일 도중에 사용할 수 있는 타입 관련 정보를 만들어 낸다.

     또한 특성 정보 클래스는 템플릿 및 템플릿 특수 버전을 사용하여 구현한다.

   - 함수 오버로딩 기법과 결합하여 특성 정보 클래스를 사용하면, 컴파일 타임에 결정되는 if... else 점검문을 구사 할 수 있다.

     ex) 현재의 iterator가 random_access를 지원하는지 여부 판단 등

 

48. 템플릿 메타 프로그래밍 (TMP)를 사용해보자.

   - 템플릿 메타 프로그래밍은 기존 작업을 런타임에서 컴파일 타임으로 전환하는 효과를 만들어 낸다.

     따라서 TMP를 쓰면 선행 에러탐지와 높은 런타임 효율을 얻을 수 있다.

   - TMP는 정택 선택의 조합에 기반하여 사용자 정의 코드를 생성하는데 사용가능하며, 또한 특정 타

     입에 대해 부적절한 코드가 만들어지는 것을 막는데 쓸 수 있다.

   - C++에서 TMP가 활약하는 부분은 세가지로 나눌 수 있다.

      1) 차수 단위(DIMENSIONAL UNIT)의 정확성 확인.

      2) 행렬 연산의 최적화.

      3) 맞춤식 디자인 패턴 구현의 생성.

 

49. new 처리자의 동작 원리를 제대로 이해하자.

   - 메모리 할당이 제대로 되지 못한 상황에 대한 반응으로 operator new가 예외를 던지기 전에 사용

     자쪽에서 지정 할 수 있는 에러처리 함수를 new 처리자라고 한다.

   - set_new_handler함수를 쓰면 메모리 할당 요청이 만족되지 못했을 때 호출되는 함수를 지정할 수 있다.

   - 예외불가(nothrow) new는 영향력이 제한되어 있다. 메모리 할당 자체에만 적용되기 때문이다. 

후에 호출되는 생성자는 얼마든지 예외를 던질 수 있다.

 

50. new 및 delete를 언제 바꿔야 좋은 소리를 들을지 파악해두자.

  operator new와 operator delete를 바꾸는 시기는 다음과 같다.

   - 잘못된 힙 사용을 탐지하기 위해서.

   - 효율을 향상 시키기 위해서. (할당 및 해제 속력 Up)

   - 동적 할당 메모리의 실제 사용에 관한 통계 정보를 수집하기 위해서.

   - 기본 메모리 관리자의 공간 오버헤드를 줄이기 위해서.

   - 기본 할당자의 바이트 정렬 동작을 보장하기 위해서. (x86 아키텍처에서는 double이 8바이트 단위

     로 정렬되어 있을 때 가장 빠르다.)

   - 임의의 관계를 맺고 있는 객체들을 한군데에 나란히 모아 놓기 위해서.

   - 원하는 동작을 그때 그때 수행하기 위해서.

 

51. new 및 delete를 작성할 때 따라야 할 기존의 관례를 잘알아두자.

   - 관례적으로, operator new 함수는 메모리 할당을 반복해서 시도하는 무한 루프를 가져야 하며, 메

     모리 할당 요구를 만족 시킬 수 없을 때 new 처리자를 호출해야 한다. 또한 0 바이트에 대한 대책

     도 있어야 하며, 클래스 전용 버전은 자신이 할당하기로 예정된 크기보다 더 큰(틀린)메모리 블록

     에 대한 요구도 처리해야 한다. operator delete 함수는 null pointer가 들어왔을 때, 아무 일도 하지

     않아야 한다. 클래스 전용버전의 경우에는 예정 크기보다 더 큰 블록을 처리해야 한다.

 

52. 위치 지정 new를 작성한다면 위치 지정 delete도 같이 준비하자.

   - operator new는 기본형이 존재하지만, 기본형과 달리 매개 변수를 추가로 받는 형태로도 선언이

     가능하다. 이런 형태의 함수를 위치 지정(placement) new라고 한다. operator new 함수의 위치

     지정 버전을 만들 때는 이 함수와 짝을 이루는 위치 지정 버전의 operator delete 함수가 있어야 한

     다. new 및 delete의 위치 지정 버전을 선언할 때는 의도한 바도 아닌데, 이들의 표준 버전이 가려

     지는 일이 생기지 않도록 주의하자.

 

53. 컴파일러 경고를 지나치지 말자.

   - 자신의 컴파일러에서 지원하는 최고 경고 수준에도 경고 메시지를 내지 않고 컴파일 되는 코드를

     만드는 쪽에 최선을 다하자.

   - 컴파일러 경고에 너무 민감하게 반응하지 말자. 컴파일러마다 트집을 잡고 경고를 내는 부분들이

     천차 만별이기 때문이다.

 

54. TR1을 포함한 표준 라이브러리 구성요소와 친구가 되자.

   - TR1(1차 기술 보고서) 구성요소.

     1. 스마트 포인터 : (tr1::shared_ptr, tr1::weak_ptr 등)

     2. tr1::function

        어떤 함수가 가진 시그너처와 호환되는 시그너처를 갖는 함수 호출성 개체

        void registerCallback(std::tr1::function<std::string (int)> func);

        매개 변수인 func는 int로 변환 가능한 어떤 타입도 전달받는 것이 가능하며, string 타입 혹은

        string 타입으로 변환이 가능한 어떤 타입도 반환할 수 있는 그런 함수를 registerCallback 함수

        에 매개변수로 설정할 수 있다.

     3. tr1::bind

        STL 바인더로 쓰이는 bind1st 및 bind2nd와 같은 동작에 더 많은 기능이 포함되어있는 범용 바

        인더.

     4. 해시 테이블(hash table)

        원소가 저장되는 순서를 예측 할 수 없게 구현한 연관 컨테이너들을 말한다.

        tr1::unordered_set, tr1::unordered_map 등이 있다.

     5. 정규 표현식

        정규 표현식 기반의 탐색과 문자열에 대한 대체 연산이 가능하며 일치되는 원소들 사이의 순회도

        지원한다.

     6. 투플(tuple)

        기존의 pair 템플릿의 새로운 버전으로 tr1::tuple 객체는 두개 이상의 객체를 몇개라도 담을 수

       있게 한다.

     7. tr1::array

        begin 및 end를 지원하는 STL 스러운 배열을 사용하나 동적 메모리를 사용하지 않는다.

     8. tr1::mem_fn

        멤버 함수 포인터를 abapt 용도에 쓸 수 있는 템플릿 mem_fun 및 mem_fun_ref에 기능을 확장한

        것이다.

     9. tr1::reference_wrapper

        기존의 참조자가 객체처럼 행세할 수 있도록 만들어주는 템플릿 사용시 참조자를 담은 것 처럼

        동작하는 컨테이너를 만들 수 있다.

        (컨테이너는 객체 혹은 포인터만 담을 수 있다.)

     10. 난수 발생

        C에서 물려받은 rand함수보다 기능이 좋은 난수 발생 기능.

     11. 특수용도의 수학함수

        라게르(Laguerre)다항식, 베셀(Bessel)함수, 완전 타원 적분등 특수 용도의 수학 함수를 사용할

        수 있다.

     12. C99호환성 확장 기능

        C99의 새로운 라이브러리를 C++로 가져올 목적으로 설계된 함수 및 템플릿 모음

     13. tr1::result_of

        어떤 함수 호출의 반환 타입을 추론해주는 템플릿

     14. 타입 특성 정보

        주어진 타입에 대한 컴파일 타임 정보를 제공하는 특성 정보 클래스 모음

 

     tr1은 어디까지나 문서이므로 기능을 사용하려면 구현해야 한다.

     tr1의 14개 구성요소중 10개는 부스트에서 무료로 공개한 라이브러리 기반으로 tr1을 사용하려면 부

     스트에 라이브러리를 사용하면 된다.

 

55. 부스트를 늘 가까이 두자.

   - 즉시 투입 가능할 정도로 품질 우수에, 오픈 소스이고 어지간한 플랫폼과 컴파일러에서 모두 돌아

     가는 C++ 라이브러리이다.

   - TR1의 기능을 구현한 라이브러리 뿐만 아니라 가변 크기 비트셋, 다차원 배열, 함수 객체 및 고차

     프로그래밍 TMP, 수학 및 수조작 타입 안정성을 갖춘 고용체등 그외에 유용한 라이브러리가 많이

     있기 때문에 필요하다고 생각할 때마다 검색해 보자.

 

출처 : http://blog.naver.com/sorkelf/40146367692

728x90
728x90

26. 변수 정의는 늦출 수 있는 데 까지 늦추는 근성을 발휘하자. 

   - 생성자 혹은 소멸자를 끌고 다니는 타입 변수를 정의하면 물게 되는 비용이 2가지 있는데, 하나는 제어 흐름이 변수의 정의에 

     닿을 때 생성자가 호출되는 비용과 그 변수가 유효 범위를 벗어날 때 소멸자가 호출되는 비용이다. 변수가 사용이 끝나고 소멸

     이 되기 전에 예외를 발생하거나 하는 상황이 발생하게 되면 소멸자를 호출하지 못하고 끝나게 되는 상황을 상당히 자주 볼 수 

     있게 된다. 최대한 늦출 수 있도록 하는 것이 프로그램을 좀 더 효율적으로 구동시킬 수 있으며, 변수가 근처에 있기 때문에 확

     인하기도 편하다. 

 

27. 캐스팅은 절약, 또 절약! 잊지 말자.

   - "어떤 일이 있어도 타입 에러가 생기지 않도록 보장한다"는 C++의 바탕이 되는 철학이다. 이 말은 즉 이론적으로 C++는 컴파일

     만 되면 그 이후에는 어떤 객체에 대해서도 불안전한 연산이나 말도안되는 연산을 수행하려 들지 않는다는 것이다. 이 철학을 

     깨는 것이 캐스팅인데 캐스팅은 총 3개의 방법이 있다.

     (T) 표현식    // 표현식 부분을 T타입으로 캐스팅

     T (표현식)    // 표현식 부분을 T타입으로 캐스팅

     의 구형 스타일 캐스팅과 신형 스타일 캐스팅이 있다.

     신형 스타일 캐스팅은 static_cast, dynamic_cast등 총 4개지가 있다.

     가능하다면 캐스팅을 피하도록 다른 방법을 시도한 뒤 더 이상 캐스팅 이외의 방법이 없을 때만 사

     용하자. 캐스팅을 쓰려거든 구형 캐스팅을 사용하지말고, C++스타일의 신형 캐스팅을 사용하자.

     (발견하기 쉽고 어떤 의도로 사용하는지 파악가능)

 

28. 내부에서 사용하는 객체에 대한 핸들을 반환하는 코드는 되도록 피하자.

   - 객체에 내부 요소에 대한 핸들(참조자, 포인터, 반복자)를 반환하게 되면, 객체에 상수성을 가지고 있는 요소에 대해 유지가 불

     가능 하게 될 뿐만 아니라 캡슐화 정도를 낮추는 원인되 된다. 또한 무효 참조 핸들이 생기는 경우도 생길 수 있으니 되도록 피

     하자.

 

29. 예외 안전성이 확보되는 그날 까지.

   - 함수를 설계할 때 항상 예외 안전성을 고려하도록 하자. 예외 안전성을 가진 함수라면 예외가 발생할 때 자원이 새도록 만들지 

     않으며, 자료구조가 더럽혀지는 것을 허용하지 않게 된다.

     또한 3가지의 보장중 하나를 제공하는데 기본적인 보장(basic guarantee), 강력한 보장(strong quarantee), 예외불가 보장

     (nothrow guarantee)가 있다.

 

30. 인라인 함수는 모두 이해하도록 하자.

   - 인라인 함수는 함수처럼 보이고 함수처럼 동작하며, 매크로보다 훨씬 안전하고 쓰기 좋다. 또한 인라인 함수는 명령이 아닌 컴

     파일러에 요청을 하는 것이다. 즉 컴파일러가 암시적으로 할 수도 있다.

     대표적인 예가 클래스 정의 안에 함수를 바로 넣을 때 해당 함수를 인라인 후보로 찍는 경우이다.

     또한 조건이 갖추어져 있더라도 인라인이 되지 않는 경우도 있는데 함수의 주소를 취하는 코드를 사용할 때 이다. 대표적인 예

     가 생성자, 소멸자이다. 인라인은 작고, 자주 호출되는 함수에 대해서만 하는 것으로 묶는 것이 좋다. 디버깅 및 라이브러리 바

     이너리 업그레이드가 용이 해지고 코드 부풀림 현상이 최소화 되며, 조금 더 프로그램 속도가 빨라지게 된다.

 

31. 파일 사이의 컴파일 의존성을 최대한 줄이기(전방 선언)

   - 컴파일 의존성을 최소화하는 작업의 배경이 되는 가장 기본적인 아이디어는 '정의'대신에 '선언'에 의존하게 만들자는 것이다. 

     이 아이디어에 기반한 두 가지 접근 방법은 핸들 클래스와 인터페이스 클래스이다. 라이브러리 헤더는 그 자체로 모든 것을 갖

     추어야 하며 선어부만 갖고 있는 형태여야 한다. 이 규칙은 템플릿이 쓰거나 쓰이지 않거나 동일하게 적용하도록 노력하자.

 

32. public 상속 모형은 반드시 "is a"를 따르도록 하자.

   - 기본 클래스에 적용되는 모든 것들이 파생 클래스에 그대로 적용되어야 한다.

     모든 파생클래스 객체는 기본 클래스 객체의 일종이기 때문이다.

     Person클래스와 이를 상속한 Student클래스가 있을 때 "사람은 학생일 수도 있지만 학생이라고 해서 반드시 사람은 아니다"라

     는 개념으로 접근해보자.

 

33. 상속된 이름을 숨기는 일은 피하자.

   - 파생 클래스의 이름은 기본 클래스의 이름을 가리케 된다. public 상속에서 이런 이름 가림현상은 바람직하지 못하다. 가려진 

     이름을 다시 볼 수 있게 하는 방법은 using 선언 혹은 전달 함수를 사용할 수 있다.

     void foo_Student()

     {

          Person::foo();

     }

 

34. 인터페이스 상속과 구현 상속의 차이를 제대로 파악하고 구별하자.

   - 순수 가상 함수는 인터페이스 상속만을 허용하며, 단순 가상함수는 인터페이스 상속과 더불어 기본 구현의 상속도 가능하도록 

     지정할 수 있다. 비가상함수는 인터페이스 상속과 더불어 필수 구현의 상속도 가하도록 만들자.

 

35. 가상 함수 대신 쓸 것들도 생각해두는 자세를 시시 때때로 길러두자.

   - 가상 함수를 함수 포인터 데이터 멤버로 대체한다.

   - 한쪽 클래스 계통에 속해 있는 가상 함수를 다른 쪽 계통에 속해있는 가상 함수로 대체한다.

   - 가상 함수 대신에 쓸 수 있는 방법으로 NVI관용구 및 전략 패턴 (Non Virtual Interface)가 있다.

   - 객체에 필요한 기능을 멤버 함수로부터 클래스 외부의 비멤버함수로 옮기면, 그 비멤버 함수는 그클래스의 public 멤버가 아닌 

     것들을 접근할 수 없다는 단점이 생긴다.

   - tr1::function 객체는 일반화된 함수 포인터처럼 동작하는데 이 객체는 주어진 대상 시그너처와 호환되는 모든 함수 호출성 개체

     를 지원한다.

 

36. 상속 받은 비가상함수를 파생 클래스에서 재정의하는 것은 절대 금물.

   - 비가상함수는 정적바인딩(프로그램 소스에 스인 각종 내부요소, 이름, 식별자 등에 대해 빌드 중에 값 혹은 속성을 확정하는 과

     정)으로 묶이고 가상 함수는 동적 바인딩(실행중에 확정)으로 이루어져 있기 때문이다.

 

     바인딩 : int foo = 2;가 있을 대 데이터 타입은 int인 것과 변수명이 foo로 정해지는 것은 정적 바인딩, 변수에 2가 대입되는 것

     이 동적 바인딩이다. C++에서는 컴파일 중 아예 가상 함수 테이블을 파생 클래스에 맞게 바꿈으로서 겉보기에는 파생 클래스 타

     입에서 오버라이드한 가상 함수를 호출 하는 것 처럼 보이게 만든다.

 

37. 어떤 함수에 대해서도 상속 받은 기본 매개변수 값은 절대로 재정의 하지 말자.

   - 기본 매개 변수 값은 정적 바인딩에 비해 가상 함수는 동적으로 바인딩 되기 때문이다.

 

38. has -a 혹은 is-implement-interms-of를 모형화할 때는 객체 합성을 사용하자.

   - 포함된 객체들을 모아서 이들을 포함한 다른 객체를 합성한다는 뜻으로

     class Person

     {

          string name;

          Address address;

          Data data;

     };

     등과 같은 경우이다.

 

     여기서 선지식으로 소프트웨어 개발에서 영역이라는 부분이 있다.

     이 부분 중 사람, 이동수단, 비디오 프레임 등 일상 생활에서 볼 수 있는 사물을 본 뜬 객체는 응용 영역(Application Domain)

     에 속하는데 여기서 객체 합성이 응용 영역의 객체들 사이에서 일어나면 has -a의 관게이고 구현 영역에서 일어나면 

     is implement in terms of의 관계를 나타낸다.

 

39. private 상속은 심사숙고해서 구사하자.

   - private 상속에는 두가지 동작 규칙이 있는데 첫번재는 일반적으로 파생클래스 객체를 기본 클래스 객체로 변환하지 않는 것과 

     기본 클래스로부터 물려바든 멤버는 모두 private 멤버로 바뀐다는 것이다. 여기서 private 상속은 is imlement in terms of의 의

     미를 가지고 있는데 어떤 클래스를 파생시킨 것은 부모 클래스에서 쓸 수 있는 기능들 몇개를 활용할 목적으로 한 행동이지, 부

     모와 자식클래스의 객체들 사이에 어떤 개념적 관계가 있어서 한 행동이 아니라는 것, 즉 private 상속 자체가 구현 기법 중 하나

     라는 것이다. 섣불리 이 방식을 쓸 필요가 없다는 생각을 갖고 모든 대안을 고민해보고 주어진 상황에서 두 클래스 사이의 관계

     를 나타낼 가장 좋은 방법이 private 상속이라는 결론이 나면 쓰는 것이 좋다.

 

40. 다중 상속은 심사 숙고해서 구사하자.

   - 다인 상속보다 복잡할 뿐만 아니라 새로운 모호성 문제를 일으킬 수 이싸. 가상 상속이 필요해지는 경우가 있다.

     예) 부모가 둘 이상이고 부모의 부모 클래스가 데이터 멤버를 가질 경우 가장 하위 클래스에게는 2개의 데이터 멤버가 생기는 결

         과가 나온다 이럴 때 가상 상속을 사용해야 한다.

   - 가상 상속을 사용하면 크기 비용, 속도 비용이 늘어나며, 초기화 및 대입 연산의 복잡도가 상당히 커진다. 가상 기본 클래스에

     는 데이터를 두지 않는 것이 현실적이다.

   - 적합한 경우는 여러 시나리오 중 하나가 인터페이스 클래스로부터 public 상속을 시킴과 동시에 구현을 돕는 클래스로부터 

      private 상속을 시키는 것이다.

 

출처 : http://blog.naver.com/sorkelf/40146367692

728x90
728x90

13. 자원관리에는 객체가 그만이다. 

   - 자원누출을 막기위해, 생성자 안에서 자원을 획득하고 소멸자에서 그것을 해제하자.

     RAII(Resource Acquisition is Initialize) 방식을 사용하자.

     (RAII :생성자에서 할당하고, 에러 상황에서도 호출이 보장되는 같은 객체의 소멸자 같은 것에서 리소스를 해제)

   - 일반적인 RAII클래스는 tr1::shared_ptr과 auto_prt이다.

     (둘 중 tr1:shared_ptr이 복사시의 동작이 직관적이기 때문에 좋다.)

     (auto_ptr은 복사되는 객체(원본 객체)를 null로 만들어 버린다.)

 

14. 자원관리 클래스의 복사 동작에 대해 진지하게 고찰하자.

   - RAII객체의 복사는 그 객체가 관리하는 자원의 복사 문제를 안고가고 때문에 그 자원을 어떻게 복사하느냐에 따라 RAII객체의 

     복사 동작이 결정된다.

   - RAII클래스에 구현하는 일반적인 복사동작은 복사를 금지하거나 참조 카운팅을 해주는 선으로 마무리한다.

 

15. 자원관리 클래스에서 관리되는 자원은 외부에서 접근할 수 있도록 하자.

   - 실제 자원을 직접 접근해야 하는 기존 API가 많으므로 RAII클래스를 만들때는 그 클래스가 관리하는 자원을 얻을 수 있는 방법

     을 만들자.

   - 자원 접근은 명시적 변환 혹은 암시적 변환을 통해 가능하다.

     (명시적변환은 안전성에서 우수하고, 고객편의성을 놓고보면 암시적변환이 우수하다.)

 

16. new 및 delete를 사용할 때는 형태를 반드시 맞추도록 하자.

   - new 표현식에 []가 있으면, delete표현식에도 []를 써야 한다.

   - new 표현식에 []가 없으면 delete표현식에도 []를 사용해서는 안된다.

 

17. new로 생성한 객체를 스마트포인터에 저장하는 코드는 별도의 한 문장으로 만들자.

   - 예외가 발생할 때 디버깅을 하기 힘든 자원 누출이 초래될 수 있다.

     어떤 함수가 있고 동적으로 할당한 객체에 스마트포인터를 사용할 시

     int process();

     void foo(std::tr1::shared_ptr<ABC>(new ABC), process);

    

     위 소스는 컴파일은 가능하나 C++컴파일러는 함수 호출시 이루어지는 연산의 순서를 정하는데 있어서 상당한 자유도를 갖고 있

     으므로 (JAVA나 C#은 매개변수의 평가순서가 특정하게 고정) 컴파일러에 따라 자원 누출을 초래하는 결과가 생길 수도 있다.

 

     // 해결책

     std::tr1::shared_ptr<ABC> pw(new ABC);

     foo(pw, process());

     이렇게 하면 문장과 문장 사이에 있는 연산들이 컴파일러의 재조정을 받을 여지가 적어지므로 자원 누출 가능성이 없어진다.

 

18. 인터페이스 설계는 제대로 쓰기엔 쉽게, 엉터리로 쓰기엔 어렵게 하자.

   - 좋은 인터페이스 자체는 제대로 쓰기 쉽게, 엉터리로 쓰기에 어렵게 되어 있다.

   - 사용자의 실수를 방지하도록 새로운 타입을 만들거나, 타입에 대한 연산을 제한하기, 객체의 값에

     대해 제약걸기, 자원관리 작업을 사용자 책임으로 놓지 않기 등이 있다.

   - tr1::shared_ptr은 사용자 정의 삭제자를 지원한다. 이 특징 때문에 교차 DLL 문제를 막아주며, 뮤

     텍스등을 자동으로 잠금 해제하는데 쓸 수 있다.

 

   * 교차 DLL(Cross-DLL) : 객체 생성시 dll의 new를 썻는데 그 객체를 삭제할 때는 이전의 dll과 다

     른 dll에 있는 delete를 썻을 경우 발생한다.

 

19. 클래스 설계는 타입 설계와 똑같이 취급하자.

   - 새로 정의한 타입에 객체 생성 소멸은 어떻게 할 것인가?

   - 객체 초기화는 객체 대입과 어떻게 달라야 하는가?

   - 새로운 타입으로 만든 객체가 값에 의한 전달일 경우 어떤 의미인가?

   - 새로운 타입이 가질 수 있는 적법한 값에 대한 제약은 무엇으로 할 것인가?

   - 기존의 클래스 상속 계통망에 맞출 것인가?

   - 어떤 종류의 타입 변환을 허용할 것인가?

   - 어떤 연산자와 함수를 두어야 의미가 있는가?

   - 표준 함수중 어떤 것을 허용하지 말아야하는가(private member function)?

   - 멤버에 대한 접근 권한을 어느 쪽에 줄 것인가?

   - 선언되지 않은 인터페이스로 무엇을 둘 것인가?

   - 새로 만드는 타입이 얼마나 일반 적인가?

   - 정말로 꼭 필요한 타입인가?

   빠짐 없이 점검해보도록 하자.

 

20. '값에 의한 전달'보다는 '상수 객체 참조자에 의한 전달'방식을 사용하자.

   - 대체 적으로 효율 적일 뿐만 아니라(생성자, 소멸자가 호출되지 않음) 복사 손실 문제도 막아준다.

   - 단, 기본 제공 타입(int등), STL iterator, 펑터(함수 객체)에는 맞지 않다.

   - 이들은 값에 의한 전달이 더 적절하다.

 

21. 함수에서 객체를 반환해야 할 경우에 참조자를 반환하려고 들지 말자.

   - 함수 수준에서 새로운 객체를 만드는 방법은 두 가지 이다.(스택과 힙)

   - 지역 스택 객체에 대한 포인터나 참조자를 반환하는 일.

   - 힙에 할당된 객체에 대한 참조자를 반환하는 일.

   - 지역 정적 객체에 대한 포인터나 참조자를 반환하는 일.

   - 그런 객체가 두 개 이상 필요해질 가능성이 있다면 하지 말도록 하자.

 

22. 데이터 멤버가 선언될 곳은 private 임을 명심하자.

   - 데이터 멤버는 private로 선언하고 문법적으로 일관성 있는 데이터 접근 통로를 제공해주자.

   - protected는 public 보다 더 많이 보호 받고 있는 것이 아니라는 것을 명심하자.

 

23. 멤버 함수보다는 비 멤버 비프랜드 함수와 더 가까워지자.

   - 관련 기능을 구성하는데 있어서 패키지 유연성(package flexibility)가 높아진다.

   - 컴파일 의존도를 낮추며 함수의 확장성도 높일 수 있다.

   - 클래스 내부에서 멤버 함수를 정의하는 것도 좋지만

      void foo(Student& Stu)

      {

           stu.clearHistory();

           stu.clearList();

       }

     식으로 사용하는 것도 좋다는 것이다.

 

24. 타입 변환이 모든 매개 변수에 대해 적용되어야 한다면 비 멤버 함수를 선언하자.

   - 어떤 함수에 들어가는 모든 매개 변수(this 포인터가 가리키는 객체 포함)에 대해 타입 변환이 필요하다면 

     그 멤버 함수는 비멤버이어야 한다.

 

25. 예외를 던지지 않는 swap에 대한 지원도 생각해보자.

   -  std::swap이 타입에 대해 느리게 동작할 여지가 있다면 swap 멤버 함수를 제공하자(단 예외는 던지지 않게 만들 것)

   -  멤버 swap을 제공 했으면, 이 멤버를 호출하는 비멤버 swap도 제공하자.

   - 클래스(템플릿이 아닌)에 대해서는 std::wap을 만들도록 하자.

   - 사용자 입장에서 swap을 호출할 때는 std::swap에 대한 using 선언을 넣어준 후에 네임 스페이스 한정없이 swap을 호출하자.

   - 사용자 정의 타입에 대한 std 템플릿을 완전 특수화 하는 것이 가능하지만 std에 어떤 것이라도 새로 추가하려고 들지는 말자.

 

   - 1. 두 객체의 값을 맞바꾸는 함수를 swap이라 하고, public 멤버 함수로 둔다.

   - 2. 클래스나 템플릿이 들어 있는 네임 스페이스와 같은 곳에 비멤버 swap을 만든다. 그리고 1번에

         서 만든 swap 멤버 함수를 이 비멤버 함수가 호출하도록 한다.

   - 3. 새로운 클래스를 만든다면 그 클래스에 대한 std::swaap의 특수화 버전을 준비하자.

         이 버전에서도 swap 멤버 함수를 호출하도록 하자.

         template<typename T>

         {

              using std::swap;     // std::swap을 함수 안으로 끌어옴.

              ...

              swap(obj1, obj2);    // T타입의 전용 SWAP을 호출.

         }

 

출처 : http://blog.naver.com/sorkelf/40146367692

728x90
728x90

Effective C++의 내용 정리입니다. 

 

1. C++을 언어들의 연합체로 생각하자.

   - C++이 C에 가깝다는 생각은 예전 생각이고 이제는 경우에 따라 프로그래머 규칙을 새로이 생각함으로써 바꿀 필요가 있는데 

     그 경우란 C++의 어떤 부분을 사용하느냐에 따라 달려 있다.

     C++은 크게 4가지로 나뉘는데 종류는 다음과 같다.

     1) C : C++은 당연히 C를 기본으로 작성되어 있다.

     2) 객체지향개념의 C++ : 클래스를 사용하는 C에 관한 모든 것.

     3) Template C++ : C++의 일반화 프로그래밍 부분.

     4) STL : 컨테이너, 반복자, 알고리즘 펑터로 이루어진 템플릿 라이브러리.

 

2. #define을 쓰려거든 const, enum, inline을 먼저 생각하자.

   - 단순 상수일 경우일때는 #define보다 const 객체나 enum을 사용하자.

   - 함수처럼 쓰이는 매크로를 만들 경우 #define보다는 template inline함수를 사용하자.

 

3. 가능하면 const를 사용한다.

   - const를 붙여 사용시 컴파일러가 사용상의 에러를 잡아내는데 도움을 준다.

     어떤 유효범위에 있는 객체에도 붙어 있을 수 있다.

   - 상수 멤버나 비상수 멤버함수가 기능적으로 서로 같게 구현되어 있을 경우에 코드 중복이 일어날

      수 있다. 이럴땐 비상수 버전이 상수버전을 호출하도록 사용하자. (mutable 키워드)

 

4. 객체 사용 이전엔 반드시 그 객체를 초기화 하자.

   - 기본제공 타입의 객체(int, float)는 직접 손으로 초기화 하자.

   - 생성자에서 멤버이니셜라이저를 사용하자. (const상수 멤버 및 변수는 물론 생성자 이전에 호출함으로 상당히 유용하다.)

   - 여러 번역 단위에 있는 비지역 정적객체들의 초기화 순서 문제를 피해서 클래스 설계를 하자. (Singleton Pattern)

 

5. C++가 은근슬쩍 만들어 호출해 버리는 함수들에게 신경을 쓰자.

   - 컴파일러는 경우에 따라 클래스에 대해 기본 생성자, 복사 생성자, 복사 대입연산자, 소멸자를 암시적으로 생성한다.

 

6. 컴파일러가 만들어낸 함수가 필요없으면 이들의 사용을 확실히 없애버리자.

   - 컴파일러에서 자동으로 제공하는 기능을 허용하지 않으면, 대응되는 멤버함수를 private으로 선언한 후에 구현은 하지 않은 채

     로 두자. 기본 클래스를 사용하는 것도 한 방법이다.

 

7. 다형성을 가진 기본 클래스는 반드시 가상소멸자를 사용한다.

   - 상속을 하는 기본 클래스일 경우 가상 소멸자를 사용하자.

   - 기본 클래스로 설계되지 않았거나 다형성을 갖도록 설계되지 않은 클래스에는 가상소멸자는 선언하지 말자.

 

8. 예외가 소멸자를 떠나지 못하도록 붙들어 놓자.

   - 소멸자에서는 예외가 빠져나가지 않도록 하되, 가능성이 있다면 소멸자에서 받아 삼키거나 프로그램을 종료하자.

   - 어떤 예외에 대해 사용자가 반응해야 할 필요가 있다면 해당 연산을 제공하는 함수는 소멸자가 아닌 함수여야 한다.

 

9 . 객체 생성 및 소멸 과정중에 절대 가상 함수를 호출하지 말자.

   - 생성자 혹은 소멸자안에서는 가상함수를 호출하지 말자.

 

10. 대입 연산자는 *this의 참조자를 반환하게 하자.

   - T& operator=(const T& rhs)

     {

          ......

          return *this;

     }

 

11. operator=에서는 자기 대입에 대한 처리가 빠지지 않도록 하자.

   - 원본 객체와 복사대상 객체의 주소를 비교해도 되고, 문장의 순서를 적절히 조정해도 되고 copy and swap 을 사용해도 된다.

   - 두개 이상의 객체에 대해 동작하는 함수가 있다면, 이 함수에 넘겨지는 객체들이 사실 같은 객체인경우에 정확하게 동작하는지 

     확인하자.

 

12. 객체의 모든 부분을 모조리 복사하자.

   - 객체 복사 함수는 주어진 객체의 모든 데이터 및 부모 클래스 부분도 빠뜨리지 않고 복사해서 한다.

   - 클래스의 복사 함수 두개를 구현할 때 한쪽을 이용해서 다른 쪽을 구현하려는 시도는 하지말자.

 

출처 : http://blog.naver.com/sorkelf/40146367692

728x90
728x90

여기서의 차이는 ++i가 증감한 뒤 i를 리턴하고 i++가 리턴하고 증감하는 차이를 설명하고자 하는게 아니다. 

여기서는 보다 자세하게 실제 내부적으로 어떠하게 구현되어 있는지 알아볼 필요가 있다.

우선  ++i의 내부를 살펴보면

 

i = i + 1;

return i;

 

인데 반해 i++은

 

variouble temp;

temp = temp + 1;

return temp

 

로써 temp라는 변수를 선언한 후 연산한다. 즉 메모리의 temp라는 값이 남으며 temp를 선언해야 하기 때문에 한번 더 연산을 해야하는 과정이 생겨있다.

즉 iterator를 사용하거나 반복문에서 사용할 경우에는 i++보다는 ++i가 효율적이다.

 

출처 :  http://blog.naver.com/sorkelf/40146367692

728x90
728x90

#pragma 

Precomplier 즉 전처리기로서 컴파일 하기 이전에 해당 명령을 처리해주는 명령어이다.

Visual C++에는 다양한 프로젝트 옵션 등이 제공되지만

혹여 다른 컴파일러를 사용할 경우도 생길지 모르니

옵션 위주보다는 #pragma를 사용하는 것을 권장한다.

몇가지 옵션을 소개한다.

 

#pragma once : 해당 소스가 단 한번만 포함되게 한다.

 - 여러번 include 되는 것을 방지하며 이 파일은 한번만 포함되는 것을 알 수 있다.

   #ifndef #endif 와 같은 방식이다.

 

 

#pragma pack : 구조체의 정렬 방식을 지정한다.

 - 제어문 종류 :: pack(n) : n으로 정렬 방식을 바꾼다.

     default : Default 정렬 방식을 바꾼다.

     pack(push, n) : n으로 정렬 방식을 바꾸고 기존 정렬 방식은 스택에 저장한다.

     pop : 스택에 저장한 정렬방식으로 다시 돌린다.

     여기서 n은 컴파일러가 저장된 메모리에 데이터를 정렬하는 방법을 결정하는 바이트 크기이다.

     보통은 4바이트이다.

 

ex) #pragma pack(1)

typedef struct

{

     char ch1;      // 1바이트

     char ch2;      // 1바이트

     char* pchar; // 4바이트

     int a;            // 4바이트

     int* pa;         // 4바이트

}STUDENT, *LP_STUDENT;

#pragma pack() 이 경우 구조체의 크기는 14바이트여야 하나 실제로 sizeof()를 구해보면 16바이트이다. 이 때 pack을 사용하면 빈공간없이 14바이트로 구조체를 사용할 수 있다.

 

 

#pragma data_seg : dll에서 데이터를 공유하고자 할 때 사용된다.

 

 

#pragma warning : 컴파일시 나오는 경고를 설정한다.

 - 제어문 종류 : once : 번호 -> 반복되는 경고를 한번만 출력

      default : 번호 -> 원래 설정대로 되돌린다.

      disable : 번호 -> 경고 출력을 하지 않는다.

      error : 번호 -> 경고를 에러로 처리한다.

      level : 번호 -> 경고의 레벨을 변경한다.

      push[, n] -> 모든 경고의 레벨을 저장한다. n이 있을 경우 저장과 동시에 전역 경고 레벨을 n으로 변경한다.

      pop -> 스택에 마지막으로 저장된 경고 레벨을 복원한다.

 

ex) #pragma warning(disable : 4244)

 - 4244번의 Warning에 대한 경고를 표시하지 않는다.

    #pragma warning(3 : 4244)

 - 4244번 경고를 레벨 3으로 올린다.

 

 

#pragma message("Hello") : 컴파일 시 특정 문장을 표시하게 한다.

ex)

      #define __DISMSG__(x) #x

      #define __PREFIX__(x) __DISMSG__(x)

      #define __WARNING__ __FILE__ "("__PREFIX__(__LINE__)") warning : "

      #define __ERROR__  __FILE__ "("__PREFIX__(__LINE__)") error : "

 

      #pragma message(">> compliing begin point...")

 

      int main()

      {

           char szMsg[100];

           memset(szMsg, 0x00, sizeof(szMsg));

           strcpy(szMsg, "helloworld");

           #pragma message(">> "__WARNING__ "strcpy로 하면 안전하지 않다.")

           printf("%s\n", szMsg);

      }

 

      #pragma message(">> compliing end point...")

 


 

 

#pragma intrinsic(함수명1, 함수명2) : 해당 함수를 호출시 function call이 아닌 inline 형식으로 코드자체를 함수 사용하는 장소에 

                                           삽입시켜 최적화를 시켜준다.

 

 

#pragma function(함수명1, 함수명2) : 해당 함수를 function call 형태로 구현되도록 지시한다.

                                           둘중에 하나를 선언 후 다시 변경할 때까지 pragma가 기술된 첫 함수부터 영향을 미쳐 소스

                                           코드의 마지막 라인까지 영향을미친다.

 

 

#pragma comment(lib, "링크명.lib") : 해당 dll이나 라이브러리를 링커에 포함시키도록 한다.

 

 

#pragma optimize("옵션", on/off) : 함수 밖에 선언이 되며 pragma가 선언된 후 정의된 첫 번째 함수

                                                 에 영향을 미치도록 한다.

옵션 - g : 전역 최적화 옵션을 활성화

     - p : Floating Point의 Consistency를 향상시킨다.

     - s or t :

     - y : 프로그램 스택의 프레임 포인터를 일반화 시킨다.

     - 옵션의 아무것도 주지 않고 뒤에 파라미터를 사용시 모든 옵션에 대해 설정이 적용된다.

 

ex) #pragma optimize("", on)

    #pragma optimize("tp", off)

 

 

#pragma region & endregion : 멤버 함수나 변수들을 일정한 분류를 시켜준다. 해당 영역을 줄이거나

                                          확장시켜 코드의 가독성을 높일 수 있다.

 

ex) #pragma region 테스트용 함수들

void Test1() {}

void Test2() {}

void Test3() {}

  #pragma endregion 테스트용 함수들

 

더 자세한건 msdn을 찾아보자.

http://msdn.microsoft.com/ko-kr/library/h7k3a0bw.aspx

 

출처 : http://blog.naver.com/sorkelf/40146367692

728x90

+ Recent posts