[GPG 1 글 1.1] Clear(), Destroy() 구조에 대해....

GPG 시리즈 관련 질답, 논의 공간.

Moderator: 류광

Gamza
Posts: 610
Joined: 2001-10-11 09:00
Contact:

Clear(), Destroy() 구조에 대해....

Post by Gamza »

GPG에서 보셨을텐데.....

이걸 실제로 쓰려니 상속에서 좀 껄끄러운 부분이 생기네요

class A
{
public:
A(){ Clear(); }
virtual ~A(){ Destroy(); }
void Clear(){ ... }
void Destroy(){ ...; Clear(); }
};

class B : public A
{
public:
B(){ Clear(); }
virtual ~B(){ Destroy(); }
void Clear(){ ...; A::Clear(); }
void Destroy(){ ...; A::Destroy(); Clear(); }
};

GPG에서 말했듯...Destroy() 의 마지막에 Clear() 를 넣어줘야 할거 같은데.....

상속을 하다보니....부모객체의 Clear() 가 두번 호출되는 결과가....

거기다가.....자칫 호출 순서를 뒤바꾸기라도 한다면....정말 큰일이겠죠...

void Destroy(){ ...; Clear(); A::Destroy(); }

...뭔가 깔끔한 해결책이 있을것 같은데...ㅡ.ㅡ;;

조언 부탁드립니다.

감자 성수올림...@~
류광
Posts: 3805
Joined: 2001-07-25 09:00
Location: GPGstudy
Contact:

Post by 류광 »

아... 어디에 나왔던 거였죠?
(역자가 할 질문은 아니겠지만-.-)
Gamza
Posts: 610
Joined: 2001-10-11 09:00
Contact:

Post by Gamza »

1권 1.1 객체지향적 프로그래밍과 설계 기법

48~49page 에 나온내용입니다.

거기엔 Create도 언급이 되어있는데....

질문하고자 하는 문제와는 상관이 없어서 빼버렸습니다.
류광
Posts: 3805
Joined: 2001-07-25 09:00
Location: GPGstudy
Contact:

Post by 류광 »

C++ 언어 메커니즘 차원에서의 해결 방법을 원하시는 것인지.. 아마 있을 것 같은데(virtual이라던가 override라던가 등등) 잘 모르겠습니다..(사실 저는 이런 부분이 아직도 제대로 정리가 안 되고 있습니다)


그와는 별도로... 첫번째로 드는 생각은.. Clear()가 두 번 호출되어도 상관이 없게 만든다.. 는 것입니다. 기본형식(int, float 등)의 포인터가 아닌 멤버 변수들을 0으로 초기화하는 수준이라면 100 번 호출되어도 문제가 없겠죠..

마찬가지로 호출 순서에 상관없이 안전하게 작동할 수 있을 만한 것들만 수행하도록 하구요.. 포인터 멤버들이 필요하다면 단일체라던가 스마트 포인터 류의 것들이 도움이 될 것 같습니다.

그것보다도.. 저자의 주장에 대한 한가지 반대 의견으로.. 생성과 소멸에서 동일한 멤버 함수를 호출한다는 것이 왠지 냄새(!)가 납니다. 생성과 소멸은 정반대의 것인데 동일한 뭔가를 수행한다는 게 왠지... 냄새가.. Destroy()에서 꼭 Clear()를 호출해야 할까요??
(번역할 때에는 이런 의문이 들지 않았는데.. 감자님 감사!)
sk74
Posts: 23
Joined: 2001-12-09 09:00

지금 책이 없어서 정확치가 않지만..

Post by sk74 »

안녕하세요.

그 부분을 읽은 기억이 확실히 나기는하지만 지금 책이 없어서 정확하지는
않습니다. Clear ()를 호출하는건 소멸자가 아닐텐데요?

게임에서 class를 필요할때 마다 새로이 생성을 하는게 아니라, 한번 생성
한걸 재활용하는 의미에서 그렇게 썼던 걸로 기억을 합니다. 그러니까
소멸자에서 Clear ()가 호출되게 하는게 아니라 destory ()를 따로 만들고
그 후에 Clear ()를 호출해서 class를 생성 초기상태로 되돌려두자 뭐 이런
식이 였던걸로 기억합니다.

그 섹션의 글을 며칠전에 다시 봤거든요. 실제로 전 그런 방법을 쓰고 있구
요. destroy ()가 따로 필요하지는 않아서 clear () 하나만 두고 있기만 하지
만요. 배열에 클래스 때려 박아 두고 있습니다. ㅡ.ㅡ;;
Gamza
Posts: 610
Joined: 2001-10-11 09:00
Contact:

Post by Gamza »

GPG에서는 소멸자가 명시적으로 Clear를 호출하는건 아니고요,

소멸자가 호출하는 Destroy가 Clear를 호출하는겁니다.

Destroy입장에서는 파괴된 객체가 불법적으로 사용되는것을 막기위해

객체를 안전하게 초기화할 필요가 있다는거겠죠.

Clear가 두번 호출되는건 순전히 기분상 쓸데없는 일을 두번씩 한다는 것

때문에 말씀드린거라....

류광님 말씀처럼 만들어 두고 그냥 상관없다고 생각하면 그만이겠네요.

(그래도...전 왠지 찜찜....ㅡ.ㅡ;; )

하지만 호출순서 문제는 좀 심각한거 같네요.

Destroy라고 하면 일반적으로 할당된 리소스의 해제를 담당하게 되기때문에,

Destroy보다 먼저 Clear가 호출되어버리면 곤란하다는 생각입니다.

그냥 '주의해서 짜라'고 말씀드린다면....모두들 안쓰고 만다고 하실지도....

PS.
스마트 포인터를 사용한다고 하면....Clear가 참조카운터를 줄여주는....
즉....Destory역할을 해버리는 셈이니....
그런 리소스만 포함하도록 설계한다고 하면
sk74님이 하신대로 Destroy가 아예 필요 없어지게 됩니다.

PS.
Destroy의 반대는 Create....
Create와 Destroy가 쌍이 안맞아도 돌도록 하라는 말을 한거보면 아마도
이런구조를 생각한것 같습니다.
Create(){ Destroy(); ..... }
Destroy(){ .....; Clear(); }
Gamza
Posts: 610
Joined: 2001-10-11 09:00
Contact:

Post by Gamza »

출근길...지하철에서 졸다가.....(!)

Clear를 하위 클래스에서 중첩하지 못하도록 private으로 막아버리면 모든 문제가 깔끔하게 처리되는군요.

외부에서 Clear를 쓰려면 Destroy를 사용하면 되고...
(생각해보니 외부에서 명시적으로 Clear를 호출할수 있게 놔두는건 위험천만)

정리하면 이렇게 되네요.

Code: Select all

class B : public A 
{ 
public: 
	B(){ Clear(); } 
	virtual ~B(){ Destroy(); } 
	void Destroy(){ ...; Clear(); A::Destroy(); } //여기가 유일한 중첩
private:
	void Clear(){ ...; } // class B 의 멤버만 초기화
};
이렇게 하고나니 Clear의 역할도 훨씬 분명하고, 보다 안전하게 된듯...

Clear()
- 생성자와 Destroy에서 공통적으로 발생하는 멤버초기화코드를 묶어놓은것
- 모든멤버가 유효한 리소스를 가리키고 있지 않다는 전제하에 동작.
- 생성자와 Destroy이외의 곳에서 절대로 호출하지 말것! ( Destroy를 사용 )
- 자기 자신의 멤버만 초기화 할것!

Destroy()
- 모든 멤버의 유효한 리소스를 해제하고, 멤버를 초기화한다.
- 모든 멤버가 초기값이거나, 유효한 리소스를 가리키고 있다는 전제하에 동작.
- 반드시 부모클래스의 Destroy()를 호출할것!
Post Reply