~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로 구축되었단 사실. 알아서 잘 기본 생성자 만들어 적용하시라. 다른 방도 없다.