["MoreEffectiveC++"] ||[[TableOfContents]]|| = Techniques = == Item 25: Virtualizing constructors and non-member functions == * Item 25: ìƒì„±ìžì™€ 비멤버 함수를 ê°€ìƒìœ¼ë¡œ ëŒì•„가게 하기. === Virtual Constructor : ê°€ìƒ ìƒì„±ìž === ê°€ìƒ ìƒì„±ìž, ì´ê²ƒì— 관해서 ìƒê°í•´ 보기는 좀 ìƒì†Œí•˜ë‹¤. 사실 ê°€ìƒ í•¨ìˆ˜ë¥¼ ë¶€ë¥¼ë ¤ë©´, ê°ì²´ì—대한 참조나, í¬ì¸í„°ë¥¼ ê°€ì§€ê³ ìžˆì–´ì•¼ 하는ë°, ìƒì„±í• 때 부터 ê°€ìƒ(virtual) 함수를 사용한다? 좀 ì´ìƒí•˜ì§€ 않ì€ê°€? 어떨때, 어떻게 ì´ '''ê°€ìƒ ìƒì„±ìž''' ë¼ëŠ”ê±¸ ì¨ë¨¹ì„수 있ì„꺼? 사실 ê°€ìƒ ìƒì„±ìžë¼ëŠ”ê±´ 존재하지 않는다. 하지만 ì´ë¥¼ 비슷하게 구현하는 것ì´ë‹¤. 예를들ìžë©´ ë‹¹ì‹ ì´ newsletterì„ ë³´ë‚´ëŠ” 어플리케ì´ì…˜ì„ 짜는ë°, ì—¬ê¸°ì— ê¸€ê³¼ 그림 ë°ì´í„° ì¸ìžë¡œ êµ¬ì„±ì‹œí‚¨ë‹¤ê³ ê°€ì •í•˜ë©´ ì´ë ‡ê²Œ 만들수 ìžˆì„ ê²ƒì´ë‹¤. {{{~cpp class NLComponent{ // News Letter ì˜ ë¶€ëª¨ ì¸ìž public: ... }; class TextBlock:public NLComponent{ // ê¸€ì„ í‘œí˜„í•˜ëŠ” ì¸ìž public: ... }; class Graphic:public NLComponent{ // ê·¸ë¦¼ì„ í‘œí˜„í•˜ëŠ” ì¸ìž public: ... }; class NewsLetter { // 글과, ê·¸ë¦¼ì„ ê°€ì§€ëŠ” News Letter public: ... private: list<NLComponent*> components; // 글과 그림 ì¸ìžì˜ ì €ìž¥ì†Œ }; }}} ì´ëŸ° 코드는 다ìŒê³¼ ê°™ì´ í‘œí˜„ëœë‹¤. http://zeropage.org/~neocoin/data/MoreEffectiveC++_124_1.gif list í´ëž˜ìŠ¤ëŠ” STL로 ì´ë£¨ì–´ì ¸ 있는ë°, Item 35를 ì°¸ê³ í•˜ë©´ ì •ë³´ë¥¼ ì–»ì„수 있다. 단순히 ì´ì¤‘ ì—°ê²° 리스í¬(double linked list)ë¼ê³ ìƒê°í•˜ë©´ 무리가 ì—†ì„ ê²ƒì´ë‹¤. NewsLetter ê°ì²´ëŠ” 아마 디스í¬ì—서 ìžë£Œë¥¼ ì ìž¬í• ê²ƒì´ë‹¤. NewsLetterê°€ 디스í¬ì—서 ìžë£Œë¥¼ ê°€ì§€ê³ ë³´ì—¬ì£¼ê¸° 위해 istreamì„ ì‚¬ìš©í•´ì„œ NewsLetter를 êµ¬ì„±í• ê°ì²´ë“¤ì„ ìƒì„±í•œë‹¤ê³ ê°€ì •í•œë‹¤ë©´ 다ìŒê³¼ ê°™ì€ ì½”ë“œë“¤ì„ ëŒ€ê°• 만들수 ìžˆëŠ”ë° {{{~cpp class NewsLetter { public: NewsLetter(istream& str); ... }; }}} 가짜 코드로 ì´ ìƒì„±ìžë¥¼ 대강 표현하면 {{{~cpp NewsLetter::NewsLetter(istream& str) { while(str){ 다ìŒì— 추가ë ì¸ìž ê°ì²´(component object)를 str로 부터 ì½ëŠ”ë‹¤. newsletterì˜ ì¸ìžì¤‘ ë¦¬ìŠ¤íŠ¸ì¸ componentsì— ë§Œë“ ê°ì²´ë¥¼ 넣는다. } } }}} í˜¹ì€ ì•½ê°„ 수를 ì¨ì„œ readComponent를 호출하는 ì‹ìœ¼ë¡œ 바꾼다면 {{{~cpp class NewsLetter{ publicc: ... private: // 순수히 디스í¬ì— 있는 ë°ì´í„°ë¥¼ ì½ì–´ 드리는 istreamê³¼ ìžë£Œ strì—서 // 해당 ìžë£Œë¥¼ í•´ì„í•˜ê³ , 알맞는 ê°ì²´ë¥¼ ë§Œë“¤ê³ ë°˜í˜¸ë‚˜í•˜ëŠ” readComponentê°ì²´ static NLComponent * readComponent(istream& str); }; NewsLetter::NewsLetter(istream& str) { while (str) { // readComponentê°€ í•´ì„한 ê°ì²´ë¥¼ newsletterì˜ ë¦¬ìŠ¤íŠ¸ ë§ˆì§€ë§‰ì— ì¶”ê°€ì‹œí‚¤ëŠ” ê³¼ì • components.push_back(readComponent(str)); } } }}} readComponentê°€ ë¬´ì—‡ì„ ì–´ë–»ê²Œ 하는지 ê¶ë¦¬í•´ ë³´ìž. ìœ„ì— ì–¸ê¸‰í•œë“¯ì´ readComponent는 ë¦¬ìŠ¤íŠ¸ì— ë„£ì„ TextBlock나 Graphicí˜•ì˜ ê°ì²´ë¥¼ 디스í¬ì—서 ì½ì–´ 드린 ìžë£Œë¥¼ 바탕으로 만들어 낸다. ê·¸ë¦¬ê³ ìµœì¢…ì 으로 만들어진 해당 ê°ì²´ì˜ í¬ì¸í„°ë¥¼ 반환해서 listì˜ ì¸ìžë¥¼ 구성하게 해야 í• ê²ƒì´ë‹¤. ì´ë•Œ 마지막 코드ì—서 ê°€ìƒ ìƒì„±ìžì˜ ê°œë…ì´ ë§Œë“¤ì–´ ì ¸ì•¼ í• ê²ƒì´ë‹¤. ìž…ë ¥ë˜ ëŠ”ìžë£Œì— 기초ë˜ì–´ì„œ, 알아서 만들어 ì¸ìž. ê°œë…ìƒìœ¼ë¡œëŠ” 옳지만 ì‹¤ì œë¡œëŠ” ê·¸ë ‡ê²Œ 구현ë 수는 ì—†ì„ ê²ƒì´ë‹¤. ê°ì²´ë¥¼ ìƒì„±í• 때 부터 í˜•ì„ ì•Œê³ ìžˆì–´ì•¼ 하는건 ìžëª…하니까. ê·¸ë ‡ë‹¤ë©´ 비슷하게 구현해 본다? ê°€ìƒ ìƒì„±ìžì˜ ë°©ì‹ì˜ 한 종류로 특별하게 ê°€ìƒ ë³µìž ìƒì„±ìž(virtual copy constructor)는 ë„리 ì“°ì¸ë‹¤. ì´ ê°€ìƒ ë³µì‚¬ ìƒì„±ìžëŠ” 새로운 ì‚¬ë³¸ì˜ í¬ì¸í„°ë¥¼ ë°˜í™˜í•˜ëŠ”ë° copySlef나 cloneSelfê°™ì€ ë³µì‚¬ì˜ ê°œë…으로 ìƒì„±ìžë¥¼ 구현하는 것ì´ë‹¤. ë‹¤ìŒ ì½”ë“œì—서는 cloneì˜ ì´ë¦„ì„ ì“°ì˜€ë‹¤. {{{~cpp class NLComponent{ public: // ê°€ìƒ ë³µì‚¬ ìƒì„±ìž virtual copy constructor ì„ ì–¸ virtual NLComponent * clone() const = 0; ... }; class TextBlock: public NLComponent{ public: virtual TextBlock * clone() const // ê°€ìƒ ë³µì‚¬ ìƒì„±ìž ì„ ì–¸ { return new TextBlock(*this); } ... }; class Graphic:public NLComponent{ public: virtual Graphic * clone() const // ê°€ìƒ ë³µì‚¬ ìƒì„±ìž ì„ ì–¸ { return new Graphic(*this); } ... }; }}} 보다시피 í´ëž˜ìŠ¤ì˜ ê°€ìƒ ë³µì‚¬ ìƒì„±ìžëŠ” ì‹¤ì œ 복사 ìƒì„±ìžë¥¼ 호출한다. 그러므로 "복사" ì˜ë¯¸ë¡œëŠ” ì¼ë°˜ 복사 ìƒì„±ìžì™€ 수행 ê¸°ëŠ¥ì´ ê°™ë‹¤ 하지만 다른 ì ì€ ë§Œë“¤ì–´ì§„ ê°ì²´ë§ˆë‹¤ ì œê°ê°ì˜ 알맞는 복사 ìƒì„±ìžë¥¼ ë§Œë“ ë‹¤ëŠ” ì ì´ ë‹¤ë¥´ë‹¤. ì´ëŸ° cloneì´ NewsLetter 복사 ìƒì„±ìžë¥¼ 만들때 NLComponentë“¤ì„ ë³µì‚¬í•˜ëŠ”ë° ê¸°ì—¬ë¥¼ 한다. 어떻게 ë” ì‰¬ìš´ ìž‘ì—…ì´ ë˜ëŠ”ì§€ ë‹¤ìŒ ì˜ˆì œë¥¼ ë³´ë©´ ì´í•´ í• ìˆ˜ ìžˆì„ ê²ƒì´ë‹¤. {{{~cpp class NewsLetter{ public: NewsLetter(const NewsLetter* rhs); // NewsLetterì˜ ë³µì‚¬ ìƒì„±ìž ... private: list<NLComponent *> components; }; NewsLetter::NewsLetter(const NewsLetter* rhs) { for (list<NLComponent*>::constiterator it = rhs.components.begin(); it != rhs.components.end(); ++it){ // itì€ rhs.componentsì˜ í˜„ìž¬ë¥¼ 가리키는 ì¸ìžë¡œ, 복사ë˜ì–´ì•¼ í• ê°ì²´ì´ë‹¤. // í˜•ì„ ê°€ë¦¬ëŠ” 작업 ì—†ì´ ê·¸ëƒ¥ cloneì„ ì´ìš©í•´ ê³„ì† ë³µì‚¬í•´ 버리면 ê° í˜•ì— ì•Œë§žëŠ” 복사 // ìƒì„±ìžê°€ 불리면서 복사 ë˜ëŠ” 것ì´ë‹¤. components.push_back((it*)->clone()); } } }}} STLì´ ìƒì†Œí•˜ë‹¤ë©´ ë‚˜ì¤‘ì— ìµí˜€ë¼ 하지만 ìœ„ì˜ ì•„ì´ë””어는 주ì„문으로 ì¸í•´ ì´í•´ê°€ 갈것ì´ë‹¤. ê°€ìƒ ë³µì‚¬ ìƒì„±ìžë¡œ ì¸í•´ì„œ, ê°ì²´ 복사가 ìƒë‹¹ížˆ 간편해 진다. === Making Non-Member Functions Act Virtual : 비멤버 함수를 ê°€ìƒ í•¨ìˆ˜ì²˜ëŸ¼ ë™ìž‘하게 하기 === ìƒì„±ìžëŠ” ì‹¤ì œë¡œ ê°€ìƒ í•¨ìˆ˜ê°€ ë 수 없다. 마찬가지로 비멤버 함수들 ì—시 마찬가지 ì´ë¦¬ë¼, 하지만 그러한 ê¸°ëŠ¥ì´ í•„ìš”í• ë–„ê°€ 있다. 바로 앞 ì˜ˆì œì—서 NLComponent와 그것ì—서 ìœ ë„ëœ í´ëž˜ìŠ¤ë“¤ì„ ì˜ˆë¥¼ 들어 ë³´ìžë©´ ì´ë“¤ì„ operator<<으로 ì¶œë ¥ì„ í•„ìš”ë¡œ í• ë•Œ ì´ë¦¬ë¼. ë 다ìŒê³¼ ê°™ì´ í•˜ë©´ ë¬¸ì œ 없다. {{{~cpp class NLComponent{ public: // operator<<ì˜ ê°€ìƒ í•¨ìˆ˜ virtual ostream& operator<<(ostream& str) const = 0; ... }; class TextBlock:public NLComponent{ public: virtual ostream& operator<<(ostream& str) const; }; class Graphic:public NLComponent{ public: virtual ostream& operator<<(ostream& str) const; }; TextBlock t; Graphic g; ... t << cout; g << cout; }}} 하지만 ì¶œë ¥í•´ì•¼í• ìŠ¤íŠ¸ë¦¼ ê°ì²´ê°€ righ-hand ê°ì²´ë¼ëŠ”ê²ƒì´ ì‚¬ìš©ìž ìž…ìž¥ì—서 불편하게 ë§Œë“ ë‹¤. 우리가 보통 사용하는 것처럼 스트림 ê°ì²´ì— ì¶œë ¥í• ê°ì²´ë¥¼ 넣는 다는 ê°œë…ì´ ì ìš©ë˜ì§€ 않는 것ì´ë¦¬ë¼. 하지만, ì „ì— í•¨ìˆ˜ë‚˜ friend함수를 ì´ìš©í•´ì„œ 구현한다면 ë”ì´ìƒ ê°€ìƒí•¨ìˆ˜ë¡œ êµ¬í˜„í• ìˆ˜ê°€ 없게 ëœë‹¤. ì—¬ê¸°ì„œì˜ ë°©ë²•ì´ ë¹„ë©¤ë²„ 함수를 ì´ìš©í•˜ëŠ” 것ì´ë‹¤. 다ìŒê³¼ ê°™ì´ ê°€ìƒ í•¨ìˆ˜ë¡œ ì¶œë ¥ì„ êµ¬í˜„í•˜ê³ , ì „ì—ì´ë“ , namespace로 묶여 ìžˆë“ , 비 멤버 함수로 스트림 ê°ì²´ì™€ì˜ ì¶œë ¥ì˜ ê³ ë¦¬ë¥¼ 연결시켜서, 비멤버 함수가 ê°€ìƒí•¨ìˆ˜ì²˜ëŸ¼ ëŒì•„ê°€ë„ë¡ êµ¬í˜„í•œë‹¤. {{{~cpp class NLComponent { public: virtual ostream& print(ostream& s) const = 0; ... }; class TextBlock: public NLComponent { public: virtual ostream& print(ostream& s) const; ... }; class Graphic: public NLComponent { public: virtual ostream& print(ostream& s) const; ... }; inline ostream& operator<<(ostream& s, const NLComponent& c) { return c.print(s); } }}} ê°€ìƒ í•¨ìˆ˜ì˜ ì—í• ì„ ë¹„ë©¤ë²„(non-member)함수로 구현한 사례ì´ë‹¤. 비 멤버 함수ì´ì§€ë§Œ inlineì„ í†µí•´ì„œ ê°€ìƒ í•¨ìˆ˜ì™€ì˜ ì§ì ‘ ì—°ê²° 통로를 만들었다. 비멤버 í•¨ìˆ˜ì˜ ê°€ìƒ(virtual)ê´€ë ¨ ì‚¬ìš©ì— ëŒ€í•˜ì—¬ ë” ë‹¤ë£¨ìžë©´ ë‚´ìš©ì´ ë³µìž¡í•´ 진다. ì´ì— 대한 ê´€ë ¨ 사í•ì€ Item 31ì„ ì°¸ê³ í•˜ë¼ == Item 26: Limiting the number of objects of a class == * Item 26: ê°ì²´ ìˆ«ìž ì œí•œí•˜ê¸°. ìž ì§€ê¸ˆê¹Œì§€ ê°ì²´ì— 대한 ì´ì•¼ê¸°ë¡œ ë‹¹ì‹ ì€ ë¯¸ì¹ ì§€ê²½ì— ë¹ ì¡Œì„ êº¼ë‹¤. 게다가 ì´ê²ƒì€ ë‹¹ì‹ ì„ í˜¼ëž€ì— ë¹ íŠ¸ë¦´ 수준까지 ì™”ì„ ê²ƒì´ë‹¤. (첫줄만 ì§ë…ì§í•´.) 예를들어서 ë‹¹ì‹ ì˜ ì‹œìŠ¤í…œì— í”„ë¦°í„°ê°€ 하나 ë°–ì— ì—†ì„때 프린터를 대변하는 ê°ì²´ì˜ 숫ìžë¥¼ 하나로 ì œí•œí•´ì•¼ 하지 않ì„까? 아니면 í•˜ë‚˜ì˜ íŒŒì¼ì— 대하여 16ê°œì˜ íŒŒì¼ ì ‘ê·¼ìžë§Œ í—ˆìš©í• ë•Œ 따위 ê°™ì€ê±° ë§ì´ë‹¤. 여기서는 ê·¸ í•´ë²•ì— ê´€í•´ì„œ ìƒê°í•´ 본다. * 작성ìžì£¼ : ì´ ë¶€ë¶„ì€ Singleton 패턴과 연관해서 ìƒê°í•˜ë©´ ìž¬ë¯¸ìžˆì„ ê²ƒ 같다. Singleton íŒ¨í„´ì´ DPì— ë…¼ì˜ë 때 ì´ê²ƒì„ ê°ì•ˆ ì•ˆí•œê²ƒì´ ì•„ì‰½ë‹¤. 1995ë…„ì— ë°œê°„ì´ë¼ STLë„ ì œëŒ€ë¡œ 다루지 ì•Šì•˜ê³ , C++ì˜ ê¸°ë³¸ì ì¸ ë¬¸ë²•ì„ ì´ìš©í•´ 구현하였다. MEC++는 Techniques ë¶€ë¶„ì€ C++ì˜ ë¬¸ë²•ê³¼ ê°œë…ì„ ê·¹í•œìœ¼ë¡œ 쓴다는 ëŠë‚Œì´ ë“ ë‹¤. === Allowing Zero or One Objects : 0 í˜¹ì€ í•˜ë‚˜ì˜ ê°ì²´ ë§Œì„ í—ˆìš©í•˜ëŠ” 방법 === ê°ì²´ë“¤ì´ ìƒì„±ë 때 ê¼ í•˜ëŠ” ì¼ì´ 있다. 바로 ìƒì„±ìžë¥¼ 부르는 ì¼ì´ë‹¤. 하지만 ì´ê±¸ 막ì„수 있는 ë°©ë²•ì´ ìžˆì„까? ê°€ìƒ ì¥ìš´ ë°©ë²•ì€ ìƒì„±ìžë¥¼ private(사ì—)ì¸ìžë¡œ 묶어 버리는 것ì´ë‹¤. 다ìŒì„ ë³´ìž {{{~cpp class CantBeInstantiated { private: CantBeInstantiated(); CantBeInstantiated(const CantBeInstantiated&); ... }; }}} ìž ì´ë ‡ê²Œ 하면 ìƒì„±ìžê°€ privateìƒíƒœë¼ì„œ 외부ì—서 ìƒì„±ìžë¥¼ 부를수 없기 때문ì—, 외부ì—서 ê°ì²´ë¥¼ ìƒì„±í• 수 없다. ì´ëŸ° ì•„ì´ë””어를 바탕으로 ìƒì„± ìžì²´ì— ì œí•œì„ ê°€í•´ 버리는 것ì´ë‹¤. 그럼 처ìŒì— ë§í•œ í”„ë¦°í„°ì˜ ì˜ˆì œë¥¼ ìœ„ì˜ ì•„ì´ë””어를 구현해 ë³´ìž. {{{~cpp class PrintJob; // 미리 ì„ ì–¸, 프린터 ìž‘ì—…ì— ì“°ì´ëŠ” ê°ì²´. ì°¸ê³ Effective C++ Item 34 class Printer { public: void submitJob(const PrintJob& job); void reset(); void performSelfTest(); ... friend Printer& thePrinter(); // ì´ friend 함수가 ìœ ì¼í•œ ê°ì²´ 하나를 ìœ ì§€ ì‹œí‚¤ê³ // ê°ì²´ì˜ ì‚¬ìš©ê¶Œí•œì„ ë¶€ì—¬ 하는 ì—í• ì„ í•œë‹¤. private: Printer(); Printer(const Printer& rhs); ... }; Printer& thePrinter() { static Printer p; // 단ì¼ì˜ Printer ê°ì²´(the single printer object) return p; } }}} 해당 ë””ìžì¸ì€ ì„¸ê°€ì§€ì˜ ì¤‘ì 으로 ì´í•´ 하면 ëœë‹¤. '''첫번째''' Printerí´ëž˜ìŠ¤ì˜ ìƒì„±ìžë¥¼ private(사ì—)ì¸ìžë¡œ ì„¤ì •í•œë‹¤. ì´ëŠ” ê°ì²´ ìƒì„±ì„ ì œí•œí•œë‹¤. '''ë‘번째''' ì „ì— í•¨ìˆ˜ì¸ thePrinter를 Printerí´ëž˜ìŠ¤ì˜ friend로 ì„ ì–¸í•œë‹¤. 그래서 ì´ thePrinterê°€ 첫번째ì—ì„œì˜ ì œí•œì— ìƒê´€ì—†ì´ 관여 가능하ë„ë¡ ë§Œë“ ë‹¤. '''마지막으로(세번째)''' ì „ì—í•¨ìˆ˜ì¸ thePrinter ë‚´ë¶€ì— ì •ì (static) Printer ê°ì²´ë¥¼ ë§Œë“ ë‹¤. ì´ëŠ” ì˜¤ì§ í•˜ë‚˜ë§Œì˜ ê°ì²´ë¥¼ thePrinterë‚´ë¶€ì— ìœ ì§€ì‹œí‚¨ë‹¤. í´ë¼ì´ì–¸íЏ 입장ì—서는 ì´ë ‡ê²Œ 사용하면 ëœë‹¤. {{{~cpp class PrintJob { public: PrintJob(const string& whatToPrint); ... }; string buffer; ... // 버í¼ì˜ ì¸ìžë¥¼ 넣는 코드 thePrinter().reset(); thePrinter().submitJob(buffer); // ìƒìˆ˜ 참조(const reference)ë¼ì„œ, // 임시 ê°ì²´ê°€ ìƒì„±ë˜ì–´ 실행ëœë‹¤. }}} 하지만 ì´ë ‡ê²Œ 구현시ì—는 thePrinterê°€ "'''ì „ì— ê³µê°„ì„ ì‚¬ìš©í•´ì•¼ 한다.'''" 것으로 코드를 약하게 ë§Œë“ ë‹¤. 알다 시피, ì „ì— ê³µê°„ì˜ ì‚¬ìš©ì€ ë˜ë„ë¡ì´ë©´ 피해야 하는 방법ì´ë©°, thePrinter를 Printerí´ëž˜ìФ ë‚´ë¶€ì— ìˆ¨ê¸°ê¸°ë¥¼ 추천하다. thePrinter를 Printerí´ëž˜ìФ ë‚´ë¶€ 메소드로 넣어 ë²„ë¦¬ê³ , friend를 ì‚ì œí•´ ë³´ìž. {{{~cpp class Printer { public: static Printer& thePrinter(); // 외부ì—서 호출 가능하ë„ë¡ static으로 ì„ ì–¸í•˜ê³ ... private: Printer(); Printer(const Printer& rhs); ... }; Printer& Printer::thePrinter() // friendë§Œ 없앴지 마찬가지 ë°©ë²•ì„ ì œì‹œí•œë‹¤. { static Printer p; return p; } }}} ê·¸ë¦¬ê³ ì´ë¥¼ 사용하는 í´ë¼ì´ì–¸íЏ 측ì—서는 다ìŒê³¼ ê°™ì€ ë°©ë²•ìœ¼ë¡œ 사용하면 ë˜ê² 다. {{{~cpp Printer::thePrinter().reset(); Printer::thePrinter().submitJob(buffer); }}} ì „ì— ê³µê°„ ì‚¬ìš©ì— ëŒ€í•œ ë¬¸ì œì˜ í•´ê²°ì±…ì˜ ë˜ë‹¤ë¥¸ ì ‘ê·¼ 방법ì´ë¼ê³ 한다면, name space를 사용하는 것ì´ë‹¤. 다ìŒê³¼ ê°™ì´ ë‹¨ìˆœížˆ PrintingStuff name space로 묶어 버린다. {{{~cpp namespace PrintingStuff { // namespaceì˜ ì‹œìž‘ class Printer { // namespace로서 ì´ í´ëž˜ìŠ¤ëŠ” public: // PrintingStuff namespace ì•ˆì— ì¡´ìž¬í•˜ëŠ” 것ì´ë‹¤. void submitJob(const PrintJob& job); void reset(); void performSelfTest(); ... friend Printer& thePrinter(); private: Printer(); Printer(const Printer& rhs); ... }; Printer& thePrinter() // ì´ friend 함수 ì—시 ì „ì—ì´ ì•„ë‹Œ { // PrintingStuff namespaceì•ˆì— ì¡´ìž¬ 하는 것ì´ë‹¤. static Printer p; return p; } } // namespaceì˜ ë }}} ê·¸ë¦¬ê³ thePrinter를 í˜¸ì¶œí•˜ë ¤ë©´ ì´ì œëŠ” ì´ë ‡ê²Œ 해야 한다. {{{~cpp PrintingStuff::thePrinter().reset(); PrintingStuff::thePrinter().submitJob(buffer); }}} ë¬¼ë¡ using 키워드를 사용해서 해당 ì´ë¦„ì„ ìžìœ ë¡ê²Œ ì‚¬ìš©í• ìˆ˜ 잇다. {{{~cpp using PrintingStuff::thePrinter; // thePrinter를 í˜„ìž¬ì˜ namespace안ì—서 // ìžìœ ë¡œì´ ì“¸ìˆ˜ 있게 ë§Œë“ ë‹¤. thePrinter().reset(); // ê·¸ë¦¬ê³ ì‚¬ìš©í•˜ëŠ” ê³¼ì • thePrinter().submitJob(buffer); }}} thePrinter 를 ì ìš©í• ë•Œ ë‘가지 ìƒê°í•´ì•¼í• 미묘한 ë¬¸ì œì ì´ ìžˆë‹¤. '''첫번째'''로 만들어지는 ê°ì²´ì˜ 위치ì´ë‹¤. ìœ„ì˜ ì œì‹œëœ ë‘ê°€ì§€ì˜ ë°©ë²•ì—서, Printer ì •ì (staitc) ê°ì²´ê°€ 하나는 friend로 í´ëž˜ìŠ¤ì˜ ì œì–´ê¶Œì„ íšë“한 함수 ë‚´ë¶€ì— ìžˆê³ , ë˜ í•˜ë‚˜ëŠ” í´ëž˜ìФ 멤버 메소드 ë‚´ë¶€ì— ìžˆë‹¤. í•¨ìˆ˜ì— ìžˆëŠ” 경우ì—는 ì •ì (static) ê°ì²´ëŠ” í•ìƒ ë§Œë“¤ì–´ì ¸ 있다. ì´ ì˜ë¯¸ëŠ” 해당 ì½”ë“œì˜ í”„ë¡œê·¸ëž¨ì´ ì‹œìž‘ë 때 부터 아예 ê°ì²´ê°€ 만들어 진다는 ì˜ë¯¸ì´ë‹¤. 즉, í•œë²ˆë„ ê·¸ ê°ì²´ë¥¼ 사용하지 않아ë„, ê°ì²´ëŠ” ì´ë¯¸ ë§Œë“¤ì–´ì ¸ ë¹„ìš©ì„ ì§€ì¶œí•˜ê²Œ 한다. 반면ì—, 함수 멤버 메소드 ë‚´ë¶€ì— ì •ì (static)ê°ì²´ë¥¼ 만들 후ìžì˜ 경우ì—는 ê°ì²´ë¥¼ 만드는 ì—í• ì„ í•˜ëŠ” ë©”ì†Œë“œì¸ Printer::thePrinter ê°€ ì œì¼ ì²˜ìŒ í˜¸ì¶œë 때 ê°ì²´ê°€ ìƒì„±ëœë‹¤. ì´ê²ƒì€ C++ì—서 "사용하지 않는 ê°ì²´ì— 대한 ë¹„ìš©ì€ ì§€ë¶ˆí•˜ì§€ 않는다."ì˜ ì„¤ê³„ 다소 복잡한 ì´ë…ì— ê·¼ê°„ì„ ë‘” ê°œë…ì´ë‹¤. ê·¸ë¦¬ê³ ì´ëŸ¬í•œ 복잡한 ê°œë…ì€ ë‹¹ì‹ ì„ í•´ê¹”ë¦¬ê²Œ ë§Œë“ ë‹¤. ( DeleteMe Translation unitì˜ í•´ì„ì´ ë¶ˆë¶„ëª…í•˜ë‹¤.) ë˜ ì´ ë‘˜ì˜ ë‹¤ë¥¸ 취약ì ì€ ì´ˆê¸°í™” ë˜ëŠ” 시간ì´ë‹¤. 우리는 í•¨ìˆ˜ì˜ ê²½ìš°ì— ì´ˆê¸°í™” ì‹œê°„ì„ ì •í™•ížˆ 알수 있다. 아예 ì²˜ìŒ ì´ë‹ˆê¹Œ 하지만 멤버 메소드로 구현시ì—는 모호하다. C++는 확실히 특별하게 í•´ì„해야 í• ë¶€ë¶„(ë‹¨ì¼ ê°ì²´ì—서 소스 코드 몸체 부분 따위)ì€ ì •ì ì¸ìžë“¤ì— 대한 초기화 순서가 보장 ëœë‹¤. 하지만 서로 다른 í•´ì„ ë¶€ë¶„(translation unit)ì— ìžˆëŠ” ì •ì ê°ì²´ë“¤ì˜ 초기화 ìˆœì„œì— ëŒ€í•´ì„œëŠ” ë§í• 수가 없다. ì´ê²ƒì€ 머리를 아프게만 í• ë¿ì´ë‹¤. '''ë‘번째'''로 미묘한 ë¬¸ì œë¼ë©´, inlineê³¼ ì •ì ê°ì²´ì˜ 관게ì´ë‹¤. 다ìŒê³¼ ê°™ì€ ë¹„ë©¤ë²„ ë²„ì „ì˜ thePrinter를 ë³´ë©´ {{{~cpp Printer& thePrinter() { static Printer p; return p; } }}} 다ìŒê³¼ ê°™ì€ ì½”ë“œì˜ í•¨ìˆ˜ëŠ” 매우 짧다. ì´ëŸ° ì§§ì€ í•¨ìˆ˜ëŠ” 함수보다 inline 시켜서 ì†ë„를 높ì´ëŠ” ê²ƒì´ ë” íš¨ê³¼ì ì´ë‹¤. 하지만 그럴수가 없다. 왜 그런가 하면, inlineì˜ ì˜ë¯¸ëŠ” ì •í™•ížˆ 해당 함수가 ì“°ì´ëŠ” 코드를 현재 í•¨ìˆ˜ì˜ ëª¸ì²´ë¡œ êµì²´í•´ 버리는 ì—í• ì´ë‹¤. 그런게 ì´ë ‡ê²Œ í• ê²½ìš°, 위와 ê°™ì€ í•¨ìˆ˜ëŠ” staticê°ì²´ì˜ 처리ì—서 ì˜ë¬¸ì´ ìƒê¸´ë‹¤. 해당 함수가 í˜¸ì¶œëœ ê³³ì„ ìœ„ì™€ ê°™ì€ í•¨ìˆ˜ 몸체로 êµì²´í•˜ë©´, ê° êµì²´ ë¶€ë¶„ì€ ì „ë¶€ ë…립ì ì¸ static ì¸ìžë¥¼ 부여 받는 ì…ˆì´ ë˜ì–´ 버린다. 그래서 ì •ì ì¸ìžë¥¼ ì“´ 함수는 inlineì„ ì‹œí‚¤ì§€ 못하며, ì´ëŸ° ì •ì ì¸ìžì˜ ì‚¬ìš©ì— ë”°ë¼ ì¼ì–´ë‚˜ëŠ” ì˜ë¬¸ì„ internal linkage를 가진 ë¬¸ì œ ë¼ê³ 한다. DeleteMe) ë‚ ë¦¼ 요약 ìˆ˜ì • í•„ìš” ìž, 똑똑한 사람 ì´ë¼ë©´ 당연히, ì§€ê¸ˆê¹Œì§€ì˜ ì½”ë“œì—서 ì˜ë¬¸ì ê³¼ ë¬¸ë²•ì— ëŒ€í•œ ì˜ì•„í•¨ì„ ì¼ìœ¼ 킬수 있다. thePrinter는 둘다 ë‚´ë¶€ì— ìžˆëŠ” static ê°ì²´ì˜ 참조를 반환하는ë°, ì´ "static ê°ì²´ëŠ” 해당 함수,ë©”ì†Œë“œì˜ ì˜ì—(scop) ë‚´ë¶€ì—서 쓰여야지 외부ì—서 ì“°ì´ë©´ 안ë˜ì§€ 않는가?" ë¼ëŠ” ì˜ë¬¸ì´ 그것ì´ë‹¤. 즉, í´ë¼ì´ì–¸íЏ 입장ì—서 ì´ë“¤ ê°ì²´ëŠ” ìˆ¨ê²¨ì ¸(hidden)있는 존재ì´ê³ , ì´ê²ƒì„ 사용하는 ê²ƒì€ ìž˜ëª»ëœ ë°©ë²•ì´ë‹¤. ë¼ê³ ë§í• 수 ìžˆê² ëŠ”ë°, 그래서 아마 ë‹¹ì‹ ì€ ë‹¤ìŒê³¼ ê°™ì´ ê°ì²´ì˜ 숫ìžë¥¼ ì„¸ê³ , ì œí•œëœ ê°ì²´ì˜ 수보다 ë” ë§Žì€ ê°ì²´ë¥¼ 사용시 예외를 ë°œìƒì‹œì¼œì„œ ë¬¸ì œë¥¼ 해결하는 ê²ƒì´ ë” ì¢‹ì€ ë°©ë²•ì´ë¼ ë§í• 것ì´ë‹¤. {{{~cpp class Printer { public: class TooManyObjects{}; // 너무 ë§Žì€ ê°ì²´ë¥¼ 요구하면 // ì´ ì˜ˆì™¸ë¥¼ ë°œìƒ ì‹œí‚¨ë‹¤. Printer(); ~Printer(); ... private: static size_t numObjects; Printer(const Printer& rhs); // 프린터는 1개만 허용하며, ì‚¬ë³¸ë„ í—ˆìš© 하지 // 않는다. Item E27 }; }}} ì´ëŸ° ì•„ì´ë””어는 numObject를 사용해서 Printerê°ì²´ì˜ 수를 ì œí•œ 시켜 버리는 것ì´ë‹¤. 위ì—ë„ ì–¸ê¸‰í•˜ì˜€ë“¯ì´ ê°ì²´ì˜ 수가 1개를 초과하면, TooManyObject 예외를 ë°œìƒì‹œí‚¨ë‹¤. {{{~cpp size_t Printer::numObjects = 0; Printer::Printer() { if (numObjects >= 1) { throw TooManyObjects(); } ë³´í†µì˜ ìƒì„± ê³¼ì •ì„ ìˆ˜í–‰í•œë‹¤. ++numObjects; } Printer::~Printer() { ë³´í†µì˜ íŒŒê´´ ê³¼ì •ì„ ìˆ˜í–‰í•œë‹¤. --numObjects; } }}} ì´ëŸ¬í•œ ì ‘ê·¼ ë°©ë²•ì€ ë§¤ë ¥ì ì´ë©°, 단지 ê°ì²´ì˜ 수를 1ê°œì— êµí•œ 하지 ì•Šê³ , íŠ¹ì • 숫ìžë¡œ ì¡°ì •í• ìˆ˜ 있는 ì´ì ì´ ìžˆë‹¤. 사용하기는 ê·€ì°®ì•„ë„ ë§ì´ë‹¤. 하지만 ì´ ê²ƒì˜ ë¬¸ì œë¥¼ 바로 다루어 ì£¼ê² ë‹¤. === Context for Object Construction : ê°ì²´ì˜ ìƒì„±ì„ 위한 구문(관계, 문맥, ìƒí™©) === 방금 위ì—서 ì˜ˆì œë¡œ ì œì‹œí•œ 방법 ì—시 ë¬¸ì œëŠ” ë‚´í¬í•˜ê³ 있다. ê°€ë ¹ 특별한 í”„ë¦°í„°ì¸ ì»¬ëŸ¬ í”„ë¦°í„°ì— ëŒ€í•œ í´ëž˜ìŠ¤ë¥¼ ìž‘ì„±í•œë‹¤ê³ ê°€ì •í•´ 본다. {{{~cpp class ColorPrinter: public Printer { ... }; }}} ê·¸ë¦¬ê³ ë‹¤ìŒê³¼ ê°™ì´ ì‚¬ìš©í•œë‹¤ê³ ê°€ì •í•´ ë³´ìž. {{{~cpp Printer p; ColorPrinter cp; }}} 첫번째 ê°ì²´ p는 순조로히 ìƒì„±ëœë‹¤. 하지만 엄연히 다른 프린터를 대ìƒìœ¼ë¡œ í•˜ê³ ìžˆëŠ” cp는 ìƒì„±ë˜ì§€ ì•Šê³ , TooManyObjects 예외를 ë°œìƒ ì‹œí‚¨ë‹¤. 왜 그러는지 모ë‘들 ì˜ˆìƒ í• ê²ƒì´ë‹¤. ë”불어 비슷 ë˜ ë‹¤ë¥¸ 경우를 ìƒê° í•´ 본다면. {{{~cpp class CPFMachine { // copy, print, fax ì „ë¶€ í• ìˆ˜ 있는 기계 private: Printer p; // 프리터 ê¸°ëŠ¥ì˜ ì¸ìž. FaxMachine f; // 팩스 ê¸°ëŠ¥ì˜ ì¸ìž CopyMachine c; // 복사기 ê¸°ëŠ¥ì˜ ì¸ìž. ... }; CPFMachine m1; // 여기 까지는 잘ëœë‹¤. CPFMachine m2; // TooManyObjects 예외를 ë°œìƒ ì‹œí‚¨ë‹¤. }}} Printer ê°ì²´ê°€ ì¡´ìž¬í• ìˆ˜ 있는 ì„¸ê°€ì§€ì˜ ìƒí™©ì—서 ì´ëŸ° ë¬¸ì œëŠ” ë°œìƒ ë 수 있다. : 그냥 그들 ìžì²´ë¥¼ ì„ ì–¸í•´ì„œ 사용하기. 다른 í´ëž˜ìŠ¤ë¡œ ìœ ë„ë 때. ì¢€ë” í° í´ëž˜ìФì—서 해당 í´ëž˜ìŠ¤ë¥¼ ì¸ìžë¡œ í¬ìš©í• 때 ì´ë‹¤. 하지만 숫ìžë¡œ ì œì–´í•˜ê³ , 예외를 ë°œìƒì‹œí‚¤ëŠ” ë°©ë²•ì´ ì•„ë‹Œ ìƒì„±ìžê°€ 사ì—(private)ì¸ìžë¡œ 들어간 경우ì—는 해당 í´ëž˜ìФì—서 ìœ ë„ëœ í´ëž˜ìŠ¤ë“¤ë„ ìƒì„±í•˜ì§€ 못하며, 다른 í´ëž˜ìŠ¤ì˜ ì¸ìžë¡œë„ 들어갈수가 없어서, ì´ëŸ° ë¬¸ì œë“¤ì´ ë´‰ì‡„ëœë‹¤. ìž, ì´ëŸ°ê±¸ë¡œ 한가지 재미있는 ê²ƒì„ ë§Œë“¤ìˆ˜ 있다. 만약 ë‹¹ì‹ ì´ C++ìƒì—서 ë”ì´ìƒ ìƒì† ë˜ì§€ 않는 í´ëž˜ìŠ¤ë¥¼ ë§Œë“¤ê³ ì‹¶ì„때 어떻게 해야 í• ê¹Œ?(주:ì°¸ê³ ë¡œ Java나 C#ì˜ ê²½ìš° 언어 설계 때부터 아예 해당 ê¸°ëŠ¥ì„ ìˆ˜í–‰ì„ ìœ„í•œ 키워드를 ì œê³µí•œë‹¤. 하지만 C++는 ì œê³µí•˜ì§€ 않는다. ì´ëŸ° ë°©ë²•ì„ ì„¤ê³„ìžê°€ ìƒê°í•œê±´ì§€, 차후 C++ì˜ ê°œë°œìžë“¤ì´ ìƒê°í•œê±´ì§€ 놀ë¼ìš¸ ë¿ì´ë‹¤. 바로 ì´ì „ì— ë‚˜ì˜¨ ê°€ìƒ ë³µì‚¬ ìƒì„±ìžì˜ ì•„ì´ë””어와 ë¹„ìŠ·í•˜ë‹¤ê³ í•´ì•¼ í• ê¹Œ) {{{~cpp class FSA { public: // 가짜 ìƒì„±ìžë“¤ static FSA * makeFSA(); // ìƒì„±ìž static FSA * makeFSA(const FSA& rhs); // 복사 ìƒì„±ìž ... private: FSA(); FSA(const FSA& rhs); ... }; FSA * FSA::makeFSA() { return new FSA(); } FSA * FSA::makeFSA(const FSA& rhs) { return new FSA(rhs); } }}} ì´ë ‡ê²Œ ìƒì„±ìžê°€ 사ì—(private)ì¸ìžë¡œ 들어가 버리면, 해당 í´ëž˜ìФì—서 ìœ ë„ë˜ëŠ” í´ëž˜ìŠ¤ë¥¼ 만들기란 불가능 하다. 하지만 ì´ ì½”ë“œì˜ ë¬¸ì œì ì€ makeFSA를 ì´ìš©í•´ ìƒì„±í•˜ë©´ í•ìƒ delete를 해주어야 한다는 ì ì´ë‹¤. ì´ì „ 예외를 다루는 부분ì—ì„œë„ ì–¸ê¸‰í–ˆì§€ë§Œ, ì´ëŠ” ìžì›ì´ 세나갈 여지를 남기는 것ì´ë‹¤. ì´ë¥¼ 위한 STLì˜ auto_ptrë„ ì°¸ê³ í•˜ìž.(Item 9 ì°¸ê³ ) {{{~cpp // 기본 ìƒì„±ìž auto_ptr<FSA> pfsa1(FSA::makeFSA()); // 복사 ìƒì„±ìž auto_ptr<FSA> pfsa2(FSA::makeFSA(*pfsa1)); ... // ìž ì´ì œ 해당 ì˜ì—ì„ ë²—ì–´ë‚˜ë©´ ê°ì²´ëŠ” ìžë™ 파괴ëœë‹¤. }}} === Allowing Objects to Come and Go : ê°ì²´ê°€ ì˜¤ê³ ê°ì„ 허용하기? === ì´ì œ, 단ì¼í•œ ê°ì²´ 만들기 ë°©ë²•ì— ê´€í•œ ë””ìžì¸ ë°©ë²•ì€ ì•Œìˆ˜ ìžˆì„ ê²ƒì´ë‹¤. ê·¸ë¦¬ê³ , ê°ì²´ë¥¼ 숫ìžë¡œ ì œì–´í•˜ëŠ” ê²ƒì€ ì„¸ê°€ì§€ì˜ ìƒì„± ìƒí™©ì— ì˜í•´ì„œ í잡한 ìƒí™©ì„ 만들어 나간다는 ê²ƒì„ ì•Œê²ƒì´ë‹¤. ì´ê²ƒì„ 위해서 ìƒì„±ìžì˜ 사ì—(private)ì—시 설명했다. 캡ìŠí™”ëœ thePrinter함수는 Printerë¼ëŠ” 단ì¼í•œ ê°ì²´ë¥¼ ì œí•œí•˜ê³ , ê·¸ê²ƒì„ ì‚¬ìš©í• ìˆ˜ 있게 한다. thePrinterê°€ 대안ì¼ê¹Œ. 하지만 ê²°êµ thePrinter는 C++ì˜ ì¼ë°˜ì ì¸ ë°©ë²•ì¸ ì´ëŸ¬í•œ ë””ìžì¸ì˜ 코드를 불가능하게 한다. {{{~cpp create Printer object p1; use p1; destroy p1; create Printer object p2; use p2; destroy p2; }}} ì´ëŸ° ë””ìžì¸ì€ ë‹¨ì¼ Printerê°ì²´ì— 관해서 행하여 질수는 없다. 하지만 서로 다른 í”„ë¡œê·¸ëž¨ì˜ ì„œë¡œ 다른 부분ì—서 Printerê°ì²´ëŠ” ì´ë ‡ê²Œ 사용ë˜ì–´ 질수 있다. ì´ê²ƒ ì—시 허용하지 못하게하는 것까지는 í•„ìš” ì—†ì„것 ê°™ì€ë°, 아무튼 ì˜¤ì§ í•˜ë‚˜ì˜ í”„ë¦°í„° ê°ì²´ë§Œ ìœ ì§€ 시킨다는 것ì—는 벗어나지는 않는 거다. 하지만 ì—¬ê¸°ì— ìš°ë¦¬ê°€ í•´ì™”ë˜ object-counting방법과, ì¼ì°ì´ ì“´ 가짜 ìƒì„±ìž(pseudo-constructor)를 혼합해서 ê¸°ëŠ¥ì„ êµ¬í˜„í•´ 본다. {{{~cpp class Printer { public: class TooManyObjects{}; static Printer * makePrinter(); // 가짜 ìƒì„±ìž(pseudo-constructors) ~Printer(); void submitJob(const PrintJob& job); void reset(); void performSelfTest(); ... private: static size_t numObjects; Printer(); Printer(const Printer& rhs); // 다ìŒê³¼ ê°™ì€ í•¨ìˆ˜ëŠ” 복사 방지를 위해 // 구현 ë˜ì§€ 않ë„ë¡ í•œë‹¤. ( E27ì°¸ê³ ) DeleteMe)ê²€ì¦í•„ìš” }; // í´ëž˜ìŠ¤ì˜ staticì˜ ì •ì˜(definition,비슷하게 초기값 구현)ì€ ë°˜ë“œì‹œ 해야 한다. size_t Printer::numObjects = 0; Printer::Printer() { if (numObjects >= 1) { throw TooManyObjects(); } ë³´í†µì˜ ìƒì„± ê³¼ì •ì„ ìˆ˜í–‰í•œë‹¤. ++numObjects; } Printer * Printer::makePrinter() { return new Printer; } }}} 다ìŒê³¼ ê°™ì€ ì½”ë“œëŠ” ì´ë ‡ê²Œ 사용해야만 한다. {{{~cpp Printer p1; // ìƒì„±ìžê°€ 사ì—(private)ì´ë¯€ë¡œ // ì—러를 ë°œìƒ ì‹œí‚¨ë‹¤. Printer *p2 = Printer::makePrinter(); // 올바르다. ì´ê²ƒì´ 기본 ìƒì„±ìžì´ë‹¤. Printer p3 = *p2; // ì—러! 복사 ìƒì„±ìž ì—시 사ì—으로 ì„¤ì •ë˜ì–´ 있다. p2->performSelfTest(); // ê°ì²´ ì‚¬ìš©í•˜ë“¯ì´ í•œë‹¤. p2->reset(); // 마찬가지 ... delete p2; // ìžì›ì´ 세나가지 않기위해 ì œê±° 해준다. // auto_ptrì„ ì“°ë©´ ì´ë„ í•„ìš” 없어 진다. }}} ì´ëŸ¬í•œ ê¸°ìˆ ì€ ì–´ë– í•œ 숫ìžì˜ ê°ì²´ì˜ ì œí•œì—ë„ ì¨ë¨¹ì„수 있는ë°, 다ìŒê³¼ ê°™ì€ ì½”ë”©ìœ¼ë¡œ 가능하다 다ìŒì€ ê°ì²´ë¥¼ 10개로 ì œí•œ 시켜버리는 것ì´ë‹¤. {{{~cpp class Printer { public: class TooManyObjects{}; static Printer * makePrinter(); // // 가짜 ìƒì„±ìž(pseudo-constructors) static Printer * makePrinter(const Printer& rhs); ... private: static size_t numObjects; static const size_t maxObjects = 10; // 아래 ì°¸ê³ Printer(); Printer(const Printer& rhs); }; // í´ëž˜ìŠ¤ì˜ staticì˜ ì •ì˜(definition,비슷하게 초기값 구현)ì€ ë°˜ë“œì‹œ 해야 한다. size_t Printer::numObjects = 0; const size_t Printer::maxObjects; Printer::Printer() { if (numObjects >= maxObjects) { throw TooManyObjects(); } ... } Printer::Printer(const Printer& rhs) { if (numObjects >= maxObjects) { throw TooManyObjects(); } ... } Printer * Printer::makePrinter() { return new Printer; } Printer * Printer::makePrinter(const Printer& rhs) { return new Printer(rhs); } }}} ê°™ì€ ì½”ë“œ ì¨ì„œ 내용만 늘린 것 같다. 하지만 ì¡°ê¸ˆë” ì–¸ê¸‰í•´ 본다면. Printer::maxObjects는 í´ëž˜ìФ ë‚´ë¶€ì—서 10으로 초기화 시켰는ë°, ì´ëŠ” 컴파ì¼ëŸ¬ì˜ ì§€ì› ì—¬ë¶€ì— ë”°ë¼ static const ë©¤ë²„ì˜ ê²½ìš° 초기화가 가능한 C++ì˜ ê·œì¹™ì´ë‹¤.(주:ì°¸ê³ ë‚´ìš©ì´ ìžˆì—ˆëŠ”ë° ëª‡ 장ì¸ì§€ 기억 안난다.) ê·¸ë¦¬ê³ maxObjectì— ê´€í•˜ì—¬ 변하지 않는 ê°’ì´ê¸°ì— enumìœ¼ë¡œë„ ì“¸ìˆ˜ 있는ë°, 다ìŒê³¼ 같다. {{{~cpp class Printer { private: enum { maxObjects = 10 }; // ìœ„ì˜ 10 ì œí•œê³¼ ê°™ì€ ì—í• ì„ í•œë‹¤. }; }}} ê·¸ë¦¬ê³ ë§Œì•½ì— í˜„ìž¬ 컴파ì¼ëŸ¬ê°€ í´ëž˜ìФ ë‚´ë¶€ì—서 초기화를 허용하지 않는다면 ì´ë ‡ê²Œ ì¨ì„œ 해결하ë¼. {{{~cpp class Printer { private: static const size_t maxObjects; // 아무런 초기화를 하지 않는다. ... }; // 여기ì—서 다ìŒê³¼ ê°™ì´ ì´ˆê¸°í™”ê°€ 가능하다. const size_t Printer::maxObjects = 10; }}} ìœ„ì˜ ë‘˜ë‹¤ ìƒë‹¨ì˜ 코드와 ë™ì¼í•œ ì—í• ì„ í•˜ëŠ” 것ì´ë‹¤. ì¢‹ì€ ì»´íŒŒì¼ëŸ¬ 쓰시길.. === An Object-Counting Base Class : Object-countingì— ê¸°ë³¸ í´ëž˜ìŠ¤ë¥¼ 만들어 본다. === ì´ì œê¹Œì§€ ê±°ì³ì™”ë˜ ì½”ë“œë“¤ì€ ì–´ëŠ ì •ë„ì˜ í˜•íƒœê°€ 잡혀 있다. ì´ê²ƒì„ ë¼ì´ë¸ŒëŸ¬ë¦¬ë¡œ 만들어 ë†“ê³ , ì¼ì •한 규칙으로 만들수는 ì—†ì„까? (ì°¸ê³ ë¡œ ì´ì™€ 비슷한 ê¸°ìˆ ì´ Item 29 reference-countingì— ë“±ìž¥í•œë‹¤. ì°¸ê³ í•´ë³´ìž.) templateê°€ ì´ë¥¼ 가능하게 해준다. 다ìŒê³¼ ê°™ì€ template를 ì´ìš©í•´ì„œ {{{~cpp template<class BeingCounted> class Counted { public: class TooManyObjects{}; // ë˜ì§ˆ 예외 static int objectCount() { return numObjects; } protected: Counted(); Counted(const Counted& rhs); ~Counted() { --numObjects; } private: static int numObjects; static const size_t maxObjects; void init(); // ìƒì„±ìžì— 코드 ì¤‘ë³µì„ í”¼í•œë‹¤.(어차피 ê°™ì€ ì½”ë“œ 쓰니까.) }; template<class BeingCounted> Counted<BeingCounted>::Counted() { init(); } template<class BeingCounted> Counted<BeingCounted>::Counted(const Counted<BeingCounted>&) { init(); } template<class BeingCounted> void Counted<BeingCounted>::init() { if (numObjects >= maxObjects) throw TooManyObjects(); ++numObjects; } }}} 해당 í´ëž˜ìŠ¤ëŠ” ì˜¤ì§ ê¸°ë³¸ í´ëž˜ìŠ¤ë¡œë§Œ ì“°ì´ë„ë¡ ì„¤ê³„ë˜ì–´ 졌다. 그러므로, ìƒì„±ìžì™€ 파괴ìžê°€ ëª¨ë‘ protected(보호)ì¸ìžë¡œ ì„¤ì •ë˜ì–´ 있다. ê·¸ë¦¬ê³ init로서 object-countingì„ êµ¬í˜„í•œë‹¤. init는 ì„¤ì •ëœ ê°ì²´ì˜ 숫ìžê°€ 넘어가면, TooManyObjects 예외 ê°ì²´ë¥¼ ë°œìƒ ì‹œí‚¨ë‹¤. ì´ì œ ìœ„ì˜ template를 바탕으로 글 ì´ˆë°˜ì— ëˆ„ëˆ„ížˆ 설명한 object-countingì— ê¸°ë°˜í•œ Printerí´ëž˜ìŠ¤ë¥¼ 만들어 본다. {{{~cpp class Printer: private Counted<Printer> { public: static Printer * makePrinter(); // 가짜 ìƒì„±ìž(pseudo-constructors) static Printer * makePrinter(const Printer& rhs); ~Printer(); void submitJob(const PrintJob& job); void reset(); void performSelfTest(); ... using Counted<Printer>::objectCount; // 왜 사용하는지 아래 ì°¸ê³ using Counted<Printer>::TooManyObjects; // 마찬가지~ private: Printer(); Printer(const Printer& rhs); }; }}} ì´í•´ê°€ 안가는 ë¶€ë¶„ì€ ì—시나 usingì´ ì“°ì¸ ê³³ê³¼ private로 ìƒì† ë˜ì—ˆë‹¤ëŠ” ì ì¼ ê²ƒì´ë‹¤. private를 ë¨¼ì € 설명해 한다. ê²°ë¡ ë¶€í„° ì´ì•¼ê¸°í•˜ë©´ 위험성 ì œê±°ì™€, í¬ê¸°ì˜ 최ì 화를 위해서 ì´ë‹¤. 다른 ì´ê°€ Counted<Printer> í¬ì¸í„°ë¥¼ 통해서 Printerê°ì²´ë¥¼ ì œê±°í•˜ë ¤ëŠ” 시ë„를 í• ì§€ë„ ëª¨ë¥¸ë‹¤. ì´ë¥¼ ë°©ì§€í•˜ê³ , privateìƒì†ì€ Item 24ì— ëª…ë°±ížˆ 나와 ìžˆë“¯ì´ Countedë‚´ì˜ ê°€ìƒ í•¨ìˆ˜ê°€ 존재 하ë”ë¼ë„, Printerì—서 해당 ê°€ìƒ í•¨ìˆ˜ì— ëŒ€í•œ vtblì„ ìƒì„±í•˜ì§€ 않으므로서 overhead를 줄ì¸ë‹¤. usingì„ ì¨ì„œ Countedì— ì¡´ìž¬í•˜ëŠ” ì¸ìžë¥¼ 참조 가능하게 만드는 ì´ìœ 는 Printer입장ì—서 다른 ì¸ìžë“¤ì€ ì‹ ê²½ 쓸필요가 없지만, 몇몇 íŠ¹ì •í•œ ì¸ìžì˜ 참조와, ì‚¬ìš©ì„ í•˜ê¸° 위해서 ì´ë‹¤. objectCount는 ì•„ëž˜ì˜ ì£¼ì„ë¬¸ì˜ ì´ìœ ì´ê³ , {{{~cpp class Printer: private Counted<Printer> { public: ... using Counted<Printer>::objectCount; // ì´ í•¨ìˆ˜ë¡œ 현재 ê°ì²´ì˜ 숫ìžë¥¼ 알수 있다. ... // ì´ê±° 함수다. ì°©ê° ë§ˆì‹œê¸¸. Counted í´ëž˜ìŠ¤ë¥¼ 한번 살펴보ë¼. }; }}} 만약, usingì„ ì§€ì›í•˜ì§€ ì•Šì€ ì»´íŒŒì¼ëŸ¬ë¼ë©´ 다ìŒê³¼ ê°™ì€ ì˜›ë‚ ë¬¸ë²•ì„ ì‚¬ìš©í• ìˆ˜ 있다. {{{~cpp class Printer: private Counted<Printer> { public: ... Counted<Printer>::objectCount; // ì´ í•¨ìˆ˜ë¡œ 현재 ê°ì²´ì˜ 숫ìžë¥¼ 알수 있다. ... // ì´ê±° 함수다. ì°©ê° ë§ˆì‹œê¸¸. Counted í´ëž˜ìŠ¤ë¥¼ 한번 살펴보ë¼. }; }}} ì´ëŠ” usingì´ ì¡´ìž¬ 하지 않았ì„때 ì‚¬ìš©ëœ ì˜›ë‚ ë¬¸ë²•ì´ë‹¤. TooManyObjects í´ëž˜ìФ ì—시 ê°™ì€ ì´ìœ 로 usingì„ ì´ìš©í•´ì„œ ì´ë¦„ì„ ì‚¬ìš©í•˜ê²Œ ê¶Œí•œì„ ì—´ì—ˆìœ¼ë©°, ì´ë ‡ê²Œ TooManyObjects를 허용해야 지만 해야지만 í´ë¼ì´ì–¸íŠ¸ë“¤ì´ í•´ë‹¹ 예외를 ìž¡ì„ ìˆ˜ 있다. ìƒì„±ìžì—서 ê°ì•ˆí• ê²ƒì€ ì—†ë‹¤. 그냥 ì¼ë°˜ì ì¸ ìƒì„±ê³¼ì •ì„ ê¸°ìˆ í•´ 주면 무리가 없으며, 필요한 ìž‘ì—…ì€ Counted<Printer>ì˜ ìƒì„±ìžì—서 í•˜ê¸°ë•Œë¬¸ì— Printerì˜ ìƒì„±ìžëŠ” 다ìŒê³ 같다. {{{~cpp Printer::Printer() { ì¼ë°˜ì ì¸ ìƒì„±ìž ê³¼ì • } }}} Printerì˜ ìƒì„±ìžì—서는 ê°ì²´ë¥¼ ì 검하는 코드가 들어가서는 안ëœë‹¤. 왜ëƒí•˜ë©´, Counted<Printer>ì—서 ì ê²€í•˜ê³ ì´ìƒ 있어서 예외를 ë˜ì§„다면, 다ìŒì— 불릴 Printerìƒì„±ìžëŠ” 아예 ë¶ˆë¦¬ì§€ë„ ì•ŠëŠ”ë‹¤. ì•Œê² ë‚˜? ë˜ í•˜ë‚˜ëŠ” object-countingì‹œì— ì´ˆê¸°ê°’ì´ë‹¤. ì´ë ‡ê²Œ 하면 초기화가 ëœë‹¤. {{{~cpp template<class BeingCounted> int Counted<BeingCounted>::numObjects; // ê°ì²´ì˜ ì„ ì–¸ê³¼, ìžë™ìœ¼ë¡œ 0으로 초기화가 ëœë‹¤. }}} DeleteMe) ì´ê²ƒì´ ë˜ëŠ” ê³¼ì •ë„ ì •í™•ížˆ ì´í•´ê°€ 가지 않는다. ë˜ëŠ”ê±°ì˜€ë‚˜ 여태? ê³ ë¯¼ëœë‹¤. maxObjectsì˜ ì´ˆê¸°í™” ì—시 ë¬¸ì œë¡œ ëŒ€ë‘ ë˜ëŠ”ë°, 다ìŒê³¼ ê°™ì´ ì´ˆê¸°í™” 시킨다. {{{~cpp const size_t Counted<Printer>::maxObjects = 10; }}} 만약 FileDescriptorê°€ ìœ ë„ëœ í´ëž˜ìФë¼ë©´ ì´ë ‡ê²Œ 한다. {{{~cpp const size_t Counted<FileDescriptor>::maxObjects = 16; }}} ì´ëŠ” ë¹„ë¡ í•´ë‹¹ ê°ì²´ê°€ private로 ìƒì† 받았지만, ìœ ë„ëœ ê°ì²´ê°€ ìƒì„±ë 때 다ìŒê³¼ ê°™ì´ ì´ˆê¸°í™”ë¥¼ 시킬수 있다. 초기화 리스트와 ë¹„ìŠ·í•˜ë‹¤ê³ í•´ì•¼ í• ê¹Œ? == Item 27: Requiring or prohibiting heap-based objects. == * Item 27: Heapì˜ì—ì„ ì‚¬ìš©í•˜ëŠ” ê°ì²´ 요구하기 or 피하기. ì´ë²ˆ 장ì—서는 Heapì˜ì—ì—서 ê°ì²´ ì‚¬ìš©ì— ê´€í•˜ì—¬ 다루어 본다. [[BR]] 때로 ìžì‚´ì„ 허용해야 하는 ê°ì²´ë¥¼ ë§Œë“¤ë•Œë„ ìžˆë‹¤. í‘œí˜„ì´ ê²©í•œê°€? "delete this"따위 ê°™ì´ ë§ì´ë‹¤. ì´ëŸ¬í•œ ê°ì²´ëŠ” heapì˜ì—ì— ë°°ì¹˜ ë˜ëŠ”ë°, 확실하게 ì œê±°ë§Œ 해준다면 ìžì›ì´ ì…€ ì´ìœ 는 없다. ê·¸ë ‡ë‹¤. ì£¼ì˜ ê¹Šê²Œ 다루어야 하는 ê°ì²´ë“¤ì´ë‹¤. embedded systemê°™ì´ ì—´ì•…í•œ 환경ì—서 ì´ë“¤ì— 잘못다루어서 ì¼ì–´ë‚œ 실수는 치명ì ì¸ ì†ìƒì„ 부른다. ì´ì œ 하나하나 heapì˜ì—ì— ê´€ë ¨í•œ ë‚´ìš©ì„ ë‹¤ë£¬ë‹¤. === Requiring Heap-Based Objects : Heap 기반 ê°ì²´ë“¤ì´ í•„ìš” === ìš°ì„ ê°ì²´ë¥¼ Heapì˜ì— ìƒì—서만 ìƒì„±ë˜ê³ 사용하는 ê°ì²´ë¡œ ì œí•œí•˜ëŠ” ê²ƒì— ê´€í•´ì„œ ìƒê°í•´ ë³´ìž. Heapì˜ì—ì— ìžë¦¬ë¥¼ 차지하는 ê°ì²´ëŠ” ëª¨ë‘ new를 호출해서 한ìžë¦¬ì”© 하니, ë, 간단히 new를 막아 버리면 ë˜ëŠ”ê²ƒì´ë‹¤. ê·¸ë ‡ë‹¤ë©´ 반대로 Heapì˜ì—ì´ ì•„ë‹Œ ê°ì²´ë“¤ì€ ëª¨ë‘ new를 호출하지 ì•Šê³ , ìžë™ìœ¼ë¡œ 묵시ì ìƒì„±ë˜ê³ , 묵시ì 파괴ë˜ì–´ 진다는 ì˜ë¯¸ê°€ ë™ã…†ë‹¤. Item 26ì— ë‹¤ë£¬ê²ƒê³¼ ê°™ì´ ìƒì„±ìžì™€ 파괴ìžë¥¼ 사ì—(private)로 묶어 버리는 ì•„ì´ë””ì–´ê°€ ê°ì²´ ìƒì„± ì œí•œì˜ ì‹œìž‘ì´ ë 수 ìžˆì„ ê²ƒì´ë‹¤. 하지만, 둘다 묶어 버리는건 너무 과잉ì´ë‹¤. 그냥 파괴ìžë§Œì„ 사ì—(private)로 묶어 ë²„ë¦¬ê³ , ìƒì„±ìžëŠ” ê³µì—(public)으로 ë†“ì•„ë„ íš¨ê³¼ëŠ” 비슷하다. 다ìŒì˜ ì˜ˆì œë¥¼ ë³´ìž. {{{~cpp class UPNumber { public: UPNumber(); UPNumber(int initValue); UPNumber(double initValue); UPNumber(const UPNumber& rhs); // 가짜 파괴ìž(pseudo-destructor) ìƒìˆ˜ 멤버 메소드, // ìƒìˆ˜ ê°ì²´ë¥¼ ì œê±° í• ìˆ˜ 있게 하기 ë•Œë¬¸ì— void destroy() const { delete this; } ... private: ~UPNumber(); // 파괴ìžê°€ 사ì—(private)으로 ì„ ì–¸ë˜ì—ˆë‹¤. }; }}} í´ë¼ì´ì–¸íŠ¸ê°€ ì´ë ‡ê²Œ ì„ ì–¸ëœ í´ëž˜ìŠ¤ë¥¼ 기반한 ê°ì²´ì˜ ì‚¬ìš©ì„ ì•Œì•„ ë³´ìžë©´ {{{~cpp UPNumber n; // ì—러! íŒŒê´´ìž ì‚¬ì—(private)ì´ë¼ ìž‘ë™ í• ìˆ˜ 없다. UPNumber *p = new UPNumber; // 통과 ... delete p; // ì—러! ì—시 íŒŒê´´ìž ì‚¬ì—(private) ì´ìœ p->destroy(); // 통과 }}} ìž ë‹¤ìŒê³¼ ê°™ì´, UPNumber í´ëž˜ìŠ¤ëŠ” Heapìƒì—서만 ì‚¬ìš©í• ìˆ˜ 있는 ê°ì²´ë§Œì„ ìƒì„± í• ìˆ˜ 있다. ì´ê²ƒì˜ 대안으로는 Item 26 ë§ˆì§€ë§‰ì— ë‚˜ì˜¨ ì˜ˆì œì™€ ê°™ì´ ëª¨ë“ ìƒì„±ìž ë§Œì„ ì‚¬ì—(private)í™” 시키는 것ì´ì§€ë§Œ, ì´ ì•„ì´ë””ì–´ì˜ ë¬¸ì œëŠ” ë§Žì€ ìƒì„±ìžê°€ ëª¨ë‘ ì‚¬ì—(private)으로 있어야 í•˜ê³ , ê·¸ê²ƒë“¤ì„ ë°˜ë“œì‹œ 기억해야 한다는 ì ì´ë‹¤. 기본 ìƒì„±ìžëŠ” ë¬¼ë¡ , 복사 ìƒì„±ìžë¥¼ ì „ë¶€ ì„ ì–¸í•´ 주어야 한다. ê·¸ë ‡ì§€ 않으면 컴파ì¼ëŸ¬ëŠ” 기본ì 으로 ëª¨ë‘ ê³µì—(public)으로 ì·¨ê¸‰í•˜ê³ ì§€ì— ë³€ìˆ˜ë¥¼ 만들수 있다. ê²°ê³¼ì 으로, 파괴ìžë§Œì„ 사ì—(private)í™” 시키는 ê²ƒì´ ê°„ë‹¨í•˜ë‹¤. í´ëž˜ìŠ¤ì˜ ìƒì„±ìžì™€, íŒŒê´´ìž ì–‘ìª½ì„ ì‚¬ì—í™”(private)시켜서 ì§€ì— ê°ì²´(non-heap object)로 만드는걸 막는ë°ë„, ì•½ê°„ì˜ ì œí•œ 사í•ì´ ë°œìƒí•œë‹¤. ê·¸ ì œí•œì‚¬í•ì´ëž€, ìƒì† 관계와 í¬í•¨(containment:has-a)관계ì—서 ë°œìƒí•œë‹¤. 다ìŒì˜ ì˜ˆì œë¥¼ ë³´ë©´. {{{~cpp class UPNumber { ... }; // ìƒì„±ìžì™€ íŒŒê´´ìž ëª¨ë‘ ì‚¬ì—(private)로 //ì„ ì–¸ë˜ì—ˆë‹¤ê³ ê°€ì •í•œë‹¤. class NonNegativeUPNumber: public UPNumber { ... }; // ì—러! ìƒì„±ìžì™€ 파괴ìžë¥¼ ì»´íŒŒì¼ ëª»í•œë‹¤. class Asset { private: UPNumber value; // ì—러! ìƒì„±ìžì™€ 파괴ìžë¥¼ ì»´íŒŒì¼ ëª»í•œë‹¤. ... }; }}} ì´ëŸ° ë¬¸ì œëŠ” 해결하기 ì–´ë µì§€ 안하. ìƒì† ê´€ê³„ì˜ ë¬¸ì œëŠ” ìƒì„±ìžëŠ” ê³µì—(public)으로 ìœ ì§€í•˜ê³ , 파괴ìžë§Œì„ 보호(proteced) 관계로 바꾸면 ë˜ëŠ”ê²ƒì´ê³ , í¬í•¨(contain) 관계ì—서는 해당 í¬í•¨ ì¸ìžë¥¼ pointer로 ë°”ê¾¸ê³ , 초기화 리스트ì—서 ìƒì„±í•´ ë²„ë¦¬ê³ , 파괴ìžì—서 약간만 ì‹ ê²½ì¨ì£¼ë©´ ëœë‹¤. ìœ„ì˜ ì½”ë“œì˜ í•´ê²°ì±…ì€ ë‹¤ìŒê³¼ 같다. {{{~cpp class UPNumber { ... }; // 파괴ìžë¥¼ 보호(protected)로 ì„¤ì •í•œë‹¤. // ëŒ€ì‹ ìƒì„±ìžëŠ” ê³µì—(public)으로 ì„¤ì •í•œë‹¤. // ê¸°ëŠ¥ìƒ ìƒê´€ 없으므로 class NonNegativeUPNumber: public UPNumber { ... }; // ì´ì œëŠ” ìœ ë„ë˜ëŠ” ë°ëŠ” 지장 없다. // ìœ ë„ë˜ëŠ” í´ëž˜ìŠ¤ë“¤ì€ protectedì¸ìžëŠ” // ì ‘ê·¼í•˜ëŠ”ë° ë¬¸ì œê°€ 없다. class Asset { public: Asset(int initValue); ~Asset(); ... private: UPNumber *value; // 위ì—ì„œì˜ ê°ì²´ ì„ ì–¸ì„ í¬ì¸í„°ë¡œ 바꾸었다. }; Asset::Asset(int initValue) : value(new UPNumber(initValue)) // 다ìŒê³¼ ê°™ì´ ì´ˆê¸°í™” 리스트로 ê°ì²´ë¥¼ ë§Œë“¤ê³ { ... } Asset::~Asset() { value->destroy(); } // íŒŒê´´ì‹œì— ì‹ ê²½ì¨ì¤€ë‹¤.(resource leakë°©ì§€) }}} === Determining Whether an Object is On The Heap : Heapì— ê°ì²´ë¥¼ 올릴지 ê²°ì •í•˜ê¸°. === ìž, 지금까지 다소 맹목ì (?)으로 Heapì˜ì—ì— ê°ì²´ 올리기ì—ë§Œ 열중했다. 그럼 여기ì—서는 "on the heap"ì˜ ì˜ë¯¸ë¥¼ 확실한 테스트로서 알아 ë³´ë„ë¡ í•˜ê² ë‹¤. 앞서 ì¨ë¨¹ì€ NonNegativeUPNumber를 non-heap ê°ì²´ë¡œ 만드는건 ë 틀리지 ì•Šì€ ê²ƒì´ë‹¤. {{{~cpp NonNegativeUPNumber n; // fine }}} ì´ê²ƒì´ 허용ëœë‹¤ëŠ” 것ì´ë‹¤. ìž, 그럼 ì§€ì— ê°ì²´ì¸ NonNegativeUPNumberì˜ nì—서 UPNumberì˜ ë¶€ë¶„ì€ heapìœ„ì— ìžˆëŠ” ê²ƒì´ ì•„ë‹ˆë‹¤. 맞는가? ì´ ë‹µë³€ì€ í´ëž˜ìŠ¤ì˜ ì„¤ê³„(design)ê³¼ ì ìš©(implementation)ì— ê¸°ì¸í•´ì•¼ ì´í•´ í• ìˆ˜ ìžˆì„ ê²ƒì´ë‹¤. ë‹µì„ ì°¾ì•„ê°€ ë³´ìž.UPNumberê°€ 반드시 heapì˜ì—ì— ì¡´ìž¬ 해야 한다는 ê²ƒì— ê´€í•´ not okay를 ë˜ì§€ë©´ì„œ 시작해 ë³´ìž. 어떻게 우리는 ì´ëŸ¬í•œ ì œí•œ 사í•ì„ í•´ê²°í•´ì•¼ 하는 걸까? ì´ê²ƒì€ 그리 쉽지 않다. UPNumber ìƒì„±ìžê°€ ì´ê²ƒì„ ìœ ë„ ë°›ì€ ëª¨ë“ ê°ì²´ì—게, Heapì˜ì— ê¸°ë°˜ì˜ ê°ì²´ë¡œ 만들어 버리ë¼ê³ ê²°ì • í•˜ëŠ”ê²ƒì€ ê°€ëŠ¥í•˜ì§€ 않다. 기본 í´ëž˜ìŠ¤ë¡œ ì“°ì¸ UPNumberì˜ ìƒì„±ìžê°€ 다ìŒì˜ ë‘ ìƒí™©ì„ ì²´í¬í•˜ëŠ” ê²ƒì€ ë¶ˆê°€ëŠ¥í•˜ë‹¤. {{{~cpp NonNegativeUPNumber *n1 = new NonNegativeUPNumber; // Heapì˜ì— NonNegativeUPNumber n2; // 비 Heapì˜ì— }}} ê·¸ë ‡ìžë¯¼ 아마 조금 다른 ë°©ë²•ì˜ ì ‘ê·¼ì„ í• ìˆ˜ 있다. 바로 Heapì˜ì—ì— ì˜¬ë¼ê°€ëŠ” ê°ì²´ëŠ” í•ìƒ new를 호출하는것, ê·¸ë¦¬ê³ ì´ newì˜ í˜¸ì¶œì€ new operator와 operator newì™€ì˜ ìƒí˜¸ 작용ì—서 ìž‘ì—…ì´ ì´ë£¨ì–´ 지는 것ì´ë‹¤. ìžì„¸í•œ ë‚´ìš©ì€ Item 8ì„ ì°¸ê³ í•˜ê³ , 다ìŒê³¼ ê°™ì´ UPNumber를 ê³ ì³ì„œ ìœ ë„ë˜ëŠ” ê°ì²´ë“¤ ë§ˆì ¸ ì œí•œì„ ê±¸ìˆ˜ 있다. {{{~cpp class UPNumber { public: // 만약 non-heap ê°ì²´ë¼ë©´ 다ìŒì˜ 예외를 ë°œìƒ ì‹œí‚¨ë‹¤. class HeapConstraintViolation {}; static void * operator new(size_t size); UPNumber(); ... private: static bool onTheHeap; // ìƒì„±ìž ë‚´ë¶€ì—서 heapì˜ì—ì— ìƒì„±ë˜ëŠ”ê°€ íŒë³„ì¸ìžë¡œ 쓴다. ... }; // static ì¸ìžì˜ ì˜ ì´ˆê¸°í™” 부분 bool UPNumber::onTheHeap = false; void *UPNumber::operator new(size_t size) { onTheHeap = true; // new operatorê°€ 불리면 Heapì˜ì—ì„ ì‚¬ìš©í•˜ëŠ”ê²ƒ. return ::operator new(size); // operator new로서 메모리를 í• ë‹¹í•œë‹¤. } UPNumber::UPNumber() { if (!onTheHeap) { throw HeapConstraintViolation(); // íž™ì´ ì•„ë‹ˆë¼ë©´ 예외를 ë˜ì§„다. } ì¼ë°˜ì ì¸ ìƒì„± ê³¼ì •ì„ ê¸°ìˆ í•œë‹¤.; onTheHeap = false; // ê°ì²´ ìƒì„± 플래스를 세팅한다. } }}} operator new는 raw메모리 í• ë‹¹ì„ í•˜ê³ , 해당 메모리ì—서 ìƒì„±ìžë¥¼ 부르므로서 초기화를 수행한다. operator newì—서 onTheHeapì„ ì°¸ìœ¼ë¡œ ì„¤ì •í•˜ì—¬ 주면, ìƒì„±ìžì—서 ì´ë¥¼ 검사해서 예외 ë°œìƒì„ 하지 ì•Šê³ , ì¼ë°˜ ì§€ì— ë³€ìˆ˜ë¡œ ê°ì²´ê°€ ì„ ì–¸ë˜ë©´ operator new를 거치지 않으므로, 기본 ê°’ì¸ falseì¸í•´ ìƒì„±ìžì—서 예외를 ë°œìƒì‹œí‚¨ë‹¤. 하지만 ì´ ì½”ë“œë‹¤ 능사가 아닌것ì´, 다ìŒê³¼ ê°™ì€ ê°ì²´ì˜ ë°°ì—´ì„ ì„ ì–¸í•˜ë©´ ë¬¸ì œê°€ ëœë‹¤. {{{~cpp UPNumber *numberArray = new UPNumber[100]; }}} 첫째로, ì´ ê²½ìš° operator newê°€ 불리는 ê²ƒì´ ì•„ë‹ˆë¼. 메모리는 operator new[]로 í• ë‹¹ ë˜ê¸°ë•Œë¬¸ì—, ë¬¸ì œê°€ ë°œìƒí•˜ëŠ” 것ì´ê³ , 둘째로 operator new[]ì— í”Œëž˜ê·¸ ê°’ì„ ì£¼ì—ˆë‹¤ê³ í•˜ë”ë¼ë„, ì²˜ìŒ í•œë²ˆì˜ operaotr new[]ì´í›„ì— ê³„ì† ìƒì„±ìž 100ë²ˆì´ ë¶ˆë¦¬ë©´ì„œ 첫번째 ìƒì„±ìžì—서 다시 onTheHeap를 false로 초기화 시키기ì—, ì´í›„ì— ë¶ˆë¦¬ëŠ” ìƒì„±ìžëŠ” ì „ë¶€ onTheHeapì´ false값으로 예외를 ë°œìƒ ì‹œì¼œ 버린다. ë˜ ë°°ì—´ì´ ì•„ë‹ˆë”ë¼ë„ 다ìŒê³¼ ê°™ì€ ê²½ìš°ë„ ìƒê°í•´ 본다. {{{~cpp new UPNumber(*new UPNumber); }}} ì´ ê²½ìš°ì—는 ë‘ê°€ì§€ì˜ new를 ê°€ì§€ê³ ìžˆë‹¤. 그러므로 operator newë„ ë‘번 ë¶ˆë¦¬ê³ ìƒì„±ìž ì—시 ë‘번 불릴 것ì´ë‹¤. 프로그래머가 ì¼ë°˜ì 으로 기대하는 ë‹¤ìŒ ìˆœì„œì—서는 아무런 ë¬¸ì œê°€ 없다. (Item 8 ì°¸ê³ ) 1. operator newê°€ 첫번째 ê°ì²´ì— 관해서 불린다. 1. 첫번째 ê°ì²´ì˜ ìƒì„±ìžê°€ 불린다. 1. ë‘번째 ê°ì²´ì˜ operator new ê°€ 불린다. 1. ë‘번째 ê°ì²´ì˜ ìƒì„±ìžê°€ 불린다. ê·¸ë ‡ì§€ë§Œ C++언어 ìƒì—서 ì´ëŸ° ë³´ìž¥ì— ëŒ€í•œ ìŠ¤íŽ™ì´ ì •ì˜ ë˜ì–´ 있지 않다 그래서 ì–´ë–¤ 컴파ì¼ëŸ¬ëŠ” 다ìŒê³¼ ê°™ì´ ë¶€ë¥¼ìˆ˜ë„ ìžˆë‹¤. 1. 첫번째 ê°ì²´ì˜ operator newê°€ 불린다. 1. ë‘번째 ê°ì²´ì˜ operator newê°€ 불린다. 1. 첫번째 ê°ì²´ì˜ ìƒì„±ìžê°€ 불린다. 1. ë‘번째 ê°ì²´ì˜ ìƒì„±ìžê°€ 불린다. : 여기서 ë¬¸ì œê°€ ë°œìƒí•œë‹¤. 후ìžì˜ 순서로 코드가 ìƒì„±ë˜ì–´ë„ 컴파ì¼ëŸ¬ì—게 ìž˜ëª»ì€ ì „í˜€ 없다. ê·¸ë ‡ì§€ë§Œ '''set-a-bit-in-operator-new''' ë°©ë²•ì„ ì‚¬ìš©í•œ ìƒë‹¨ì˜ ì˜ˆì œëŠ” ë‘번째 순서ì—서는 실패 í• ìˆ˜ ë°–ì— ì—†ë‹¤. * DeleteMe ì£¼ì œ 변환ì ì´ëŸ° ì–´ë ¤ì›€ì´ "ê° ìƒì„±ìžì—서 *thisê°€ heapì˜ì—ì— ìžˆëŠ”ê°€ì— ëŒ€í•œ 여부를 알아낸다." ë¼ëŠ” ì•„ì´ë””ì–´ì˜ ê·¼ê°„ì„ í”드는 ê²ƒì€ ì•„ë‹ˆë‹¤. 거기ì—다가 ì´ëŸ° ì–´ë ¤ì›€ë“¤ì€ operator new나 operator new[] 안ì—서 bit setì„ ì 검해 보는 ê²ƒì´ ì´ëŸ° 기본 ì •ë³´ë¥¼ ê²°ì •í•˜ëŠ”ë° ì‹ ë¢°ì„± 있는 ë°©ë²•ì´ ì•„ë‹˜ì„ ë°˜ì¦í•˜ê³ 있다. 우리가 필요한 ë°©ë²•ì„ ìœ„í•´ì„œ 한번 ìƒê°í•´ 본다. ì ˆë§í•˜ê³ 있는가? 그럼 한번 임시로나마 ì´ì‹ì„±ì´ 떨어지는 ì˜ì—ì—서까지 그런 ì•„ì´ë””어를 확장해서 ìƒê°í•´ ë³´ìž. 예를 들어서 ë§Žì€ ì‹œìŠ¤í…œ ìƒì—서 사실ì¸, í”„ë¡œê·¸ëž¨ì˜ ì£¼ì†Œ ê³µê°„ì€ ì„ í˜•ìœ¼ë¡œ ë°°ì—´ë˜ê³ , í”„ë¡œê·¸ëž¨ì˜ ìŠ¤í…ì€ ìœ„ì—서 아래로 늘어 ë‚œë‹¤ê³ ê·¸ë¦¬ê³ Heapì˜ì—ì€ ë°‘ì—서 위로 늘어난다는 ì‚¬ì‹¤ì— ì£¼ëª©í•´ ë³´ìž. 그림으로 표현ë˜ë©´ 다ìŒê³¼ ê°™ì€ ëª¨ìŠµì´ ëœë‹¤. http://zeropage.org/~neocoin/data/MoreEffectiveC++_150_1.gif ì´ëŸ° ë°©ì‹ìœ¼ë¡œ êµ¬ì„±ëœ í”„ë¡œê·¸ëž¨ì˜ ì‹œìŠ¤í…œì—서 다ìŒê³¼ ê°™ì€ ë°©ë²•ìœ¼ë¡œ 비êµë¥¼ í• ìˆ˜ 있지 않ì„까? {{{~cpp // heapì— ëŒ€í•œ 여부가 ì •í™•í•˜ì§€ ì•Šì€ ì ê²€ 방법 bool onHeap(const void *address) { char onTheStack; // ì§€ì— ìŠ¤íƒ ë³€ìˆ˜(local stack variable) return address < &onTheStack; // 주소 ë¹„êµ } }}} 함수ì—서 ì´ëŸ¬í•œ ìƒê°ì€ ì°¸ ì˜ë¯¸ë¡ë‹¤. onHeap함수내ì—서 onTheStack는 ì§€ì— ë³€ìˆ˜(local variable)ì´ë‹¤. 그러므로 ê·¸ê²ƒì€ ìŠ¤íƒì— ìœ„ì¹˜í• ê²ƒì´ê³ , onHeapê°€ 불릴때 onHeapì˜ ìŠ¤í… í”„ë ˆìž„ì€ ì•„ë§ˆ 프로그램 스í…ì˜ ê°€ìž¥ ìœ„ìª½ì— ë°°ì¹˜ ë 것ì´ë‹¤. 스íƒì€ 밑으로 ì¦ê°€í•˜ëŠ” 구조ì´ê¸°ì—, onTheStack는 방드시 ì–´ë– í•œ stack-based 변수나 ê°ì²´ì— 비하여 ë” ë‚®ì€ ìœ„ì¹˜ì˜ ë©”ëª¨ë¦¬ì— ìœ„ì¹˜í•˜ê³ ìžˆì„ ê²ƒì´ë‹¤. 만약 address ì¸ìžê°€ onTheStackì˜ ìœ„ì¹˜ë³´ë‹¤ ë” ìž‘ë‹¤ë©´ 스íƒìœ„ì— ìžˆì„수 없는 것ì´ê³ , ì´ëŠ” heapìƒì— 위치하는 ê²ƒì´ ë˜ëŠ” 것ì´ë‹¤. ì´ëŸ¬í•œ 구조는 옳다. 하지만 ì•„ì§ ì¶©ë¶„ížˆ ìƒê°í•˜ì§€ ì•Šì€ ê²ƒì´ ìžˆëŠ”ë°, ê·¸ê²ƒì€ ê°ì²´ê°€ ìœ„ì¹˜í• ìˆ˜ 있는 ì„¸ê°€ì§€ì˜ ìœ„ì¹˜ë¥¼ ê°ì•ˆí•˜ì§€ ì•Šì€ ê·¼ë³¸ì ì¸ ë¬¸ì œì´ë‹¤. ì§€ì—(local) 변수,ê°ì²´(variable, object)나, Heapì˜ì— ìƒì˜ ê°ì²´ëŠ” ê°ì•ˆí•´ì§€ë§Œ ë¹¼ë¨¹ì€ í•˜ë‚˜ì˜ ì˜ì— 바로 ì •ì (static)ê°ì²´ì˜ 위치 ì˜ì—ì´ë‹¤. ì´ë“¤ì˜ 위치는 ì „ë¶€ 수행ë˜ëŠ” ì‹œìŠ¤í…œì— ì˜ì¡´ì ì´ë‹¤. 하지만 ë§Žì€ ì‹œìŠ¤í…œì´ stack와 heapì´ ì„œë¡œë¥¼ 향해 ì¦ê°€ 하ë„ë¡ êµ¬ì„±ë˜ì–´ 있으며, 가장 ìµœí•˜ë‹¨ì— staticì—¬ì—ì´ ìžë¦¬ 잡는 구성으로 ë˜ì–´ 있다. 그러므로 ì•žì— ì–¸ê¸‰í•œ 그림ì—서 한가지를 추가하면 ì§„ì§œ ë©”ëª¨ë¦¬ì˜ êµ¬ì„±ì´ ëœë‹¤. 다ìŒê³¼ ê°™ì´ ë§ì´ë‹¤.: http://zeropage.org/~neocoin/data/MoreEffectiveC++_151_1.gif ê°‘ìžê¸° ì´ì œ 앞서 작성한 onHeap함수가 ì–´ë– í•œ 시스템ì—ì„œë„ ì •í™•í•œ ìˆ˜í–‰ì„ ëª»í• ê²ƒì´ ëª…ë°±í•´ 진다.:heap ê°ì²´ì™€ ì ì (static) ê°ì²´ë¥¼ 구별하지를 못한다. ì˜ˆì œì—서 ë³´ë©´ {{{~cpp void allocateSomeObjects() { char *pc = new char; // heap ê°ì²´: onHeap(pc) // ì´ê²ƒì€ true를 반환 char c; // 스íƒ(=:ì§€ì—) ê°ì²´ object: onHeap(&c) // ì´ ì£¼ì†ŒëŠ” false 를 반환 static char sc; // ì •ì (static) ê°ì²´: onHeap(&sc) // ì´ ì£¼ì†ŒëŠ” true 를 반환 ... } }}} ì´ì œ heap ê°ì²´ì™€ stack ê°ì²´ë¥¼ íŒë³„하는 ë°©ë²•ì— í˜¼ëž€ì´ ì˜¬ 것ì´ë‹¤. ê·¸ë¦¬ê³ ì´ëŸ¬í•œ ë°©ë²•ì€ ì´ì‹ì„±ì—ë„ ì—시나 ë¬¸ì œê°€ 있다. 그래서 ê²°êµ '''compare-the-addresses''' ë°©ë²•ì€ ì‹ ë¢°í• ìˆ˜ 없는 ë°©ë²•ì´ ëœë‹¤. 여담ì´ë¼ë©´, ì´ëŸ¬í•œ ë°©ë²•ì€ ëŒ€ë‹¤ìˆ˜ì˜ ì‹œìŠ¤í…œì—서 사실ì´ì§€ë§Œ, ê¼ ê·¸ë ‡ë‹¤ê³ ëŠ” 볼수 없다. 그러니까 ì™„ì „ížˆ not portable한게 아닌 semi-portableì´ë¼ê³ í‘œí˜„ì„ í•´ì•¼ 하나. 그래서 ì•žë¨¸ë¦¬ì— ìž ì‹œë‚˜ë§ˆ ìƒê°í•´ ë³´ìžê³ 한것ì´ê³ , ì´ëŸ¬í•œ 시스템 ì˜ì¡´ì ì¸ ì„¤ê³„ëŠ” 차후 다른 ì‹œìŠ¤í…œì— ì´ì‹ì‹œì— ë©”ëª¨ë¦¬ì˜ í‘œí˜„ ë°©ë²•ì´ ë‹¤ë¥´ë‹¤ë©´, 소프트웨어 ìžì²´ë¥¼ 다시 설계해야 하는 ìœ„í—˜ì„±ì´ ìžˆë‹¤. 그냥 ì•Œê³ ë§Œ 있ìž. * DeleteMe ì£¼ì œ 변환ì 우리는 앞쪽ì—서 "delete this"로 ê°€ìƒ íŒŒê´´ìžë¡œ ê°ì²´ê°€ 스스로를 ìžì‚´ 시키는 방법으로 heapê°ì²´ë§Œì„ 사용하ë„ë¡ ì œí•œ 시키는 ë°©ë²•ì„ ê¸°ì–µí• ê²ƒì´ë‹¤. ì´ëŸ° "delete this"ì‹ìœ¼ë¡œì˜ ì œê±°ëŠ” ì¶”ì²œí• ë§Œí•œ ë°©ë²•ì´ ê²°ì½” 아니다. ( DeleteMe 모호) ê·¸ë ‡ì§€ë§Œ, 지우기 위한 ê°ì²´ì˜ ì•ˆì „ì„±ì„ ì•„ëŠ” ê²ƒì€ heapìƒì—서 í¬ì¸í„°ê°€ ì§€ì¹í•˜ëŠ”ê°€ë¥¼ 간단히 ì•Œì•„ë„¤ê³ ìž í•˜ëŠ” 방법과 ê°™ì€ ê²ƒì´ ì•„ë‹ˆë‹¤. ìž, 다시 UPNumber ê°ì²´ë¥¼ 가지는 Asset ê°ì²´ì˜ 관해서 ìƒê°í•´ ë³´ìž. {{{~cpp class Asset { private: UPNumber value; ... }; Asset *pa = new Asset; }}} 명백히 *pa는 heapìƒì— 위치한 ê°ì²´ì´ë‹¤. ë˜ ëª…ë°±í•˜ê²Œ, pa->valueì˜ í¬ì¸í„° ì—시 delete로 지울수 없다. 왜ëƒí•˜ë©´ ì´ë…€ì„ì´ new를 호출해서 ë§Œë“ ê²ƒì´ ì•„ë‹ˆê¸° 떄문ì´ë‹¤. í¬ì¸í„°ê°€ 지우기 ì•ˆì „í•œê°€ì— íŒë‹¨ì€, í¬ì¸í„°ê°€ heapìƒì— 위치하는 ê°ì²´ë¥¼ 가리키는가를 알아내는 것보다 쉽다. 왜ëƒí•˜ë©´ 우리는 operator new로 ì¸í•´ì„œ 반환ë˜ëŠ” ì£¼ì†Œì˜ ëª¨ìŒìœ¼ë¡œ, ì „ìžì˜ ì§ˆë¬¸ì— ê´€í•´ì„œ 알아 낼수 있기 떄문ì´ë‹¤. 여기 ì˜ˆì œì— ê·¸ëŸ° ë¬¸ì œì— ê´€í•œ ì ‘ê·¼ ë°©ì‹ì´ ê¸°ìˆ ë˜ì–´ 있다. {{{~cpp void *operator new(size_t size) { void *p = getMemory(size); // 메모리를 í• ë‹¹í•˜ëŠ” ì–´ë–¤ 함수를 호출한다. // ê·¸ë¦¬ê³ ì´ê²ƒì€ out-of-memory 예외를 ìž¡ì„수 있다. í• ë‹¹ëœ ì£¼ì†Œë¥¼ collectionì— ì €ìž¥í•œë‹¤.; return p; } void operator delete(void *ptr) { releaseMemory(ptr); // 메모리를 free한다. ì´ë²ˆì— 지운 주소를 collectionì—서 ì œê±°í•œë‹¤.; } bool isSafeToDelete(const void *address) { 물어보는 주소가 collectionìƒì— 존재하는지 여부를 반환한다.; } }}} ì´ëŸ¬í•œ ê²ƒì€ ê°„ë‹¨í•˜ë‹¤. operator new는 collectionì— ë©”ëª¨ë¦¬ë¥¼ í• ë‹¹í•˜ëŠ” 주소를 기ë¡í•˜ê³ , operator delete는 ê·¸ê²ƒì„ ì§€ìš´ë‹¤. ê·¸ë¦¬ê³ isSafeToDelete는 collectionì— í•´ë‹¹ 주소가 있는지 ì•Œë ¤ì£¼ëŠ” ì—í• ì„ í•œë‹¤. 만약 operator new와 operator deleteê°€ ì „ì— ê³µê°„ì— ìžˆë‹¤ë©´ ì´ê²ƒì€ ëª¨ë“ íƒ€ìž…ì˜ ìž‘ì—…ì‹œì— ì ìš© ë 것ì´ë‹¤. ì‹¤ì œë¡œ 세가지 ìƒê°ì´ ì´ëŸ¬í•œ ë””ìžì¸ì„ 매달리지 못하게 한다. '''첫번째는''' ì „ì— ê³µê°„ì— ì–´ë–¤ê²ƒì„ ì •ì˜í•˜ëŠ” ê·¹ë„로 í”¼í•˜ë ¤ëŠ” 경향ì´ë‹¤. operator enw나 operator deleteê°™ì€ ë¯¸ë¦¬ ì •ì˜ëœ í•¨ìˆ˜ì— ëŒ€í•˜ì—¬ 특별하게 ê³ ì¹œë‹¤ëŠ” ê²ƒì€ ë” ê·¸ëŸ´ 것ì´ë‹¤. '''둘째로''' íš¨ìœ¨ì— ê´€í•œ ë¬¸ì œì´ë‹¤. ëª¨ë“ ë©”ëª¨ë¦¬ í• ë‹¹ì—서 overheadê°€ ë°œìƒí•œë‹¤ëŠ” ì˜ë¯¸ì¸ë°, ì´ê²ƒì„ ìœ ì§€í•˜ê² ëŠ”ê°€? '''마지막으로''' ê±±ì •ë˜ëŠ” ê²ƒì€ í‰ë²”하지만 중요한 것으로 isSafeToDeleteì´ ëª¨ë“ ìˆ˜í–‰ì— ê´€í•˜ì—¬ ì ìš©ë˜ëŠ” ì 용하는 것ì´ë‹¤. 하지만 ì´ê²ƒì´ 근본ì 으로 ë¶ˆê°€ëŠ¥í•˜ë‹¤ê³ ë³´ì´ê¸° 때문ì´ë‹¤. ì¡°ê¸ˆë” ì´ì•½ í•´ë³´ìžë©´, 다중 ìƒì†ì´ë‚˜, virtual base classê°€ 가지는 ì—¬ëŸ¬ê²Œì˜ ì£¼ì†Œë“¤, ì´ ì£¼ì†Œë“¤ ë•Œë¬¸ì— isSafeTo Deleteì—게 ì „ë‹¬ë˜ëŠ” ì£¼ì†Œì— ëŒ€í•œ 확실한 ë³´ìž¥ì´ ì—†ë‹¤. ìžì„¸í•œ ë‚´ìš©ì€ Item 24, 31ì¼ ì°¸ê³ í•˜ë¼. * DeleteMe)ì£¼ì œ ì „í™˜ ìž ìœ„ì˜ ì´ìœ 로 ì´ë²ˆ ì•„ì´ë””ì–´ë„ ì“°ë ˆê¸°í†µìœ¼ë¡œ 들어갈 ì°¸ì´ë‹¤. 하지만 ê·¸ ì•„ì´ë””어를 채용해서 C++ì—서는 abstract base class를 ì œê³µí• ìˆ˜ 있다. 여기서는 abstract mixin base classë¼ê³ 표현한다. ê°€ìƒ í´ëž˜ìФë¼ê³ í•´ì„ ë 수 있는 abstract base 는 초기화 ë 수가 없다. ë§ë¶™ì—¬ ë§í•˜ìžë©´, ì´ê²ƒì€ 순수 ê°€ìƒ í•¨ìˆ˜ë¥¼ 최소한 하나 ì´ìƒì€ ê°€ì§€ê³ ìžˆì–´ì•¼ë§Œ 한다. mixin("mix in")í´ëž˜ìŠ¤ëŠ” 잘 ì •ì˜ëœ 기능과 ìƒì†ë˜ëŠ” ì–´ë–¤ í´ëž˜ìŠ¤ì™€ë„ ìž˜ 부합ë˜ë„ë¡ ì„¤ê³„ë˜ì–´ì ¸ 있다. ì´ëŸ° í´ëž˜ìŠ¤ë“¤ì€ ê±°ì˜ í•ìƒ abstractì´ë‹¤. 그래서 여기ì—서는 abstract mixin base class는 ìš©ë„로 operator new로 ê°ì²´ì˜ í• ë‹¹ 여부만 알수 있는 ëŠ¥ë ¥ë§Œì„ ìœ ë„ëœ í´ëž˜ìФì—게 ì œê³µí•œë‹¤. {{{~cpp class HeapTracked { // mixin class; keeps track of public: // ptrs returned from op. new class MissingAddress{}; // 예외 í´ëž˜ìФ;아래 ì°¸ê³ virtual ~HeapTracked() = 0; static void *operator new(size_t size); static void operator delete(void *ptr); bool isOnHeap() const; private: typedef const void* RawAddress; static list<RawAddress> addresses; }; }}} ì´ í´ëž˜ìФì—서 listë°ì´í„° 구조체는 C++ì˜ ë¼ì´ë¸ŒëŸ¬ë¦¬ì— ì •ì˜ë˜ì–´ 있다.(Item 35ì°¸ê³ ) listê°€ 하는 ì¼ì€ 예ìƒë˜ëŠ” 것과 ê°™ì´ operator newì—서 ë°˜í™˜ëœ ì£¼ì†Œì˜ ì €ìž¥ì´ë‹¤. ê·¸ë¦¬ê³ operator delete는 메모리를 í•´ì œí•˜ê³ , list로 부터 해당 ì£¼ì†Œì˜ ì—”íŠ¸ë¦¬ë¥¼ 지운다. HeapTrack ì ìš©ì€ ê°„ë‹¨í•˜ë‹¤. 왜ëƒí•˜ë©´, operator new와 operator deleteê°€ ì‹¤ì œ 메모리 í• ë‹¹ê³¼ í•´ì œë¥¼ ìˆ˜í–‰í•˜ê³ list í´ëž˜ìŠ¤ëŠ” 삽입, 지우기, ê·¸ë¦¬ê³ ê²€ìƒ‰ ì—”ì§„ì„ í¬í•¨í•˜ê¸° 때문ì´ë‹¤. ìž ì—¬ê¸° ì´ëŸ° ë‚´ìš©ì˜ ì ìš©ì— ê´€ë ¨í•œ ë‚´ë¶€ 코드를 살피ìž. {{{~cpp // static í´ëž˜ìФ ë©¤ë²„ì˜ ì •ì˜ëŠ” 규칙ì´ë‹¤. list<RawAddress> HeapTracked::addresses; // HeapTrackedì˜ íŒŒê´´ìžëŠ” ê°€ìƒ í´ëž˜ìŠ¤ì˜ ìˆœìˆ˜ ê°€ìƒ í•¨ìˆ˜ì´ë‹¤. (E14ì°¸ê³ ) // 그래서 ì´ íŒŒê´´ìžê°€ 구현ë˜ì–´ì•¼ 만한다. HeapTracked::~HeapTracked() {} void * HeapTracked::operator new(size_t size) { void *memPtr = ::operator new(size); // 메모리 í• ë‹¹ addresses.push_front(memPtr); // 해당 주소를 listì˜ ì•”ìª½ì— ì €ìž¥ return memPtr; } void HeapTracked::operator delete(void *ptr) { // "iterator"를 얻어서 listì—서 검색하는 부분 Item 35ì°¸ê³ list<RawAddress>::iterator it = find(addresses.begin(), addresses.end(), ptr); if (it != addresses.end()) { // 지울 주소를 찾아서 addresses.erase(it); // 해당 엔트리를 ì§€ìš°ê³ ::operator delete(ptr); // 메모리를 í•´ì œí•˜ê³ } else { // 하지만 throw MissingAddress(); // ptrì— í• ë‹¹ì´ ì•ˆë˜ì—ˆë‹¤ë©´ 예외를 ë˜ì§„다. } } bool HeapTracked::isOnHeap() const { // *this로 í• ë‹¹ë°›ì€ ë©”ëª¨ë¦¬ì˜ ì‹œìž‘ì ì„ ì–»ëŠ” ê³¼ì •;ìžì„¸í•œê±´ ë°‘ì— ë³¸ë¬¸ const void *rawAddress = dynamic_cast<const void*>(this); // 주소 listì—서 pointer를 ì°¾ê³ operator newì— ì˜í•´ 반환ëœë‹¤. list<RawAddress>::iterator it = find(addresses.begin(), addresses.end(), rawAddress); return it != addresses.end(); // return whether it was } // found }}} ì´ì½”드는 약간 ìƒì†Œí•œ listí´ëž˜ìŠ¤ì— ëŒ€í•œ ê²ƒì´ ë³´ì¼ ê²ƒì´ë‹¤. ë, ì´ì œëŠ” í•˜ë„ ë§Žì´ ë‚˜ì™€ì„œ 별로 안ìƒì†Œ í• ê²ƒ 같다. STLì´ê³ Item 35ì— ëª¨ë“ ê²ƒì´ ì„¤ëª… ë˜ì–´ 있다. 하지만 주ì„ì„ ë°”íƒ•ìœ¼ë¡œ ì˜ˆì œì— ê´€í•˜ì—¬ 충분히 설명 가능하리ë¼ê³ 본다. 멤버 메소드 isOnHeapì— ì¡´ìž¬í•˜ëŠ” ì´ êµ¬ë¬¸ì´ ë‹¤ì†Œ ì˜ë¬¸ì´ 들것ì´ë‹¤. {{{~cpp const void *rawAddress = dynamic_cast<const void*>(this); }}} ( DeleteMe 모호 ) 위ì—서 isSafeToDelete를 êµ¬í˜„í• ë•Œ 다중 ìƒì†ì´ë‚˜ ê°€ìƒ ê¸°ì´ˆ 함수으로 ì—¬ëŸ¬ê°œì˜ ì£¼ì†Œë¥¼ ê°€ì§€ê³ ìžˆëŠ” ê°ì²´ê°€ ì „ì—ì˜ í•´ë‹¹ 함수를 복잡하게 í• ê²ƒì´ë¼ê³ 언급했다. 그런 ë¬¸ì œëŠ” isOnHeapì—서 ì—시 마찬가지ì´ë‹¤. 하지만 isOnHeap는 ì˜¤ì§ HeapTrackedê°ì²´ì— ì ìš© 시킨 것ì´ê¸° 때문ì—, dynamic_cast operatror를 활용으로 ìœ„ì˜ ë¬¸ì œë¥¼ ì œê±°í•œë‹¤. 간단히 í¬ì¸í„°ë¥¼ dynamic_cast 하는 ê²ƒì€ (í˜¹ì€ const void* or volatile void* or 알맞는 것으로 맞추어서) ê°ì²´ì˜ 가장 앞쪽 í¬ì¸í„°, 즉, í• ë‹¹ëœ ë©”ëª¨ë¦¬ì˜ ê°€ìž¥ ì•žìª½ì— ì£¼ì†Œë¥¼ 가리키는 í¬ì¸í„°ë¥¼ ì˜ë¯¸í•œë‹¤. ê·¸ë ‡ì§€ë§Œ dynamic_cast는 ê°€ìƒí•¨ìˆ˜ë¥¼ 하나 ì´ìƒ 가지는 ê°ì²´ë¥¼ 가리키는 í¬ì¸í„°ì— 한해서만 허용 ëœë‹¤. isSafeToDelete함수는 ëª¨ë“ í¬ì¸í„°ì— 관해서 가능하기 ë•Œë¬¸ì— dynamic_castê°€ 아무런 ì†Œìš©ì´ ì—†ë‹¤. isOnHeap는 ì¡°ê¸ˆë” ì„ íƒì˜ íì´ ìžˆì–´ì„œ this를 const void*로 dynamic_cast하는 ê²ƒì€ ìš°ë¦¬ì—게 현재 ê°ì²´ì˜ 메모리 시작ì ì˜ã…£ í¬ì¸í„°ë¥¼ 주게 ëœë‹¤. ê·¸ í¬ì¸í„°ëŠ” HeapTracked::operator newê°€ 반드시 반환해야만 하는 것으로 HeapTrack::operator newì˜ ì²˜ìŒ ë¶€ë¶„ì— ìžˆë‹¤. ë‹¹ì‹ ì˜ ì»´íŒŒì¼ëŸ¬ê°€ dynamix_cast를 ì§€ì›í•˜ë©´ ì´ëŸ¬í•œ ê¸°ìˆ ì€ ì´ì‹ì„±ì´ 높다. ì´ëŸ¬í•œ í´ëž˜ìŠ¤ê°€ ì£¼ì–´ì§€ê³ , BASIC 프로그래머는 ì´ì œ heap ì— í• ë‹¹í•˜ëŠ” ê²ƒì„ ì¶”ì í• ìˆ˜ 있는 ëŠ¥ë ¥ì„ í´ëž˜ìФì—게 부여 í• ìˆ˜ 있다. ì¶”ì ì„ ì›í•˜ëŠ” í´ëž˜ìŠ¤ëŠ” 반드시 HeapTracked를 ìƒì†í•˜ë„ë¡ ë§Œë“ ë‹¤. 예를들어서 우리가 Assetê°ì²´ë¥¼ ì‚¬ìš©í• ë•Œ 해당 ê°ì²´ê°€ heap-base ê°ì²´ì¸ì§€ ì•Œê³ ì‹¶ë‹¤ë©´ 다ìŒê³¼ ê°™ì´ {{{~cpp class Asset: public HeapTracked { private: UPNumber value; ... }; }}} ì´ì œ 우리는 Asset* í¬ì¸í„°ì— 관해서 ê°ì²´ì˜ ìƒíƒœë¥¼ 물어 볼수 있다. {{{~cpp void inventoryAsset(const Asset *ap) { if (ap->isOnHeap()) { ap 는 heap-based asset ì´ë‹¤. } else { ap 는 non-heap-based asset ì´ë‹¤. } } }}} ì´ mixin ê°ì²´ì˜ 단ì ì´ë¼ë©´ int나 charë”°ìœ„ì˜ built-in 형ì—는 ì¨ë¨¹ì§€ë¥¼ 못하는 것ì´ë‹¤. ì´ê²ƒë“¤ì€ ìƒì†ì„ í• ìˆ˜ 없기 때문ì´ë‹¤. === Prohibiting Heap-Based Objects : Heapì˜ì—ì— ê°ì²´ 올리기 막기 === 기나긴 ì—¬ì •ì´ë‹¤. ì´ì œ ê¸€ì˜ ë§‰ë°”ì§€ì¸ ì£¼ì œë¡œ 왔다. 여태한 것과 반대로 Heapì˜ì—ì— ê°ì²´ë¥¼ ì˜¬ë¦¬ëŠ”ã„³ì„ ë§‰ì„ë ¤ë©´ 어떻게 해야 í• ê¹Œ? ì´ë²ˆì—는 ì´ëŸ° ë°˜ëŒ€ì˜ ê²½ìš°ì— ê´€í•´ì„œ ìƒê°í•´ 본다. ê³§ 바로 ì˜ˆì œë¥¼ ë³´ìž. {{{~cpp class UPNumber { private: static void *operator new(size_t size); // ìž ì´ë ‡ê²Œ operator new를 // 사ì—(private)ì¸ìžë¡œ 만들어 버린다. static void operator delete(void *ptr); // operator delete ì—시 마찬가지ì´ë‹¤. ... }; }}} í´ëž˜ì´ì–¸íЏì—서 ë°œìƒí•˜ëŠ” ê²ƒì— ê´€í•´ ê°€ì •í•˜ìž. {{{~cpp UPNumber n1; // 맞다. static UPNumber n2; // ì´ìƒ 없다. UPNumber *p = new UPNumber; // 사ì—(private)ì¸ìžì¸ opreator newê°€ ë¶ˆë ¤ì„œ // ì—러가 ë°œìƒí•œë‹¤. }}} 26장 부터 꾸준히 잘 ë´ì™”다면 보는 순간 ì´í•´ê°€ëŠ” ì˜ˆì œì´ë‹¤. ê·¸ë¦¬ê³ ì˜ë¬¸ê¹Œì§€ ì œê¸°ëœë‹¤. operator new[]는 ì–´ë”” 있는가? 즉, heap ì˜ì—ì— ë°°ì—´ ì„ ì–¸ê¹Œì§€ 막기위해서는 operator new[]까지 사ì—(private)ì¸ìžë¡œ ì„ ì–¸í•´ 주면 ëœë‹¤. (주:분명히 다른 ì£¼ì œì¸ë° 지겹다. 너무 ê°™ì€ ê²ƒë§Œ 반복하는 ëŠë‚Œì´ ë“ ë‹¤.) í¥ë¯¸ë¡ê²Œ operator newì˜ ì‚¬ì—(private)ì¸ìžë¡œì˜ ì„ ì–¸ì€ UPNumberê°ì²´ë¥¼ ìƒì†í•œ ê°ì²´ì—ì„œë„ ê°™ì€ ëŠ¥ë ¥ì„ ë°œíœ˜í•œë‹¤ëŠ” ì ì´ë‹¤. 다ìŒì˜ ì˜ˆì œë¥¼ ë³´ìž. {{{~cpp class UPNumber { ... }; // 위와 ê°™ì€ ì½”ë“œ class NonNegativeUPNumber: // ì´ í´ëž˜ìФì—서는 operator newì—대한 public UPNumber { // ì„ ì–¸ì´ ì—†ë‹¤. ... }; NonNegativeUPNumber n1; // ì´ìƒ 없다. static NonNegativeUPNumber n2; // ì—시 ì´ìƒ 없다. NonNegativeUPNumber *p = // 사ì—(private)ì¸ìžì¸ opreator newê°€ ë¶ˆë ¤ì„œ new NonNegativeUPNumber; // ì—러가 ë°œìƒí•œë‹¤. }}} 마지막 new를 ì“´ 부분ì—서는 NonNegativeUPNumber를 new로 ìƒì„±í• 때 ë¶€ëª¨ì¸ UPNumberë¶€ë¶„ì„ ìƒì„±í• 때 private로 ì œì•½ì´ ë°œìƒë˜ì–´ì„œ ì—러가 ë°œìƒí•˜ëŠ” 것ì´ë‹¤. ë°˜ë©´ì— ìœ„ì˜ ì½”ë“œê°€ 그대로ë¼ë©´, 다ìŒê³¼ ê°™ì€ ì½”ë“œ ê°™ì´ has-aê´€ê³„ì˜ ê²½ìš°ì—는 가능하다. {{{~cpp class Asset { public: Asset(int initValue); ... private: UPNumber value; }; Asset *pa = new Asset(100); // 맞다. // ì´ê²½ìš° Asset::operator new 나 // ::operator new는 불리지만 // UPNumber::operator new는 불리지 않으므로 올바르게 ìž‘ë™í•œë‹¤. }}} ë’·ë¶€ë¶„ì€ ì•žì— í–ˆë“ heapì˜ì—ì˜ ì£¼ì†Œ ê¸°ë°˜ì˜ ê²€ì¶œê³¼ ì´ì‹ì„±ì— 대한 ë…¼ì˜ì´ë‹¤. == Item 28: Smart pointers == * Item 28: 똑똑한 í¬ì¸í„°:스마트 í¬ì¸í„° ''Smart pointer''(ì´í•˜ 스마트 í¬ì¸í„°)는 ê°ì²´ì˜ 수월한 관리를 위해서 태어난 방법ì´ë‹¤. "방법" ì´ë¼ê³ í‘œí˜„í•œê²ƒì„ ì£¼ì˜í•˜ë¼, 특별히 ì •í•´ì§„ 것ì´ì•„니ë¼. ì œì‹œëœ ë°©ë²•ë¡ ì„ ë°”íƒ•ìœ¼ë¡œ, 누구나 êµ¬í˜„í• ìˆ˜ 있다. ì´ì „ 내용중 잘 등장하는 auto_ptr STLë„ ìŠ¤ë§ˆíŠ¸ í¬ì¸í„°ì˜ ì•„ì´ë””어를 구현한 것ì´ë‹¤. C++ì˜ built-in í¬ì¸í„°ë¡œì„œ(다시 ë§í•´ '''dumb'''(ê°€ìƒì •ë„로 í•´ì„, ì´í•˜ "ë”미"로 표현) í¬ì¸í„°) 스마트 í¬ì¸í„° 사용한다. ì´ëŸ´ 경우 앞으로 다를 다ìŒê³¼ ê°™ì€ ê²½ìš°ë“¤ì˜ ë¬¸ì œë“¤ì´ ë…¼ì˜ ëœë‹¤. (작성ìžì£¼:dumb pointer를 ë¤ í¬ì¸í„° ë¼ê³ 부르지 ì•Šê³ ë”미(dummy) í¬ì¸í„°ë¼ê³ 부르는건 ì˜ë¯¸ 호ë„지만, ëŠ¥ë ¥ì„ ìƒì‹¤í•œ 가짜 í¬ì¸í„°ì˜ ì˜ë¯¸ë¡œ 사용한다. 특히 Evangelionì—서 ë”미 ë¼ëŠ” í‘œí˜„ì´ ê´œì°®ì€ ì–´ê°ì´ê¸°ì— 차용했다는 후문ì´.. ) * Construction and destruction.(ìƒì„±ê³¼ 파괴) 스마트 í¬ì¸í„°ëŠ” ìžë™ìœ¼ë¡œ 0 or nullì˜ ì´ˆê¸°í™”ë¡œ 미 초기화로 ì¸í•œ ë¬¸ì œë¥¼ ë°©ì§€í•˜ê³ , 파괴시ì—ë„ built-in íƒ€ìž…ì˜ íŠ¹ì„±ìœ¼ë¡œ ìžë™ìœ¼ë¡œ 파괴 시켜준다. * Copying and assignment. (복사와 í• ë‹¹) 스마트 í¬ì¸í„°ì—서 가장 심ê°í•œ ì œì•½ 사í•ì„ ì¼ìœ¼í‚¤ëŠ” ê²ƒì´ ë³µì‚¬ì™€ í• ë‹¹ ë¬¸ì œ ì¸ë°(차후 ë…¼ì˜ëœë‹¤.) ì´ì— 대한 ì´ìœ 와, 옳바른 ë°©í–¥ì˜ ì œì‹œë¥¼ 한다. * Dereferencing. (ì—참조) í´ë¼ì´ì–¸íŠ¸ëŠ” 스마트 í¬ì¸í„°ê°€ 가진 í´ëž˜ìŠ¤ë¥¼ '''ì–´ë–¤ 때''' '''어떻게''' 참조를 해야 í• ê¹Œ? 예를들어서 lazy fetching(Item 17ì°¸ê³ )를 스마트 í¬ì¸í„° 구현하는 것과 ê°™ì´ ì—¬ëŸ¬ ê²½ìš°ì— ëŒ€í•œ ë¬¸ì œë¥¼ ìƒê°í•˜ìž. 스마트 í¬ì¸í„°ëŠ” built-in í¬ì¸í„°ì™€ ê°™ì´ ê°•í•œ 형 ì•ˆì •ì„±ì„ ê°€ì ¸ì•¼ 하기 ë–„ë¬¸ì— template로 구현ëœë‹¤. 대다수 스마트 í¬ì¸í„°ì˜ ëª¨ìŠµì€ ë‹¤ìŒê³¼ ê°™ì´ ë³´ì—¬ì§„ë‹¤.: {{{~cpp template<class T> // 스마트 í¬ì¸í„°ë¥¼ 위한 템플릿 class SmartPtr { // í¬ì¸í„° ê°ì²´ public: SmartPtr(T* realPtr = 0); // 스마트 í¬ì¸í„°ë¥¼ 주어진 ë”미(dumb) í¬ì¸í„°ë¡œ // 초기화 시키는ë°, í• ë‹¹ë˜ì§€ 않으면 // ë”미(dumb)í¬ì¸í„°ë¥¼ 0(null)로 초기화 시킨다. SmartPtr(const SmartPtr& rhs); // 스마트 í¬ì¸í„° 복사 ìƒì„±ìž ~SmartPtr(); // 스마트 í¬ì¸í„°ì˜ íŒŒê´´ìž // 스마트 í¬ì¸í„°ì˜ ê°’ì„ í• ë‹¹í•œë‹¤. SmartPtr& operator=(const SmartPtr& rhs); T* operator->() const; // 참조 풀기:ìžë£Œì— ì ‘ê·¼í•˜ë„ë¡ í¬ì¸í„°ë¥¼ ì œê³µí•œë‹¤. T& operator*() const; // 참조 풀기:마찬가지 private: T *pointee; // 현재 스마트 í¬ì¸í„°ê°€ ê°€ë¦¬í‚¤ê³ ìžˆëŠ” 것 // ì´ê²ƒì´ ì§ì ‘ ì ‘ê·¼ 못하는 ë”미(dumb) í¬ì¸í„° ì´ë‹¤. }; }}} 현재 복사 ìƒì„±ìžì™€(copy constructor), í• ë‹¹ ì—°ì‚°ìž(assignment operator)ê°€ 특별히 ì„ ì–¸ë˜ì–´ 있지 않으므로, 둘다 ê³µì—(public)ì¸ìžì´ë‹¤. ë¬¸ì œëŠ” 함부로 ì´ë¥¼ ì‚¬ìš©í• ê²½ìš° 가리키는 ë”미(dumb)í¬ì¸í„°ê°€ 같아 지므로 built-in 형으로 ìƒì„±ë˜ëŠ” 스마트 í¬ì¸í„° ë‘˜ì´ ì‚¬ë¼ì§€ëŠ” ì˜ì—ì„ ë²—ì–´ë‚˜ëŠ” 시ì ì— ê°™ì€ ê°ì²´ë¥¼ delete í•˜ë ¤ê³ í• ìˆ˜ 있다. ì´ì— 대한 ë³´ì™„ì´ í•„ìš”í•˜ë‹¤. ê·¸ë¦¬ê³ , 스마트 í¬ì¸í„°ë¥¼ ìžì„¸ížˆ ë‹¤ë£¨ê¸°ì— ì•žì„œì„œ, 스마트 í¬ì¸í„°ê°€ 작성ë˜ëŠ” í•˜ë‚˜ì˜ ê°€ìƒ ì‹œë‚˜ë¦¬ì˜¤ë¥¼ 작성한다. ì´ìœ 는 ì ì°¨ 스마트 í¬ì¸í„°ì— 관한 ìžì„¸í•œ ì„¤ëª…ì´ í•„ìš”í• ë•Œ, ì´ ê°€ìƒ ì‹œë‚˜ë¦¬ì˜¤ìƒì—서 ë°œìƒí•˜ê±°ë‚˜, 해당 문법ì—서 ê°ì•ˆí•´ì•¼ í• ìš”ì¸ë“¤ì„ 논하기 위해 ì‹œë‚˜ë¦¬ì˜¤ì˜ ì „ì²´ 설계를 ì œì‹œí•œë‹¤. ì„¤ì •ë 시나리오는 ë¶„ì‚° 시스템ìƒ, ë¶„ì‚° DBì—서, local ì— ìžˆëŠ” ê°ì²´ì™€, remoteì— ìžˆëŠ” ê°ì²´ë¥¼ 다루기 ì´ë‹¤. í´ë¼ì´ì–¸íЏ 입장ì—서는 ë‘˜ì„ ë‹¤ë£¨ëŠ” ë°©ë²•ì´ ì´ì›í™” ë˜ì–´ 있어서, 해당 ê°ì²´ì˜ í”„ë¡œì‹œì € í˜¸ì¶œì— ì¼ê´€ì„±ì´ ì—†ì„ ê²½ìš° í”„ë¡œê·¸ëž˜ë° í™˜ê²½ì´ ë¶ˆíŽ¸í•˜ê³ , ëª…ì‹œì„±ì´ ë–¨ì–´ì§€ëŠ” 등 여러 불리한 ì ì´ ìžˆë‹¤. 그래서 아예 ì–‘ìª½ì˜ ê°ì²´ì— ì‚¬ìš©ë²•ì— ëŒ€í•œ ì¼ê´€ì„±ì„ ê°€ì§€ê³ ì‹¶ì–´ 한다. ì´ë¥¼ í•´ê²° 하기 위해서 스마트 í¬ì¸í„°ë¡œ, 해당 ê°ì²´ë¥¼ 둘러싸서, í”„ë¡œì‹œì €ì˜ ì¼ê´€ëœ 사용 ë°©ë²•ì„ ì œê³µ ë°›ê³ ìž í•œë‹¤. 다ìŒì€ ê·¸ê²ƒì„ êµ¬í˜„í•œ 코드들ì´ë‹¤.: {{{~cpp template<class T> // ë¶„ì‚° DBìƒì— ê°ì²´ë¥¼ 가리키는 스마트 í¬ì¸í„° class DBPtr { // 템플릿 public: DBPtr(T *realPtr = 0); // DBìƒì˜ DBê°ì²´ë¥¼ 가리키는 ë”미 í¬ì¸í„°ë¥¼ 바타ì‘로 // 스마트 í¬ì¸í„°ë¥¼ ìƒì„±í•œë‹¤. DBPtr(DataBaseID id); // DB ê°ì²´ì—서 DB ID를 바탕으로 스마트 í¬ì¸í„°ë¥¼ // ìƒì„±í•œë‹¤. ... // DB를 위한 다른 작업들 }; class Tuple { // ë°ì´í„° 페ì´ìŠ¤ì˜ tuple를 위한 í´ëž˜ìФ public: ... void displayEditDialog(); // tupleì˜ ìˆ˜ì •ì„ í•˜ê¸° 위한 ìœ ì €ì˜ // 그래픽 ì¸í„°íŽ˜ì´ìФ(ìˆ˜ì •ì´ í—ˆë½ë˜ëŠ” 한ì—서) bool isValid() const; // *this ë°˜í™˜ì˜ ìœ íš¨ì„± ì ê²€ }; // Tê°ì²´ê°€ ìˆ˜ì •ë˜ì–´ 해당 log를 남기는 ì—í• ì„ í•˜ëŠ” í´ëž˜ìФ 템플릿 template<class T> class LogEntry { public: LogEntry(const T& objectToBeModified); ~LogEntry(); }; void editTuple(DBPtr<Tuple>& pt) { LogEntry<Tuple> entry(*pt); // ìˆ˜ì •ì„ ìœ„í•œ 해당 log 엔트리를 작성한다. // ìžì„¸í•œ ì„¤ëª…ì€ ì•„ëž˜ // ìœ íš¨í•œ ê°’ì´ ì£¼ì–´ì§€ê¸° 까지 ìˆ˜ì • dialog를 뛰우기 위한 요구를 계ì†í•œë‹¤. do { pt->displayEditDialog(); } while (pt->isValid() == false); } }}} editTupleì˜ ë‚´ë¶€ì—서 ìˆ˜ì •ë˜ì–´ì§€ëŠ” tupleì€ ì•„ë§ˆë„ ì›ê²©(remote)ì§€ì˜ ê¸°ê³„ì— ì¡´ìž¬í•˜ëŠ” ê°ì²´ì´ë‹¤. 하지만, editTupleì„ ì‚¬ìš©í•˜ëŠ” 프로그래머는 ì´ëŸ¬í•œ 사í•ì— ëŒ€í•˜ì—¬ 스마트 í¬ì¸í„° ë•Œë¬¸ì— íŠ¹ë³„ížˆ ìƒê´€í• 필요가 없다. editTupleë‚´ì— LogEntryê°ì²´ë¥¼ ìƒê°í•´ ë³´ìž. ìˆ˜ì •ì„ ìœ„í•œ log를 남기기위해서는 displayEditDialogì˜ ì‹œìž‘ê³¼ ëì—서 매번 불러주면 ë˜ëŠ”ë°, 구지 왜 구지 ì´ë ‡ê²Œ í–ˆì„까? ì´ì—관한 ë‚´ìš©ì€ ì˜ˆì™¸ì— ê´€ë ¨ëœ ìƒí™© 때문ì¸ë°, Item 9를 ì°¸ê³ í•˜ë©´ ëœë‹¤. ë‹¹ì‹ ì´ ë³¼ìˆ˜ 있는 것처럼 스마트 í¬ì¸í„°ëŠ” ë”미(dumb)í¬ì¸í„°ë¥¼ 그냥 사용하는 것과 í¬ê²Œ ì°¨ì´ì ì€ ì—†ì–´ ë³´ì¸ë‹¤. ê·¸ê²ƒì€ ìº¡ìŠí™”ì— ëŒ€í•œ íš¨ìœ¨ì„±ì„ ë…¼í•˜ëŠ” 것과 같다. 스마트 í¬ì¸í„°ë¥¼ 사용하는 í´ë¼ì´ì–¸íŠ¸ëŠ” 그냥 ë”미(dumb)í¬ì¸í„°ë¥¼ 사용하는 것처럼 사용하므로서, 스마트 í¬ì¸í„°ê°€ ì–´ë–¤ ì¼ì„ 행하는지 특별히 ê´€ì‹¬ì„ ìŸì„ 필요가 없게 만드는 ê²ƒì´ ê´€ê±´ì´ë‹¤. === Construction, Assignment and Destruction of Smart Pointers : 스마트 í¬ì¸í„°ì˜ ìƒì„±, í• ë‹¹, 파괴 === * ìƒì„±ìž:Constructor ê´€ë ¨ 스마트 í¬ì¸í„°ì˜ ìƒì„±ì€ 특별히 ì‹ ê²½ ì“¸ê²ƒì´ ì—†ë‹¤. 초기화 ë˜ì§€ 않았따면 스마트 í¬ì¸í„°ì˜ ë‚´ë¶€ì˜ ìžˆëŠ” ë”미(dumb)í¬ì¸í„°ëŠ” null로 세팅 ë˜ë¯€ë¡œ, 차후 파괴시ì—ë„ ë³„ ë¬¸ì œ ë ê²ƒì´ ì—†ë‹¤. * 복사 ìƒì„±ìž(copy Constructor), í• ë‹¹ ì—°ì‚°ìž (assignment operator) ê´€ë ¨ 스마트 í¬ì¸í„°ì˜ ì ìš©ì—서 ë¬¸ì œì‹œ ë˜ëŠ” ê²ƒì´ ë³µì‚¬ ìƒì„±ìž(copy constructor), í• ë‹¹ ì—°ì‚°ìž(assignment operator), 파괴ìž(destuctor)ì´ë‹¤. ì´ë“¤ì—서 주요한 ë…¼ì ì´ ë˜ëŠ”ê²ƒì€ ownership 즉 ì†Œìœ ê¶Œì˜ ë¬¸ì œì´ë‹¤. ì†Œìœ ê¶Œì— ê´€í•œ ë¬¸ì œëŠ” ì´ ì•„ì´í…œ ì „ë°˜ì—서 다루는 ì£¼ì œì´ê³ , 해결법과 ê·¸ì— ëŒ€í•œ ê²°ì ì´ ë°˜ë³µë˜ëŠ” ì‹ìœ¼ë¡œ ì§„í–‰ 한다. auto_ptr í…œí”Œë¦¿ì— ê´€í•´ì„œ ìƒê°í•´ ë³´ìž. Item 9ì—서 설명한 것과 ê°™ì´ auto_ptrì€ heap-based ê°ì²´ë¥¼ auto_ptrì´ íŒŒê´´ ë 때까지 가리키는 ì—í• ì„ í•œë‹¤. 만약 auto_ptrì´ íŒŒê´´ë˜ì–´ì§€ëŠ” 경우(존재 ì§€ì—ì„ ë²—ì–´ë‚ ë•Œ) 가리키는 ê°ì²´ëŠ” 파괴ë˜ì–´ì§„다. auto_ptrì€ ë‹¤ìŒê³¼ ê°™ì´ êµ¬í˜„ë˜ì–´ 있다. {{{~cpp template<class T> class auto_ptr { public: auto_ptr(T *ptr = 0): pointee(ptr) {} ~auto_ptr() { delete pointee; } // 가리키는 ê°ì²´ë¥¼ 파괴한다. ... private: T *pointee; }; }}} ì´ë ‡ê²Œ 구현ë˜ì–´ 있는 ìƒí™©ì—서, 만약 auto_ptrì„ ë³µì‚¬(copy)하거나 í• ë‹¹(assign)하면 어떨게 ë 까? 다ìŒê³¼ ê°™ì´ ë§ì´ë‹¤. {{{~cpp auto_ptr<TreeNode> ptn1(new TreeNode); auto_ptr<TreeNode> ptn2 = ptn1; // 복사 ìƒì„±ìžë¥¼ 호출했다. ì–´ë–¤ ì¼ì´ ì¼ì–´ë‚ 까? auto_ptr<TreeNode> ptn3; ptn3 = ptn2; // operator=를 호출했다. ì–´ë–¤ ì¼ì´ ì¼ì–´ë‚ 까? }}} ì´ ë‘˜(copy, assign)ì˜ ì˜ë¯¸ê°€, 만약 ë‚´ë¶€ì˜ ë”미(dumb) í¬ì¸í„°ë¥¼ 복사 하였다면, 해당 ë‘ê°œì˜ auto_ptrì€ ê°™ì€ ê°ì²´ë¥¼ ê°€ë¦¬í‚¤ê³ ìžˆì„ ê²ƒì´ë‹¤. ê·¸ë ‡ë‹¤ë©´, ì°¨í›„ì— auto_ptrì´ íŒŒê´´ë˜ëŠ” 시ì ì—서 í•˜ë‚˜ì˜ ê°ì²´ë¥¼ ì—°ì†ìœ¼ë¡œ ë‘번 íŒŒê´´í•˜ë ¤ê³ ì‹œë„ í• ê²ƒì´ë‹¤. ì´ê²ƒì€ 잘못 ëœê²ƒì´ë‹¤. ì´ë ‡ê²Œ ì œê¸°ë˜ëŠ” ë¬¸ì œì˜ ëŒ€ì•ˆìœ¼ë¡œ ê°€ë¦¬í‚¤ê³ ìžˆëŠ” ê°ì²´ì— 대하여 복사를 ìˆ˜í–‰í•œë’¤ì— ê°€ë¦¬í‚¤ë„ë¡ ë§Œë“¤ìˆ˜ 있다. 하지만 ì´ëŸ° 구현 ìƒíƒœëŠ” ìžì¹«, ë§Žì€ copy, assignì˜ ë‚¨ë°œì‹œì— new와 deleteì˜ ë‹¤ëŸ‰ì˜ í˜¸ì¶œë¡œ, ì†ë„ ì €í•˜ì™€, "ê°™ì€ ê²ƒì„ ê°™ì€ ê²ƒì„ ê°€ë¦¬í‚¤ê³ ìžˆë‹¤." ë¼ëŠ” ì˜ë¯¸ë¡œ ì“´ 프로그래머ì—게 ì˜ë¯¸ë¥¼ í˜¸ë„ ì‹œì¼œë²„ë¦´ìˆ˜ 있다. ì´ëŸ° ë¬¸ì œëŠ” auto_ptrì´ ë§Œì•½ 복사(copying)와 í• ë‹¹(assignment)를 하지 않는다면, 근본ì 으로 ì œê±° ë 수 있다. 하지만 auto_ptr í´ëž˜ìŠ¤ë¥¼ 간단히 조작해서 ë¬¸ì œë¥¼ 피해본다. 위ì—서 언급한것과 ê°™ì´ ê²‰ìœ¼ë¡œëŠ” 복사ì´ì§€ë§Œ, ë‚´ë¶€ì 으로는 ì†Œìœ ê¶Œ(ownership)를 넘기는 ìž‘ì—…ì„ í•˜ë„ë¡ êµ¬ì„±í•˜ëŠ” 것ì´ë‹¤. 코드는 다ìŒê³¼ 같다. {{{~cpp template<class T> class auto_ptr { public: ... auto_ptr(auto_ptr<T>& rhs); // 복사 ìƒì„±ìž auto_ptr<T>& operator=(auto_ptr<T>& rhs); // í• ë‹¹(assignment) ì—°ì‚°ìž ... }; template<class T> auto_ptr<T>::auto_ptr(auto_ptr<T>& rhs) { pointee = rhs.pointee; // ì†Œìœ ê¶Œ(ownership)ì„ ì´ì–‘하는 작업 rhs.pointee = 0; // rhsì˜ ì†Œìœ ê¶Œ(ownership)ì„ ë°•íƒˆí•œë‹¤. } template<class T> auto_ptr<T>& auto_ptr<T>::operator=(auto_ptr<T>& rhs) { if (this == &rhs) // ê°™ì€ ê°ì²´ë¥¼ 가리키는건 1=1 ê°™ì€ ê²½ìš°ë°–ì— // 없다. 그러므로 아무런 ìž‘ì—…ì„ í•˜ì§€ 않는다. delete pointee; // 해당 ë”미(dumb) í¬ì¸í„°ê°€ 가리키는 ê°ì²´ë¥¼ // ì‚ì œí•œë‹¤. pointee = rhs.pointee; // ì†Œìœ ê¶Œ(ownership)ì„ ì´ì–‘한다. rhs.pointee = 0; // rhsì˜ ì†Œìœ ê¶Œ(ownership)ì„ ë°•íƒˆí•œë‹¤. return *this; } }}} ì´ë ‡ê²Œ 복사 ìƒì„±ìž(copy constructor)ê°€ 불리거나 í• ë‹¹ ì—°ì‚°ìž(assignment operator)를 í˜¸ì¶œí• ê²½ìš° ì†Œìœ ê¶Œ(ownership)ì„ ì´ì–‘한다. 만약 ì´ëŸ° ìž‘ì—…ì´ ì‹¤íŒ¨í•˜ë©´, ê²°ì½” ê°ì²´ëŠ” 지워질수가 없다. 하지만 ì´ëŸ° ë°©ì‹ì€ 치명ì ì¸ ê²°ê³¼ë¥¼ 불러 올수 있다. ê°ì²´ì˜ ì†Œìœ ê¶Œ(ownership)ì´ auto_ptrì˜ ë³µì‚¬ ìƒì„±ìžì— ì˜í•˜ì—¬ ì´ì–‘ë˜ë¯€ë¡œ, 만약 auto_ptrì„ ê°’ì— ì˜í•œ ì „ë‹¬(by value)로 넘겨 버리면 ëŒì•„올수 없는 ê°•ì„ ê±´ë„ˆëŠ” ê¼´ì´ ëœë‹¤. {{{~cpp // ê°’ì— ì˜í•œ ì „ë‹¬(by value)로 auto_ptrì„ ë„˜ê¸´ë‹¤. void printTreeNode(ostream& s, auto_ptr<TreeNode> p) { s << *p; } int main() { auto_ptr<TreeNode> ptn(new TreeNode); ... printTreeNode(cout, ptn); // ê°’ì— ì˜í•œ ì „ë‹¬(pass auto_ptr by value) ... } }}} printTreeNodeì—게 auto_ptr<TreeNode> í˜•ì¸ ptnì´ ê°’ì— ì˜í•œ ì „ë‹¬(by-value)로 ì „ë‹¬ ë˜ë¯€ë¡œ, printTreeNodeê°€ 수행ë˜ê¸°ì „ì— ìž„ì‹œ ê°ì²´ê°€ ìƒì„±ë˜ê³ ê±°ê¸°ì— ptnì´ ë³µì œ ëœë‹¤. ì´ë•Œ 불리는 복사 ìƒì„±ìž(copy constructor)는 ê°€ì§€ê³ ìžˆëŠ” ë”미(dumb) í¬ì¸í„°ë¥¼ 넘기는 ì†Œìœ ê¶Œ(ownership) ì´ì–‘ ìž‘ì—…ì„ í•˜ê²Œë˜ê³ , printerTreeNodeê°€ ë§ˆì¹œë’¤ì— í•´ë‹¹ ê°ì²´ëŠ” ì‚ì œë˜ê³ , ptnì€ 0(null)로 초기화 ë˜ì–´ 진다. auto_ptrì„ ìžƒì–´ 버린 것ì´ë‹¤. ê·¸ë ‡ë‹¤ë©´ ê°’ìœ¼ë¡œì˜ ì „ë‹¬ì´ ì•„ë‹Œ 다른 방법ì´ë¼ë©´? ìƒìˆ˜ 참조 ì „ë‹¬(Pass-by-reference-to-const)로 한다면 ë˜ì§€ 않ì„까? 구현 코드를 ë³´ìž {{{~cpp // ì§ê´€ì ì¸ ì˜ˆì œì´ë‹¤. auto_ptr<TreeNode>를 ìƒìˆ˜ 참조 ì „ë‹¬(Pass-by-reference-to-const)하였다. void printTreeNode(ostream& s, const auto_ptr<TreeNode>& p) { s << *p; } }}} 해당 ì˜ˆì œì˜ ìƒìˆ˜ 참조 ì „ë‹¬(Pass-by-reference-to-const)ì˜ ì¸ìž p는 ê°ì²´ê°€ 아니다. ê·¸ê²ƒì€ ê·¸ëƒ¥ ì°¸ì¡°ì¼ ë¿ì´ë‹¤. 그래서 아무런 ìƒì„±ìžê°€ 불리지 않는다. 그래서 ì†Œìœ ê¶Œ(ownership)ì€ ë„˜ê¸´ ì¸ìžì— 그대로 남아 있으며 ê°’ìœ¼ë¡œì˜ ì „ë‹¬(by-value)ì‹œì— ì¼ì–´ë‚˜ëŠ” ë¬¸ì œë¥¼ 막ì„수 있다. ì†Œìœ ê¶Œ(ownership)ì„ ì „ë‹¬í•˜ëŠ” ê°œë…ì„ ë‹¤ë£¬ê±´ ì°¸ í¥ë¯¸ë¡ì§€ë§Œ, 사용ìžëŠ” ê²°ì½” 재미있지 ì•Šë‹¤ê³ ìƒê°ëœë‹¤. ì´ëŠ” 복사(copy)나 í• ë‹¹(assign)ì˜ ì›ëž˜ì˜ ì˜ë¯¸ë¥¼ ì†ìƒ 시키는 ë©´ì´ë¼ì„œ, 프로그래머는 혼란스러울 ë¿ì´ë‹¤. 개다가 í•¨ìˆ˜ë“¤ì´ ëª¨ë‘ ìƒìˆ˜ 참조 ì „ë‹¬(Pass-by-reference-to-const)로 작성ëœë‹¤ëŠ” ë²•ë„ ì—†ë‹¤. 즉, ì“°ì§€ ë§ë¼ëŠ” 소리다. auto_ptrì— ê´€ë ¨í•œ ë‚´ìš©ì€ ì´ ì±…ì˜ í›„ë°˜ì— ë‹¤ë£¨ì–´ 있다.(ì •í™•ížˆ 291-294쪽) ì´ë²ˆìž¥ ì—¬ê¸°ì— êµ¬í˜„ëœ ì½”ë“œë“¤ì´ auto_ptrì˜ 100% 코드가 아니므로, STLì˜ auto_ptrì„ ì‚¬ìš©í•˜ê¸° 위해서는 ê¼ ë³´ê¸°ë¥¼ 바란다. ìœ„ì— ì‚¬ì‹¤ ë°‘ë§ê³ ë§ì´ë‹¤. * 파괴ìž(destuctor)ê´€ë ¨ ê·¸ë¦¬ê³ ëŒ€ê²Œì˜ ìŠ¤ë§ˆíŠ¸ í¬ì¸í„°ì˜ 파괴ìžëŠ” 다ìŒê³¼ ê°™ì´ ê°„ë‹¨ížˆ 구현ë˜ì–´ 있다. {{{~cpp template<class T> SmartPtr<T>::~SmartPtr() { if (*this ê°€ ê°€ì§€ê³ ìžˆëŠ” *pointee) { delete pointee; } } }}} * 맺ìŒë§:ì°¨í›„ì— ìŠ¤ë§ˆíŠ¸ í¬ì¸í„°ëŠ” Reference Counting(Item 29)ì„ êµ¬í˜„í•˜ëŠ”ë° ì ìš© ëœë‹¤. 그때 다시 스마트 í¬ì¸í„°ì— 대한 ì“°ìž„ì— ëŒ€í•˜ì—¬ ëŠê»´ 보기를 바란다. === Implementing the Dereferencing Operators : ì—참조(Dereferencing) ì—°ì‚°ìžì˜ ì ìš© === * 작성ìžì£¼:Dereferenceë¼ëŠ” í‘œí˜„ì€ ìŠ¤ë§ˆíŠ¸ í¬ì¸í„°ë¥¼ ì‚¬ìš©ì‹œì— ë‚´ë¶€ì— ê°€ì§€ê³ ìžˆëŠ” ë”미(dumb) í¬ì¸í„°ë¥¼ 통해서 해당 ê°ì²´ì— ì ‘ê·¼í•˜ê±°ë‚˜, ê·¸ ê°ì²´ì˜ 참조를 통해서 ì ‘ê·¼í•˜ëŠ” ê³¼ì •ì„ ì˜ë¯¸í•œë‹¤. ì´ê²ƒì„ Referenceì˜ ë°˜ëŒ€ ì˜ë¯¸ë¡œ Dereference로 쓴다. ì‚¬ì „ì—는 ë¬¼ë¡ ì—†ëŠ” ì˜ë¯¸ì´ë‹¤. 앞으로 여기ì—ì„œë„ '''ì—참조'''ë¼ê³ 표현한다. ì—참조(Dereference)를 하는 ì—°ì‚°ìžëŠ” operator*와 operator-> ì´ë‹¤. ê°œë…ì ì¸ êµ¬ì„±ì„ ë³´ìž. {{{~cpp template<class T> T& SmartPtr<T>::operator*() const { "스마트 í¬ì¸í„°"(똑똑한 í¬ì¸í„° 기능?)를 수행한다.; return *pointee; } }}} ì²˜ìŒ í•´ë‹¹ 함수는 초기화 하거나 pointeeê°€ ìœ íš¨í•˜ë„ë¡ ë§Œë“ ë‹¤. 예를 들어서 lazy fetchingì´ë¼ë©´(Item 17 ì°¸ê³ ) 해당 함수 ë‚´ì—서 pointeeì— í•´ë‹¹í•˜ëŠ” ê°ì²´ë¥¼ 만드는 ê³¼ì •ì„ ì¹˜ë¥¼ 것ì´ë‹¤. ì´ í•¨ìˆ˜ì˜ ë°˜í™˜ í˜•ì€ ì°¸ì¡°(reference)ì´ë‹¤. ê°ì²´ë¥¼ 반환하면 어떻게 ë˜ëŠ”ê°€? ë‚œë¦¬ë„ ì•„ë‹ê²ƒì´ë‹¤. ê°ì²´ì˜ ë°˜í™˜ì€ ê°ì²´ê°€ 복사로 ì „ë‹¬ì‹œì— ìƒì„±, ì‚ì œì—ì„œì˜ ë¹„íš¨ìœ¨ì€ ë¬¼ë¡ , Item 13ì— ì œì‹œë˜ëŠ” slicingì„ ë°œìƒí• ê°€ëŠ¥ì„±ì´ ìžˆë‹¤. ë˜ Item 13ì—ì„œë„ ê³„ì† ë‹¤ë£¬ ê°€ìƒ í•¨ìˆ˜ë¥¼ í˜¸ì¶œí• ë•Œ ì—시 ê°ì²´ë¥¼ 통한 ë°˜í™˜ì€ ë¬¸ì œë¥¼ ë°œìƒì‹œí‚¨ë‹¤. Reference로 ì „ë‹¬í•´ë¼, ì´ê²ƒì˜ íš¨ìœ¨ì„±ì— ê´€í•´ì„œëŠ” ì´ë¯¸ ë‹¤ë£¨ì—ˆê¸°ì— ë” ë§í•˜ì§€ 않는다. 한가지 ë” ìƒê°í•´ ë³´ë©´ ë”미(dumb) í¬ì¸í„°ì¸ pointeeê°€ nullê°’ì¸ ê²½ìš°ì— ì°¸ì¡°ëŠ” 어떻게 ë 까? 안심해ë¼. ì´ìƒí™©ì€ C++ì˜ ì„¤ê³„ 단계ì—서 ì´ë¯¸ ì›í•˜ì§€ 않는 것ì´ë¼ ê°€ì • í–ˆê³ , 예외를 ë°œìƒ ì‹œí‚¤ë„ë¡ ì¡°ì¹˜í•´ ë‘었다. abort(중지)를 ì›í•˜ëŠ”ê°€? ê·¸ë ‡ê²Œ ëœë‹¤. ì—참조(dereference)ì˜ ë‹¤ë¥¸ ì—°ì‚°ìžì¸ operator->를 ìƒê°í•´ ë³´ìž. operator*ê³¼ 비슷한 ì—í• ì´ì§€ë§Œ ì´ê²ƒì€ ë”미(dumb) í¬ì¸í„° ê·¸ ìžì²´ë¥¼ 반환한다. ì•žì˜ ì˜ˆì œì¤‘, operator->ê°€ 등장한 ì›ê²© DB ì ‘ê·¼ì— ê´€í•œ ì˜ˆì œë¡œ ìƒê°í•´ 본다. {{{~cpp { LogEntry<Tuple> entry(*pt); do { pt->displayEditDialog(); } while (pt->isValid() == false); } }}} 그러니까. ë¶€ë¶„ì˜ êµ¬ë¬¸ì´ {{{~cpp pt->displayEditDialog(); }}} 컴파ì¼ì‹œ ì´ëŸ° ì˜ë¯¸ë¡œ êµì²´ë˜ëŠ” 것ì´ë¼ê³ ìƒê°í•˜ë©´ ëœë‹¤. {{{~cpp (pt.operator->())->displayEditDialog(); }}} ìƒê°í•´ ë³´ë©´ operator->ì˜ ì˜ë¯¸ëŠ” ë‘가지를 가질수 있다. 스마트 í¬ì¸í„° ê°ì²´ ìžì²´ì˜ í¬ì¸í„°ë¥¼ ì§€ì¹í•˜ëŠ” 다소 ë³´í†µì˜ ì˜ë¯¸ë¡œì„œ í¬ì¸í„°ì™€, 스마트 í¬ì¸í„° ê°ì²´ê°€ ê°€ì§€ê³ ìžˆëŠ” ë”미(dumb) í¬ì¸í„°ê°€ 그것ì¸ë°, 스마트 í¬ì¸í„°ì˜ 사용 ìš©ë„ìƒ ì‚¬ìš©ìžëŠ” ë”미(dumb)í¬ì¸í„°ë¥¼ 반환 받기를 ì›í• 것ì´ë‹¤. 그래서 operator->는 다ìŒê³¼ ê°™ì´ êµ¬í˜„ë˜ì–´ 있다. {{{~cpp template<class T> T* SmartPtr<T>::operator->() const { "스마트 í¬ì¸í„°"(똑똑한 í¬ì¸í„° 기능?)를 수행한다.; return pointee; } }}} 해당함수는 올바르게 ì „ë‹¬ë˜ê³ , ë”미(dumb) í¬ì¸í„°ê°€ 가리키는 ê°ì²´ì˜ ê°€ìƒ í•¨ìˆ˜ë“¤ë„ ì˜¬ë°”ë¥´ê²Œ ë§í¬ ë 것ì´ë‹¤. ë§Žì€ ì–´í”Œë¦¬ ì¼€ì´ì…˜ì—서 스마트 í¬ì¸í„°ì™€ ê°™ì€ ê¸°ë²•ì„ ì‚¬ìš©í•˜ë¯€ë¡œ 잘 알아 ë‘기를 바란다. 특히나 ê³„ì† í™ë³´í•´ì„œ ì§€ê²¹ê² ì§€ë§Œ Reference count쪽ì—서 나온다니까. 주목하기를. === Testing Smart Pointers for Nullness : 스마트 í¬ì¸í„°ì˜ ë”미(dumb)ìƒì˜ null 여부를 ì•Œê³ ì‹¶ì–´! === 우리는 스마트 í¬ì¸í„° 사용ì—서 필요한 ê²ƒë“¤ì¸ ìƒì„±(create), 파괴(destroy), 복사(copy), í• ë‹¹(assign), ì—참조(dereference)ì— ê´€í•´ì„œ ë…¼ì˜í–ˆë‹¤. ê·¸ë ‡ì§€ë§Œ 우리가 ì•„ì§ ì•ˆí•œê²ƒì´ ìžˆë‹¤. 바로 스마트 í¬ì–¸í„°ì˜ null여부를 ì•Œê³ ì‹¶ì€ê²ƒ 바로 그것ì´ë‹¤. ì§€ê¸ˆê¹Œì§€ì˜ ì§€ì‹ê¹Œì§€ 스마트 í¬ì¸í„°ë¥¼ êµ¬í˜„í–ˆë‹¤ê³ í•˜ê³ , 다ìŒì˜ 코드를 ì‹œì „(?) í•˜ê³ ìž í• ë•Œ {{{~cpp SmartPtr<TreeNode> ptn; ... if (ptn == 0) ... // ì—러! if (ptn) ... // ì—러! if (!ptn) ... // ì—러! }}} ì´ê²ƒ ì°¸ 무서운 ì¼ì´ì§€ 않ì€ê°€? isNull 함수를 추가하면 쉽게 í•´ê²° ë 것 같다. 하지만 ì´ê²ƒì€ 스마트 í¬ì¸í„°ê°€ ë”미(dumb) í¬ì¸í„°ì™€ ê°™ì´ ì“´ë‹¤ëŠ” ê°œë…ì— ë°˜í•˜ëŠ” 것ì´ë‹¤. 그래서 암시ì (implicit) 형변환으로 í•´ê²°í•´ 본다. {{{~cpp template<class T> class SmartPtr { public: ... operator void*(); // 스마트 í¬ì¸í„°ê°€ nullì´ë©´ 0를 ë°˜í™˜í•˜ê³ returns 0 if the smart ... // 아니면 0ê°€ 아닌 ê°’ì„ ë°˜í™˜í•œë‹¤. }; SmartPtr<TreeNode> ptn; ... if (ptn == 0) ... // 올바르다. if (ptn) ... // ì—시나 if (!ptn) ... // 좋왔어! ^^ }}} ì´ê²ƒì€ iostream í´ëž˜ìŠ¤ë“¤ì´ ì œê³µí•˜ëŠ” 형변환과 비슷하다. 코드는 다ìŒê³¼ 같다. {{{~cpp ifstream inputFile("datafile.dat"); if (inputFile) ... // inputFileì´ ìž˜ ì—´ë ¸ëŠ”ê°€ì— ê´€í•´ì„œ // 확ì¸í•´ 본다. }}} ëª¨ë“ í˜• 변환 함수와 마찬가지로 ì´ê²ƒë„ ì—시 ê²°ì ì´ ìžˆëŠ”ë°, 스마트 í¬ì¸í„°ê°„ì˜ ë¹„êµì—서 ë¬¸ì œê°€ ë°œìƒëœë‹¤. ë‹¤ìŒ ì˜ˆì œë¥¼ ë³´ìž. {{{~cpp SmartPtr<Apple> pa; SmartPtr<Orange> po; ... if (pa == po) ... // ì´ê²Œ 컴파ì¼ì´ ëœë‹¨ ë§ì´ë‹¤! }}} pa와 po는 ê°™ì€ ê²ƒì´ ì•„ë‹ˆì§€ë§Œ, 암시ì (implicit) í˜•ë³€í™˜ì— ì˜í•´ì„œ 아주 잘 참으로 ì»´íŒŒì¼ ëœë‹¤. ì´ê²ƒì€ 대다수 암시ì (implicit) í˜•ë³€í™˜ì˜ ë¬¸ì œì ì´ë‹¤.(ì•„ì§ë„ 난ê°í•˜ë©´ Item 5를 다시 ë³´ìž) void*ì˜ í˜•ë³€í™˜ì˜(conversion-to-void*) ëª¨í˜¸ì„±ì„ í”¼í•˜ê¸° 위해 const void* 형변환ì´ë‚˜, 다른 ì‚¬ëžŒë“¤ì€ bool í˜•ë³€í™˜ì„ ê¶Œí•œë‹¤. 하지만 ì´ëŸ° ë°©ë²•ë„ í˜•ë³€í™˜ë“¤ì„ ì„žì–´ 쓰면서 비êµí•˜ë©´ì„œ(mixed-type comparisons) ë°œìƒí•˜ëŠ” ë¬¸ì œ ì—시 í•´ê²°í• ìˆ˜ 없다. 한 발작 물러서서, í˜•ë³€í™˜ì´ ì•„ë‹Œ operator! 경우를 ìƒê°í•´ ë³´ìž. operator!를 overload시켜 스마트 í¬ì¸í„°ê°€ nullì¸ê²½ìš° true를 ë°˜í™˜í•œë‹¤ê³ í•´ë³´ìž. {{{~cpp template<class T> class SmartPtr { public: ... bool operator!() const; // 스마트 í¬ì¸í„°ê°€ nullì¼ë•Œ returns true if and only ... // if the smart ptr is null }; }}} í´ë¼ì´ì–¸íŠ¸ë“¤ì€ ì´ë ‡ê²Œ ì ìš©í• ê²ƒì´ë‹¤. {{{~c