~cpp char *pc = 0; char& rc = *pc;프로그래머 초급자면 알수 있는 바보 짓이다. 하지만 범하기 쉬운게 문제다.
~cpp string& rs; // Reference(참조)가 초기화가 되지 않아서 에러 string s("xyzzy"); // 이건 된다 신기하네 string& rs = s;아직 string써본적 한번도 없다. 반성..
~cpp void printDouble(const double& rd) { cout << rd; // rd에 관한 특별한 검사 필요 없다. reference니까. } //////////////////////////////////////////////////////////////////// void printDouble (const double* pd) { if (pd){ cout << *pd // pd가 null인지 검사 해야 한다. pointer니까. } }pointer의 유의 사항인 null에 관한 내용을 다시 알려준다.
~cpp string s1("Nancy"); // 이제는 무슨 퀴즈 같다. string s2("Clancy"); string& rs = s1; string* ps = &s1; rs = s2; // s1의 인자가 "Clancy" 로 바뀐다는 의미다. ps = &s2; // 그냥 포인터만 세팅하는 거다. // 이걸로 ps는 s2를 가리키고 s1에는 아무런 영향을 끼치지 않는다.사견: Call by Value 보다 Call by Reference와 Const의 조합을 선호하자. 저자의 Effective C++에 전반적으로 언급되어 있고, 프로그래밍을 해보니 괜찮은 편이었다. 단 return에서 말썽이 생기는데, 현재 내 생각은 return에 대해서 회의적이다. 그래서 나는 COM식 표현인 in, out 접두어를 사용해서 아예 인자를 넘겨서 관리한다. C++의 경우 return에 의해 객체를 Call by Reference하면 {} 를 벗어나는 셈이 되는데 어디서 파괴되는 것인가. 다 공부가 부족해서야 쩝 --;
~cpp (type) expressionC++ style cast
~cpp static_cast<type>(expression) const_cast<type>(expression) dynamic_cast<type>(expression) reinterpret_cast<type>(expression)
~cpp class Widget{ ...} class SpecialWidget:public Widget{...} void update( SpecialWidget *psw); SpecialWidget sw; const SpecialWidget& csw = sw; update(&csw); // 당연히 안됨 const인자이므로 변환(풀어주는 일) 시켜 주어야 한다. update(const_cast<SpecialWidget*>(&csw)); // 옳타쿠나 update((SpecialWidget*)&csw); // C style인데 잘 돌아간다. // C++는 C와의 호환성 고려를 위한 것이므로 위와 같이 // 동작 하지만 명시적이지 못한면이 지적된다. Widget *pw = new SpecialWidget; update(pw); update(const_cast<SpecialWidget*>(pw)); // error! // const_cast<type>(expression) 는 // 오직 상수(constness)나 변수(volatileness)에 영향을 미친다. // 이런말 하면 다 가능한 듯 싶고 static_cast 와 차이가 없는 것 // 같은데 옆의 소스 처럼 down cast가 불가능하다.
~cpp Widget *pw ... update( dynamic_cast<SpecialWidget*>(pw)); // 옳다. // const_cast 가 down cast가 불가능 한 대신에 dynamic_cast 는 말그대로 // 해당 부모 객체의 포인터에 자식 객체가 가르켜 있으면 형변환이 가능하다. // 불가능 하면 null 을 반환해주는 기특한 역할이 핵심이다. void updateViaRef(SpecialWidget& rsw); updateViaRef(dynamic_cast<SpecialWidget&>(*pw)); // 옳다. // reference에서도 사용 가능하다? 그럼 불가능 할시의 처리는? // 이럴때는 예외(exception)을 발생시켜 준다.
~cpp #define static_cast(TYPE, TEXPR) ((TYPE) (EXPR)) #define const_cast(TYPE, TEXPR) ((TYPE) (EXPR)) #define reinterpret_cast(TYPE, TEXPR) ((TYPE) (EXPR))
~cpp double result = static_cast(double, firstNumber)/ secondNumber; update(const_cast(SpecialWidget*, &sw)); funcPtrArray[0] = reinterpret_cast(FuncPtr, &doSomething);
~cpp class BST { ... }; class BalancedBST : public BST { ... };이런 클래스를 선언했다. 그리고 다음과 같은 함수로 해당 클래스의 배열을 사용한다고 가정하자
~cpp void printBSTArray( ostream& s, const BST array[], int numElements) { for ( int i = 0; i < numElements; ++i ){ s << array[i]; } }그리고 다음과 같이 사용한다.
~cpp BST BSTArray[10]; ... printBSTArray(cout, BSTArray, 10); // 올바르게 작동한다. 아무 문제 없다. ////////////////////////////////////// BalancedBST bBSTArray[10]; ... printBSTArray(cout, bBSTArray, 10); // 과연 올바르게 작동하는가?
~cpp printBSTArray( cout, bBSTArray, 10 );은 정말 최악의 결과를 불러 들인다.
~cpp void deleteArray( ostream& logStream, BST array[]) { logStream << "Deleting array at address " << static_cast<void*>(array) << '\n'; delete [] array; } BalanceBST *balTreeArray = new BalancedBST[50]; ... deleteArray(cout, balTreeArray); // 이것 역시 제대로 지워지리가 없다.자 위와 같이 객체의 지움을 담당하는 함수를 작성했을때 역시 문제가 있다.
~cpp delete [] array는 다음과 같은 코드로 교체되는것과 같다.
~cpp for( int i = the number of elements in the array -1; i>0; --i) { array[i].BST::~BST(); }부모 객체의 파괴자만 부르므로 역시 memory leak 현상을 일으킨다.
~cpp class EquipmentPiece { public: EquipmentPiece(int IDNumber); ... }
~cpp EquipmentPiece bestPieces[10]; EquipmentPiece bestPieces = new EquipmentPiece[10]; // 두경우 초기화 해줄 방도가 없어서 에러이다.이걸 이렇게 풀어 보자
~cpp int ID1, ID2, ID3, ... ID10; ... EquipmentPiece bestPiece[] = { EquipmentPiece(ID1), EquipmentPiece(ID2), EquipmentPiece(ID3), ..., EquipmentPiece(ID10), }하지만 이럴 경우에는 array를 heap arrays(heap영역 사용 array라는 의미로 받아들임)로의 확장이 불가능한 고정된 방법이다.
~cpp typedef EquipmentPiece* PEP; PEP bestPieces[10]; PEP *bestPieces = new PEP[10]; for ( int i = 0; i< 10; ++i) bestPiece[1] = new EquipmentPiece( ID Number );하지만 이러한 방법은 한눈에 봐도 문제가 예상된다. 바로 delete문제
~cpp void *rawMemory = operator new[](10*sizeof(EquipmentPiece)); EquipmentPiece *bestPieces = static_cast<EquipmentPiece*>(rawMemory); for ( int i = 0; i< 10; ++i) new (bestPieces+1) EquipmentPiece ( ID Number ); // 이건 placement new 라고 하여 Item 8 에서 언급한다. // 처음 보고 놀랐다. 어서 사기야 하면서 --;; // 이 책 주특기다. 뒤에 설명하니까 그냥 넘어가 하는거 // 암튼 의미는 이해 갈것이다.참 거지 같은 짓 잘해 놓는다.
~cpp delete [] rawMemory;역시나 이것도 delete에 관한 모호성을 제공한다. 문제시 되는 점은 rawMemory상에 배치된 EquipmentPiece의 destructor 호출 유무이다. 예상대로 destructor를 강제 호출해 줘야 한다. 그래서 위가 아니라, 이런식의 삭제가 된다.
~cpp for ( int i = 9; i>= 0; --i) bestPieces[i].~EquipmentPiece(); // 언제나 느끼는 거지만 C++을 방종을 가져다 줄수 있다. operator delete[](rawMemory);참고로
~cpp delete [] bestPieces; // 이렇게 지우는건 new operator가 건들지 않아서 불가능하다.
~cpp template<class T> class Array{ public: Array(int size); ... private: T *data; }; template<class T> Array<T>::Array(int size) { data = new T[size]; .... }첫번째에서 제기된 문제가 이번에는 template class 내부에서 일어 나고 있는 셈이다. 거참 암담한 결과를 초례한다. 문제는 이러한 template class 가 이제는 아예 STL같은 library로 구축되었단 사실. 알아서 잘 기본 생성자 만들어 적용하시라. 다른 방도 없다.