U E D R , A S I H C RSS

More EffectiveC++/Techniques2of3

1. Item 29: Reference counting

  • Item 29: μ°Έμ‘° μ„ΈκΈ°

1.1. μ†Œκ°œ

Reference counting(μ΄ν•˜ μ°Έμ‘° μ„ΈκΈ°, 단어가 κΈΈμ–΄ μ˜μ–΄ 혼용 ν•˜μ§€ μ•ŠμŒ)λŠ” 같은 κ°’μœΌλ‘œ ν‘œν˜„λ˜λŠ” μˆ˜λ§Žμ€ 객체듀을 ν•˜λ‚˜μ˜ κ°’μœΌλ‘œ κ³΅μœ ν•΄μ„œ ν‘œν˜„ν•˜λŠ” κΈ°μˆ μ΄λ‹€. μ°Έμ‘° μ„ΈκΈ°λŠ” λ‘κ°€μ§€μ˜ 일반적인 λ™κΈ°λ‘œ μ œμ•ˆλ˜μ—ˆλŠ”λ°, 첫번째둜 heap 객체듀을 μˆ˜μš©ν•˜κΈ° μœ„ν•œ 기둝의 λ‹¨μˆœν™”λΌ μœ„ν•΄μ„œ 이닀. ν•˜λ‚˜μ˜ 객체가 λ§Œλ“€μ–΄ μ§€λŠ”λ°, newκ°€ 호좜되고 이것은 deleteκ°€ 뢈리기 μ „κΉŒμ§€ λ©”λͺ¨λ¦¬λΌ μ°¨μ§€ν•œλ‹€. μ°Έμ‘° μ„ΈκΈ°λŠ” 같은 μžλ£Œλ“€μ˜ μ€‘λ³΅λœ 객체듀을 ν•˜λ‚˜λ‘œ κ³΅μœ ν•˜μ—¬, new와 deleteλΌ ν˜ΈμΆœν•˜λŠ” μŠ€νŠΈλ ˆμŠ€λΌ μ„이고, λ©”λͺ¨λ¦¬μ— 객체가 λ“±λ‘λ˜μ–΄ μœ μ§€λ˜λŠ” λΉ„μš©λ„ μ„μΌμˆ˜ μžˆλ‹€. λ‘λ²ˆμ§Έμ˜ λ™κΈ°λŠ” κ·Έλƒ₯ 일반적인 μƒκ°μ—μ„œ λ‚˜μ™”λ‹€. μ€‘λ³΅λœ μžλ£ŒλΌ μ—¬λŸ¬ 객체가 κ³΅μœ ν•˜μ—¬, λΉ„μš© μ ˆμ•½ λΏμ•„λ‹ˆλΌ, 생성, 파괴의 κ³Όμ •μ˜ μƒλž΅μœΌλ‘œ ν”„λ‘œκ·Έλž¨ μˆ˜ν–‰ μ†λ„κΉŒμ§€ λ†’μ΄κ³ μž ν•˜λŠ” λͺ©μ μ΄λ‹€.

이런 κ°„λ‹¨ν•œ μƒκ°λ“€μ˜ μ μš©μ„ μœ„ν•΄μ„œλŠ” 많이 μ“°μ΄λŠ” μžλ£Œν˜•νƒœλΌ μ°Ύμ•„ μ˜ˆμ œμ‚Όμ•„ λ³΄μ—¬μ£ΌλŠ” 것이 이해에 도움이 도리것이닀. 이런 λ©΄μ—μ„œ λ‹€μŒμ˜ μ˜ˆλŠ” 적합할 것이닀.

~cpp 
class String {                      // ν‘œμ€ λ¬Έμžμ—΄ ν˜•μ€ 이번 μ•„μ΄ν…œμ˜ μ°Έμ‘°μ„ΈκΈ°λΌ κ°–μΆ”κ³ 
public:                             // μžˆμ„ν…Œμ§€λ§Œ, 그것을 λ°°μ œν–ˆλ‹€κ³  κ°€μ •ν•˜μž
    String(const char *value = "");
    String& operator=(const String& rhs);
    ...
private:
    char *data;
};
String a, b, c, d, e;

a = b = c = d = e = "Hello";
a~eκΉŒμ§€ λͺ¨λ‘ "Hello"λΌλŠ” 같은 값을 가지고 μžˆλŠ” λ‹€λ₯Έ 객체이닀. 이 ν΄λž˜μŠ€λŠ” μ°Έμ‘° μ„ΈκΈ°κ°€ μ μš©λ˜μ§€ μ•Šμ•˜κΈ° λ•Œλ¬Έμ— λͺ¨λ‘ 각각의 값을 가지고 μžˆλ‹€. λ¬Έμžμ—΄μ˜ ν• λ‹Ή(assignment) μ—°μ‚°μžλŠ” μ•„λ§ˆ λ‹€μŒκ³Ό 같이 κ΅¬ν˜„λ˜μ–΄ μžˆμ„ 것이닀.

~cpp 
String& String::operator=(const String& rhs)
{
    if (this == &rhs) return *this;         // Item E17 μ°Έκ³ 

    delete [] data;
    data =   new char[strlen(rhs.data) + 1];
    strcpy(data, rhs.data);

    return *this;                           // Item E15 μ°Έκ³ 
}
이 μ½”λ“œμ˜ 의λΈλΌ 그림으둜 ν‘œν˜„ν•˜λ©΄ λ‹€μŒκ³Ό κ°™λ‹€.



μ΄λŸ¬ν•œ 그림을 μ°Έμ‘° μ„ΈκΈ°μ—μ„œ λ§ν•˜λŠ” 쀑볡 객체에 λŒ€ν•œ 자료의 κ³΅μœ λΌ μ μš©μ‹œν‚¨ 그림은 λ‹€μŒκ³Ό 같을 것이닀.


"Hello"λΌλŠ” 값은 ν•˜λ‚˜λ§Œ μ €μž₯λ˜μ–΄ μžˆλŠ” 것이고, μ΄λΌ λ¬Έμžμ—΄λ“€μ΄ κ³΅μœ ν•΄μ„œ ν‘œν˜„μ‹œ 가지고 μžˆλŠ” 것이닀. ν•˜μ§€λ§Œ μ‹€μ§ˆμ μœΌλ‘œ "Hello"의 ν• λ‹Ή μ‹œμ μ€ μ†μ‰½κ²Œ μ•Œμˆ˜ μžˆμ§€λ§Œ, 파괴 μ‹œμ μ„ μ•Œμˆ˜ μžˆλŠ”κ²ƒμ€ 만만치 μ•Šλ‹€. κ·Έλž˜μ„œ 파괴 μ‹œμ μ„ μ•ŒκΈ° μœ„ν•΄μ„œ "Hello" 값에 그것을 μ°Έμ‘°ν•˜λŠ” μ •λ„λΌ κΈ°λ‘ν•˜κ³ , κ·Έ μ°Έμ‘°κ°€ 0κ°€ λ˜λŠ” μ‹œμ μ„ κ°’μ˜ 파괴 μ‹œμ μœΌλ‘œ μ‚Όμ•„μ•Ό ν•˜λŠ”λ°, 이런 생각을 μ•„κΉŒ 그림에 λ‹€μ‹œ λ„£μœΌλ©΄ λ‹€μŒκ³Ό κ°™λ‹€.


그리고 μ—¬κΈ°μ˜ 5에 ν•΄λ‹Ή ν•˜λŠ” μˆ«μžλΌ Reference count 라고 λΆ€λ₯Έλ‹€. ν˜ΉμžλŠ” use count라고 λΆ€λ₯΄κΈ°λ„ ν•˜λŠ”λ°, ν•™μˆ  μš©μ–΄μ˜ λ‹ΉνŒŒμ— λ”°λ₯Έκ±°λ‹ˆ 별 상관 μ•ˆν•œλ‹€. ν•˜μ§€λ§Œ λ‚˜(scott mayer) κ·Έλ ‡κ²Œ μ•ˆλΆ€λ₯Έλ‹€.

1.2. Implementing Reference Counting : μ°Έμ‘° μ„ΈκΈ° 적용

μ°Έμ‘° μ„ΈκΈ°λΌ ν•˜λŠ” String ν΄λž˜μŠ€λΌ λ§Œλ“œλŠ”κ±΄ μ–΄λ ΅μ§€λŠ” μ•Šμ§€λ§Œ, μ„Έμ„Έν•œ 뢀뢄에 μ£Όλͺ©ν•΄μ„œ μ–΄λ–»κ²Œ κ·ΈλŸ¬ν•œ ν΄λž˜μŠ€κ°€ κ΅¬ν˜„λ˜λŠ”μ§€ μ£Όλͺ©ν•΄ 보자. 일단, μžλ£ŒλΌ μ €μž₯ν•˜λŠ” μ €μž₯μ†Œκ°€ 있고, μ°Έμ‘°λΌ μ…€μˆ˜ μžˆλŠ” μΉ΄μš΄ν„°κ°€ μžˆμ–΄μ•Ό ν•˜λŠ”λ°, 이 λ‘˜μ„ ν•˜λ‚˜λ‘œ λ¬Άμ–΄μ„œ StringValue ꡬ쑰체둜 μž‘λŠ”λ‹€. κ΅¬μ‘°μ²΄λŠ” String의 사역(private)에 μœ„μΉ˜ν•œλ‹€.

μ§€κΈˆκΉŒμ§€ λ§ν•œ μ‚¬ν•­μ˜ 기본은 λ‹€μŒκ³Ό κ°™λ‹€.

~cpp 
class String {
public:
    ...                         // String memberλ“€ μœ„μΉ˜
private:
    struct StringValue { ... }; // μ°Έμ‘°λΌ μ„ΈλŠ” μΈμžμ™€, String의 값을 μ €μž₯.
    StringValue *value;         // μœ„μ˜ ꡬ쑰체의 κ°’
};
λ¬Όλ‘  이의 이름은 Stringκ³Ό λ‹€λ₯Έ 이름을 맀겨야 ν•˜κ² μ§€λ§Œ,(μ•„λ§ˆ RCString정도?) ν•˜μ§€λ§Œ Stringμžμ²΄λΌ κ΅¬ν˜„ν•œλ‹€λŠ” 의λΈλ‘œ κ·Έλƒ₯ 이름은 μœ μ§€ν•˜κ³ , μ•žμœΌλ‘œ 말할 μ°Έμ‘°μ„ΈκΈ°λΌ μ μš©μ‹œν‚¨ String κ°μ²΄λΌ λ§Œλ“€μ–΄ λ‚˜κ°€κ² λ‹€.

μœ„μ˜ κΈ°λ³Έ λ””μžμΈμ„ κ΅¬ν˜„ν•΄ λ³Έλ‹€.

~cpp 
class String {
private:
    struct StringValue {
        int refCount;       // μ°Έμ‘°λΌ μ„ΈκΈ°μœ„ν•¨ μΉ΄μš΄ν„°
        char *data;         // κ°’ 포인터

        StringValue(const char *initValue);
        ~StringValue();
    };
    ...
};
// StringValue의 볡사 μƒμ„±μž, μ΄ˆκΈ°ν™” λͺ©λ‘μœΌλ‘œ refCount 인자 1둜 μ΄ˆκΈ°ν™”
String::StringValue::StringValue(const char *initValue): refCount(1)
{
    data = new char[strlen(initValue) + 1];     // μƒˆλ‘œμš΄ κ°’ ν• λ‹Ή(아직 μ°Έμ‘°μ„ΈκΈ° 적용 x
    strcpy(data, initValue);                    // 슀트링 볡사
}
// StringValue의 파괴자
String::StringValue::~StringValue()
{
    delete [] data;     // 슀트링 μ‚­μ œ
}
μ΄κ²ƒμœΌλ‘œ StringValue의 κ΅¬ν˜„μ€ 일단 끝이닀. μ΄λΌ μ‚¬μš©ν•˜λŠ” String 객체의 κ΅¬ν˜„μ— λ“€μ–΄κ°€μ•Ό ν•œλ‹€.

λ¨Όμ € μƒμ„±μžλΌ κ΅¬ν˜„ν•œλ‹€.

~cpp 
class String {
public:
    String(const char *initValue = "");
    String(const String& rhs);
  ...
};

~cpp 
// String의 볡사 μƒμ„±μž, μ΄ˆκΈ°ν™” λͺ©λ‘μœΌλ‘œ StringValue에 값을 ν• λ‹Ήν•œλ‹€.
String::String(const char *initValue): value(new StringValue(initValue))
{}
ν΄λΌμ΄μ–ΈνŠΈμ˜ μ½”λ“œλŠ” 보톡 λ‹€μŒκ³Ό κ°™λ‹€.

~cpp 
String s("More Effective C++");
μ΄λΌ μ²˜μŒ μ°Έμ‘°μ„ΈκΈ° μ„λͺ…에 λ‚˜μ˜¨ κ·Έλ¦Όμ‹μœΌλ‘œ μ„λͺ…ν•˜μžλ©΄ λ‹€μŒκ³Ό κ°™λ‹€.


그럼 String객체의 μƒμ„±μ‹œ μ„œλ‘œ κ³΅μœ ν•˜μ§€ μ•Šμ€ 데이터 κ΅¬μ‘°λΌ κ°€μ§€λ„λ‘ ν΄λΌμ΄μ–ΈνŠΈκ°€ μ½”λ“œλΌ μž‘μ„±ν•˜λ©΄ λ‹€μŒκ³Ό κ°™λ‹€.

~cpp 
String s1("More Effective C++");
String s2("More Effective C++");
μ΄λ ‡κ²Œ λ˜λŠ”λ°, μ΄λŠ” 그림으둜 ν‘œν˜„ν•˜λ©΄,


λ‹€μŒκ³Ό κ°™λ‹€. μ—¬κΈ°μ—μ„œ "More Effective C++" λΌλŠ” λ¬Έμžμ—΄μ„ κ³΅μœ ν•œλ‹€λ©΄, μ°Έμ‘°μ„ΈκΈ°κ°€ 이루어 μ§€λŠ” 것일 κ±°λ‹€. 그러기 μœ„ν•΄μ„œ String의 볡사 μƒμ„±μžλŠ”, μ°Έμ‘° μΉ΄μš΄ν„°λΌ μ˜¬λ¦¬κ³ , μžλ£ŒλΌ μƒˆλ‘œ 생성할것이 μ•„λ‹ˆλΌ, κ·Έλƒ₯ ν¬μΈν„°λ§Œ μ„ΈνŒ…ν•΄ μ£ΌλŠ” 일을 ν•΄μ•Ό ν•œλ‹€. 이런 과정을 κ΅¬ν˜„ν•˜λ©΄

~cpp 
// String 클래슀의 볡사 μƒμ„±μž, μ΄ˆκΈ°ν™” λͺ©λ‘μœΌλ‘œ StringValue의 ν¬μΈν„°λΌ μ„ΈνŒ…ν•œλ‹€.
String::String(const String& rhs): value(rhs.value)
{
    ++value->refCount;  // μ°Έμ‘° μΉ΄μš΄ν„°λΌ μ˜¬λ¦°λ‹€.
}
이 μ½”λ“œλΌ μ‚¬μš©ν•˜λŠ” 상황은 λ‹€μŒκ³Ό κ°™κ³ ,

~cpp 
String s1("More Effective C++");
String s2 = s1;
μ΄λ²ˆμ— 그림으둜 ν‘œν˜„ν•˜λ©΄


μ—¬κΈ°μ„œμ˜ μš”μ μ€ μ°Έμ‘°μ„ΈκΈ°κ°€ μ μš©λ˜μ§€ μ•Šμ€ String ν΄λž˜μŠ€λ³΄λ‹€ 더 효율이 λ†’μ•„ μ§„λ‹€λŠ” 점이닀. 이 μ±… μ „λ°˜μ— 계속 μ–ΈκΈ‰ν–ˆλ“이, 생성과 파괴 같은 μ‹œκ°„κ³Ό, κ³΅κ°„μ˜ λ§Žμ€ λΉ„μš©μ„ μ†Œλͺ¨ν•˜λŠ” 식이 μ•„λ‹Œ, ν•΄λ‹Ή ν¬μΈν„°λ§Œμ„ λ³΅μ‚¬ν•˜λ€λ‘œ, 일단, μƒμ„±μ‹œμ˜ λΉ„μš©μ„ μ•„λ‚€λ‹€.

μƒμ„±μžμ˜ μ†μ‰¬μš΄ κ΅¬ν˜„κ°™μ΄ 파괴자 κ΅¬ν˜„λ„ 그리 μ–΄λ €μš΄ 일이 μ•„λ‹ˆλ‹€. StringValue의 νŒŒκ΄΄λŠ”, μ„œλ‘œκ°€ μ΅œλŒ€ν•œ μ‚¬μš©ν•˜κ³ , 값이 파괴 μ‹œμ μ€ μ°Έμ‘° μΉ΄μš΄ν„°κ°€(reference counter:μ΄ν•˜ μ°Έμ‘° μΉ΄μš΄ν„°λ§Œ) 1인 경우 더이상 μ‚¬μš©ν•˜λŠ” 객체가 μ—†μœΌλ€λ‘œ νŒŒκ΄΄ν•˜λ„λ‘ κ΅¬μ„±ν•œλ‹€.

~cpp 
class String {
public:
    ~String();
    ...
};
String::~String()
{
    if (--value->refCount == 0) delete value;   // μ°Έμ‘° μΉ΄μš΄ν„°κ°€ 1인 μƒνƒœ
}
파괴자의 νš¨μœ¨μ„ 비ꡐ해도 μ—­μ‹œ, λ§ˆμ°¬κ°€μ§€μ΄λ‹€. deleteκ°€ λΆˆλ¦¬λŠ” μ‹œμ μ€ ν•΄λ‹Ή 객체가 더 이상 ν•„μš” μ—†μ„λ•Œλ§Œ μ œκ±°ν•˜λŠ” 것이기 λ•Œλ¬Έμ— λΉ„μš©μ„ μ•„λ‚„μˆ˜ μžˆλ‹€.

λ‹€μŒμ— κ΅¬ν˜„ν•΄μ•Όν•  사항은 ν• λ‹Ή(assignment)κ΄€λ ¨ν•œ κ΅¬ν˜„ 즉 operator= 이닀. 볡사 μƒμ„±μž(copy constructor)λΌ ν˜ΈμΆœμ΄ μ•„λ‹Œ 할당은 λ‹€μŒκ³Ό 같이 μ„ μ–Έλ˜κ³ ,

~cpp 
class String {
public:
    String& operator=(const String& rhs);
    ...
};
λ‹€μŒκ³Ό 같이 μ‚¬μš©λ˜λ©°

~cpp 
s1 = s2;
μ΄κ²ƒμ˜ κ΅¬ν˜„μ€ 약간은 λ³΅μž‘ν•œλ°, μ΄μœ λŠ” 생성과 νŒŒκ΄΄κ°€ λ™μ‹œμ— μžˆμ–΄μ•Ό ν•˜λŠ” 상황을 κ³ λ €ν•΄μ•Ό ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€. κ·Έλž˜λ„ 아직 μ•žμœΌλ‘œ 닀루어야할 λ‚΄μš©μ— λΉ„ν•΄ μƒλ‹Ήνžˆ κ°„λ‹¨ν•œ νŽΈμ΄λ‹€. μžμ„Έν•œ μ„λͺ…은 μ†ŒμŠ€μ— 주석을 μ°Έκ³ ν•˜λΌ

~cpp 
String& String::operator=(const String& rhs)
{
    if (value == rhs.value) {           // μ΄λΈ λ‘ 객체가 같은 값을 가리킨닀면,
        return *this;                   // νŠΉλ³„νžˆ 할일은 μ—†λ‹€.
    }                                     

    if (--value->refCount == 0) {       // ν˜„μž¬ 값이 μžμ‹  외에 아무도 μ°Έμ‘°ν•˜κ³ 
        delete value;                   // μžˆμ§€ μ•Šλ‹€λ©΄ μ‚­μ œ ν•œλ‹€.
    }
    value = rhs.value;                  // μžλ£ŒλΌ κ³΅μœ ν•œλ‹€.
    ++value->refCount;                  // μ°Έμ‘° μΉ΄μš΄ν„°λΌ μ˜¬λ¦°λ‹€.
    return *this;                       // 이건 μ•Œμ•„μ„œ --+
}

1.3. Copy-on-Write : 쓰기에 κΈ°λ°˜ν•œ 볡사

μ°Έμ‘°μ„ΈκΈ°κ°€ 적용된 λ¬Έμžμ—΄μ— λŒ€ν•˜μ—¬ λ‘˜λŸ¬ λ΄€λŠ”λ°, μ΄λ²ˆμ—λŠ” 배열에 κ΄€ν•œ(array-bracket) μ—°μ‚°μžλ“€ "[]" 이녀석듀에 κ΄€ν•΄μ„œ 생각해 보자. 클래슀의 선언은 λ‹€μŒκ³Ό κ°™λ‹€.

~cpp 
class String {
public:
    const char& operator[](int index) const;    // const String에 λŒ€ν•˜μ—¬

    char& operator[](int index);                // non-const String에 λŒ€ν•˜μ—¬
    ...
};
const String에 λŒ€ν•œ 값을 μ£ΌλŠ” 것은 μ•„λž˜μ™€ 같이 κ°„λ‹¨νžˆ ν•΄κ²°λœλ‹€. λ‚΄λΆ€ 값이 μ•„λ¬΄λŸ° 영ν–₯을 받을 것이 없을 경우이기 떄문이닀.

~cpp 
const char& String::operator[](int index) const
{
  return value->data[index];
}
(이 ν•¨μˆ˜λŠ” μ›λž˜μ˜ C++μ—μ„œ λ°°μ—΄μ˜ μ‚¬μš© κ°œλ…κ³Ό 같이, index의 μœ νš¨μ„±μ„ μ κ²€ν•˜μ§€ μ•ŠλŠ”λ‹€. 이에 λŒ€ν•œ 감은은 μ°Έμ‘° μ„ΈκΈ°μ˜ μ£Όμ œμ— λ–¨μ–΄μ Έ 있고, μΆ”κ°€ν•˜λŠ” 것도 그리 μ–΄λ €μš΄μΌμ΄ μ•„λ‹ˆλΌ 일단은 μ œμ™Έν•œλ‹€.)

ν•˜μ§€λ§Œ non-const의 operator[]λŠ” 이것(const operator[])와 μ™„μ „νžˆ λ‹€λ₯Έ 상황이 λœλ‹€. μ΄μœ λŠ” non-const operator[]λŠ” StringValueκ°€ 가리킀고 μžˆλŠ” 값을 λ³€κ²½ν• μˆ˜ μžˆλŠ” κΆŒν•œμ„ λ‚΄μ£ΌκΈ° λ•Œλ¬Έμ΄λ‹€. 즉, non-const operator[]λŠ” 자료의 읽기(Read)와 μ“°κΈ°(Write)λΌ λ‹€ ν—ˆμš©ν•œλ‹€.

~cpp 
String s;
...
cout << s[3];       // 이건 읽기(Read)
s[5] = 'x';         // 이건 μ“°κΈ°(Write)

μ°Έμ‘° μ„ΈκΈ°κ°€ 적용된 String은 μˆ˜μ •ν• λ•Œ μ‘°μ‹¬ν•˜κ²Œ ν•΄μ•Ό λœλ‹€. κ·Έλž˜μ„œ 일단 μ•ˆμ „ν•œ non-const operator[]λΌ μˆ˜ν–‰ν•˜κΈ° μœ„ν•˜μ—¬ μ•„μ˜ˆ operator[]λΌ μˆ˜ν–‰ν• λ•Œ λ§ˆλ‹€ μƒˆλ‘œμš΄ κ°μ²΄λΌ μƒμ„±ν•΄ λ²„λ¦¬λŠ” 것이닀. κ·Έλž˜μ„œ λ§Œλ“  것이 λ‹€μŒκ³Ό 같은 λ°©λ²•μœΌλ‘œ ν•˜κ³ , μ„λͺ…은 주석에 더 μžμ„Ένžˆ

~cpp 
char& String::operator[](int index)     // non-const operator[]
{
  // if we're sharing a value with other String objects,
  // break off a separate copy of the value for ourselves
    // λ§Œμ•½ λ‹€λ₯Έ 객체와 μžλ£ŒλΌ κ³΅μœ ν•˜μ§€ μ•Šκ³  μžˆλ‹€λ©΄, μžλ£ŒλΌ λ…ΈμΆœ μ‹œμΌœλ„ 
    // 상관이 μ—†λ‹€. 
    if (value->refCount > 1) {          // if μ•ˆμͺ½μ€ μƒˆλ‘œμš΄ κ°μ²΄λΌ μƒμ„±ν•΄μ•Ό ν•  경우
        --value->refCount;              // μƒˆλ‘œμš΄ 객체의 생성을 μœ„ν•΄μ„œ ν˜„μž¬ μ°Έμ‘°ν•˜λŠ”
                                        // 자료의 refCountλΌ κ°μ†Œ μ‹œν‚¨λ‹€.
    value = new StringValue(value->data);   // ν˜„μž¬ 자료의 사본을 λ§Œλ“€μ–΄
                                            // 이것을 가리킨닀.
    }

    // 이제, 아무도 κ³΅μœ ν•˜κ³  μžˆμ§€ μ•Šμ€ StringValue의 객체
    // λ‚΄λΆ€μ˜ λ¬Έμžμ—΄μ˜ ν•œ 뢀뢄을 λ°˜ν™˜ν•œλ‹€.
    return value->data[index];
}
이것은 λΆ€μ±ν•œ 점이 μžˆλ‹€. λ‹€μŒ κ³Όμ •μ—μ„œ λ³΄κ°•ν•œλ‹€.

μ΄λŸ¬ν•œ 생각은 컴퓨터 κ³Όν•™μ—μ„œ μ˜€λž«λ™μ•ˆ 닀루어 져있던 것이닀. νŠΉνžˆλ‚˜ OS(operating system)μ„κ³„μ‹œμ— Processκ°€ κ³΅μœ ν•˜κ³  있던 λ°μ΄ν„°λΌ κ·Έλ“€μ΄ μˆ˜μ •ν•˜κ³ μž ν•˜λŠ” 뢀뢄을 λ³΅μ‚¬ν•΄μ„œ 접근을 ν—ˆλ½ λ°›λŠ” λ£¨ν‹΄μ—μ„œ 많이 쓰인닀. 흔이 μ΄λŸ¬ν•œ κΈ°μˆ μ— κ΄€ν•΄μ„œ copy-on-write(쓰기에 κΈ°λ°˜ν•œ 볡사, 이후 copy-on-writeλΌ κ·Έλƒ₯ μ“΄λ‹€.) 라고 λΆ€λ₯Έλ‹€.

1.4. Pointers, References, and Copy-on-Write : 포인터, μ°Έμ‘°, 쓰기에 κΈ°λ°˜ν•œ 볡사

copy-on-writeλΌ κ΅¬ν˜„ν• λ•Œ μ •ν™•μ„±κ³Ό νš¨μœ¨μ„±, λ‘˜λ‹€ 만적 μ‹œν‚€λŠ” νŽΈμ΄λ‹€. ν•˜μ§€λ§Œ ν•­μƒλ”°λΌλ‹€λ‹ˆλŠ”λ¬΄λ„Ίκ°€ μžˆλŠ”λ°, λ‹€μŒ μ½”λ“œλΌ μƒκ°ν•΄ 보자.

~cpp 
String s1 = "Hello";
char *p = &s1[1];
μ΄λΌ λŒ€κ°• 그림을 λ‚˜νƒ€λ‚΄ 보면 pκ°€ Hello의 eλΌ κ°€λ¦¬ν‚€λŠ” μ΄μ •λ„μ˜ λŠλ‚ŒμΌ 것이닀.


그러면 μ΄λ²ˆμ—λŠ” 여기에 ν•œμ„λ§Œ 덧뢙여 λ³Έλ‹€.

~cpp 
String s2 = s1;
String 볡사 μƒμ„±μžκ°€ μ•„λ§ˆ s2κ°€ s1의 StringValueλΌ κ³΅μœ ν•˜κ²Œ λ§Œλ“€κ²ƒμ΄λ‹€. κ·Έλž˜μ„œ λ‹ΉμŒκ³Ό 같은 그림으둜 ν‘œν˜„λ  것이닀.


κ·Έλž˜μ„œ λ‹€μŒκ³Ό 같은 ꡬ문을 μ‹€ν–‰ν•œλ‹€λ©΄

~cpp 
p = 'x';                     // 이 것은 s1, s2λΌ λͺ¨λ‘ λ³€κ²½!
String의 볡사 μƒμ„±μžλŠ” μ΄λŸ¬ν•œ μƒνƒœλΌ κ°μ§€ν•  방법이 μ—†λ‹€. μœ„μ—μ„œ 보λ“이, s2κ°€ μ°Έκ³ ν• μˆ˜ μžˆλŠ” μ •λ³΄λŠ” λͺ¨λ‘ s1의 내뢀에 μžˆλŠ”λ°, s1μ—κ²ŒλŠ” non-const operator[]λΌ μˆ˜ν–‰ν•˜μ˜€λŠ”μ§€μ— κ΄€ν•œ 기둝은 μ—†λ‹€.

μ΄λŸ°κ²ƒμ„ ν•΄κ²°ν• μˆ˜ μžˆλŠ” λ°©λ²•μœΌλ‘œλŠ” μ΅œμ†Œν•œ μ„Έκ°€μ§€λΌ μƒκ°ν• μˆ˜ μžˆλŠ”λ°, μ²«λ²ˆμ§ΈλŠ” 이것을 μ—†λŠ”κ±Έλ‘œ μ·¨κΈ‰ν•˜κ³ , λ¬΄μ‹œ ν•΄ λ²„λ¦¬λŠ” 것이닀. μ΄λŸ¬ν•œ μ ‘κ·Ό λ°©ν–₯은 μ°Έμ‘° μ„ΈκΈ°κ°€ μ μš©λ˜μ–΄ μžˆλŠ” 클래슀 λΌμ΄λΈŒλŸ¬λ¦¬μ— μƒλ‹Ήν•œ κ΄΄λ‘œμ›€μ„ λœμ–΄ μ£ΌλŠ”κ²ƒμ΄λ‹€. ν•˜μ§€λ§Œ μ΄λŸ¬ν•œ λ¬Έμ œλΌ κ΅¬ν˜„μƒμ—μ„œ μ™„μ „νžˆ λ¬΄μ‹œν• μˆ˜λŠ” μ—†λŠ” 노릇이닀. λ‘λ²ˆμ§Έλ‘œ μƒκ°ν• μˆ˜ μžˆλŠ” 방법은 μ΄λŸ¬ν•œκ²ƒμ„ ν•˜μ§€ 말도둝 λͺ…μ‹œν•˜λŠ” 것인데, μ—­μ‹œλ‚˜ λ³΅μž‘ν•˜λ‹€. μ„Έλ²ˆμ§Έλ‘œ, 뭐 κ²°κ΅­ μ œκ±°μ•Όλ§Œ 할것이닀. μ΄λŸ¬ν•œ λΆ„μ œμ˜ μ œκ±°λŠ” 그리 μ–΄λ ΅μ§€λŠ” μ•Šλ‹€. λ¬Έμ œλŠ” νš¨μœ¨μ΄λ‹€. 이런 쀑볡에 κ΄€λ ¨ν•œ λ¬Έμ œλΌ μ œκ±°ν•˜κΈ° μœ„ν•΄μ„œλŠ”, μƒˆλ‘œμš΄ 자료 κ΅¬μ‘°λΌ λ§Œλ“€μ–΄ λ‚΄μ•Όν•˜κ³ , μ΄κ²ƒμ˜ 의λΈλŠ” 객체간에 μ„œλ‘œ κ³΅μœ ν•˜λŠ” μžλ£Œκ°€ μ„μ–΄ λ“ λ‹€λŠ” 의λΈμ΄λ‹€. 즉, λΉ„μš©μ΄ 많이 λ“€μ–΄κ°„λ‹€. ν•˜μ§€λ§Œ μ–΄μ©”μˆ˜ 없지 μ•Šμ„κΉŒ?

κ°„λ‹¨ν•œ ν”Œλž˜κ·Έ ν•˜λ‚˜λΌ μΆ”κ°€ν•˜λŠ” κ²ƒμœΌλ‘œ 이 λ¬Έμ œλŠ” ν•΄κ²°λœλ‹€. 이번 μˆ˜μ • 버전은 곡유의 μ—¬λΆ€λΌ ν—ˆλ½ν•˜λŠ” sharable μΈμžλΌ λ”ν•œ 것이닀. μ½”λ“œλΌ λ³΄μž.

~cpp 
class String {
private:
    struct StringValue {
        int refCount;
        bool shareabl;              // 이 μΈμžκ°€ 더해 μ‘Œλ‹€.
        char *data;
        StringValue(const char *initValue);
        ~StringValue();
    };
    ...
};
String::StringValue::StringValue(const char *initValue)
:   refCount(1),
    shareable(true)                 // μ΄ˆκΈ°ν™”μ‹œμ—λŠ” κ³΅μœ λΌ ν—ˆλ½ν•œλ‹€.
{
    data = new char[strlen(initValue) + 1];
    strcpy(data, initValue);
}
String::StringValue::~StringValue()
{
    delete [] data;
}
볡사 μƒμ„±μžμ—μ„œλŠ” μ°Έμ‘° 뿐만 μ•„λ‹ˆλΌ. κ³΅μœ λΌ κ±°λΆ€ν• λ•Œ μ—­μ‹œ κ°μ•ˆν•΄μ•Ό ν•˜λ€λ‘œ, μˆ˜μ •ν•΄μ•Ό ν•œλ‹€.

~cpp 
String::String(const String& rhs)
{
  if (rhs.value->shareable) {       // κ³΅μœ λΌ ν—ˆλ½ν•  경우
    value = rhs.value;
    ++value->refCount;
  }
  else {                            // κ³΅μœ λΌ κ±°λΆ€ν•  경우
    value = new StringValue(rhs.value->data);
  }
}
그리고 이 shareableμΈμžλŠ” non-const operator[]κ°€ ν˜ΈμΆœλ λ•Œ false둜 λ³€κ²½λ˜μ–΄μ„œ 이후, ν•΄λ‹Ή 자료의 κ³΅μœ λΌ κΈˆμ§€ν•œλ‹€.

~cpp 
char& String::operator[](int index)
{
    if (value->refCount > 1) {
        --value->refCount;
        value = new StringValue(value->data);
    }
    value->shareable = false;           // 이제 자료의 κ³΅μœ λΌ κΈˆμ§€ν•œλ‹€.
    return value->data[index];
}
λ§Œμ•½ Item 30에 μ–ΈκΈ‰λ˜λŠ” proxy 클래슀 방법을 μ‚¬μš©ν•΄μ„œ μ½λŠ” μž‘μ—…κ³Ό, μ“°λŠ” μž‘μ—…μ— λŒ€ν•œ 것을 κ΅¬λΆ„ν•œλ‹€λ©΄ StringValue 객체의 μˆ«μžλΌ μ€λ” μ„μΌμˆ˜ μžˆμ„ 것이닀. 그건 λ‹€μŒ μž₯μ—μ„œ 보자.

1.5. A Reference-Counting Base Class : μ°Έμ‘°-μ„ΈκΈ° 기초 클래슀

μ°Έμ‘°μ„ΈκΈ°λŠ” 단지 string의 κ²½μš°μ—λ§Œ μœ μš©ν•œκ±ΈκΉŒ? λ‹€λ₯Έ 객체 μ—­μ‹œ μ°Έμ‘°μ„ΈκΈ°λΌ μ μš©ν•΄μ„œ μ‚¬μš©ν• μˆ˜λŠ” μ—†μ„κΉŒ? 생각해 보면 μ°Έμ‘°μ„ΈκΈ°λΌ κ΅¬ν˜„ν•˜κΈ° μœ„ν•΄μ„œλŠ” μ§€κΈˆκΉŒμ§€μ˜ μ–ΈκΈ‰μ˜ κΈ°λŠ₯을 λ¬Άμ–΄μ„œ μž¬μ‚¬μš© κ°€λŠ₯ν•˜κ²Œ λ§Œλ“€λ©΄ 쒋을것 같은데, ν•œλ²ˆ λ§Œλ“€μ–΄ 보도둝 ν•˜μž.

제일 μ²˜μŒμ— ν•΄μ•Ό 할일은 μ°Έμ‘°μ„ΈκΈ°κ°€ 적용된 κ°μ²΄λΌ μœ„ν•œ (reference-counded object) RCObject같은 기초 ν΄λž˜μŠ€λΌ λ§Œλ“œλŠ” 것이닀. μ–΄λ– ν•œ ν΄λΌμŠ€λΌλ„ 이 ν΄λž˜μŠ€λΌ μƒμ†λ°›μœΌλ©΄ μžλ™μ μœΌλ‘œ μ°Έμ‘°μ„ΈκΈ°μ˜ κΈ°λŠ₯이 κ΅¬ν˜„λ˜λŠ” ν˜•μ‹μ„ λ°”λΌλŠ” 것이닀. 그러기 μœ„ν•΄μ„œλŠ” RCObjectκ°€ 가지고 μžˆμ–΄μ•Ό ν•˜λŠ” λŠ₯λ ₯은 μΉ΄μš΄ν„°μ— λŒ€ν•œ 증감에 λŒ€ν•œ λŠ₯λ ₯일 것이닀. κ²Œλ‹€κ°€ 더 이상, μ‚¬μš©μ΄ ν•„μš” μ—†λŠ” μ‹œμ μ—μ„œλŠ” νŒŒκ΄΄λ˜μ–΄μ•Ό ν•œκ²ƒμ΄λ‹€. λ‹€μ‹œλ§ν•΄, νŒŒκ΄΄λ˜λŠ” μ‹œμ μ€ μΉ΄μš΄ν„°μ˜ μΈμžκ°€ 0이 λ λ•Œμ΄λ‹€. 그리고 곡유 ν—ˆμš© ν”Œλž˜κ·Έμ— λŒ€ν•œ(shareable flag) κ΄€ν•œ 것은, ν˜„μž¬κ°€ 곡유 κ°€λŠ₯ν•œ μƒνƒœμΈμ§€ κ²€μ¦ν•˜λŠ” λ©”μ†Œλ“œμ™€, κ³΅μœ λΌ λ¬Άμ–΄ λ²„λ¦¬λŠ” λ©”μ†Œλ“œ, μ΄λ ‡κ²Œλ§Œ 있으면 될것이닀. κ³΅μœ λΌ ν‘Όλ‹€λŠ” 것은 μ§€κΈˆκΉŒμ§€μ˜ μƒκ°μœΌλ‘œλŠ” λΆˆκ°€λŠ₯ν•œ 것이기 λ•Œλ¬Έμ΄λ‹€.

μ΄λŸ¬ν•œ μ •μ˜λΌ λͺ¨μ•„λͺ¨μ•„ λΌˆλŒ€λΌ λ§Œλ“€λ©΄ λ‹€μŒκ³Ό κ°™λ‹€.

~cpp 
class RCObject {
public:
    RCObject();
    RCObject(const RCObject& rhs);
    RCObject& operator=(const RCObject& rhs);
    virtual ~RCObject() = 0;

    void addReference();
    void removeReference();
    void markUnshareable();
    bool isShareable() const;
    bool isShared() const;
private:
    int refCount;
    bool shareable;
};

RCObjectλŠ” μƒμ„±λ˜κ³ , νŒŒκ΄΄λ˜μ–΄ 질수 μžˆμ–΄μ•Ό ν•œλ‹€. μƒˆλ‘œμš΄ μ°Έμ‘°κ°€ μΆ”κ°€λ˜λ©΄ ν˜„μž¬μ˜ μ°Έμ‘°λŠ” μ œκ±°λ˜μ–΄μ•Ό ν•œλ‹€. 곡유 μƒνƒœμ— λŒ€ν•΄ μ—¬λΆ€λΌ μ•Œμˆ˜ μžˆμ–΄μ•Ό ν•˜λ©°, disable둜 μ„μ •ν• μˆ˜ μžˆμ–΄μ•Ό ν•œλ‹€. νŒŒκ΄΄μžμ— λŒ€ν•œ μ‹€μˆ˜λΌ λ°©μ§€ν•˜κΈ° μœ„ν•˜μ—¬ 베이슀 ν΄λž˜μŠ€λŠ” νŒŒκ΄΄μžλΌ κ°€μƒ ν•¨μˆ˜λ‘œμ„ μ–Έν•΄μ•Ό λœλ‹€. μ—¬κΈ°μ—μ„œλŠ” 순수 가상 ν•¨μˆ˜(pure virtual function)둜 μ„ μ–Έν•œλ‹€.

λ‹€μŒ μ½”λ“œκ°€ RCObjectλΌ λ³„ μ˜ˆμ™Έ μ—†λ‹€λŠ” μ „μ œλ‘œ, μ§€κΈˆκΉŒμ§€μ˜ 생각듀을 κ°„λ‹¨νžˆ κ΅¬ν˜„ν•œ μ½”λ“œμ΄λ‹€.


~cpp 
RCObject::RCObject()
: refCount(0), shareable(true) {}

RCObject::RCObject(const RCObject&)
: refCount(0), shareable(true) {}

RCObject& RCObject::operator=(const RCObject&)
{ return *this; }

RCObject::~RCObject() {}                // νŒŒκ΄΄μžλŠ” 그것이 순수 가상 ν•¨μˆ˜λΌλ„
                                        // 항상 κ΅¬ν˜„λ˜μ–΄ μžˆμ–΄μ•Ό ν•œλ‹€.

void RCObject::addReference() { ++refCount; }

void RCObject::removeReference()
{   if (--refCount == 0) delete this; }

void RCObject::markUnshareable()
{ shareable = false; }

bool RCObject::isShareable() const
{ return shareable; }

bool RCObject::isShared() const
{ return refCount > 1; }
의문 ν•˜λ‚˜, ν₯λΈλ‘­κ²Œλ„ μ²˜μŒμ— refCountλΌ 0으둜 μ„ΈνŒ…ν•œλ‹€. 이건 직관적이지 λͺ»ν•˜κ²Œ λ³΄μ΄λŠ”λ°, ν™•μ‹€νžˆ μƒˆλ‘œ λ§Œλ“€μ–΄μ§€λŠ” RCObject의 μƒμ„±μžλŠ” μƒˆλ‘œ λ§Œλ“€μ–΄μ§„ RCObjectλΌ μ°Έμ‘°ν•˜κ³  μžˆμ„ 것이기 λ•Œλ¬Έμ΄λ‹€. ν•˜μ§€λ§Œ μ΄λŸ¬ν•œ μ΄μœ λŠ”, μƒˆλ‘œμš΄ 객체가 ν• λ‹Ήλœ 뒀에 μœ λ„λœ 클래슀 슀슀둜, refCountλΌ μ¦κ°€ μ‹œμΌœμ£ΌκΈ°μœ„ν•΄μ„œ 이닀. 이것이 μ‚¬μš©μžμ—κ²Œ 더 λͺ…μ‹œμ μΈ μ½”λ“œλΌ μž‘μ„±ν•˜κ²Œ λœλ‹€.

의문 λ‘˜, 볡사 μƒμ„±μžλŠ” refCountλΌ ν•­μƒ 0으둜 μ„ΈνŒ…ν•΄ 버린닀. 사싀 λ³΅μ‚¬ν• λ•ŒλŠ” refCountλŠ” 상관이 μ—†λ‹€. μ΄μœ λŠ” μƒˆλ‘œμš΄ 객체의 값이라, 이것은 항상 μ–΄λ–€ 객체와도 κ³΅μœ ν•˜κ³  μžˆμ§€ μ•ŠκΈ° λ•Œλ¬Έμ— refCountλΌ μ΄ˆκΈ°ν™” μ‹œν‚¨λ‹€.

의문 μ…‹, ν• λ‹Ή(assignment) μ—°μ‚°μžλŠ” μ•„κΉŒ 곡유 λ¬Έμ œλΌ λ’€μ§‘μ–΄ λ²„λ¦¬λŠ” 것 처럼 보인닀. ν•˜μ§€λ§Œ 이 κ°μ²΄λŠ” 기초 ν΄λž˜μŠ€μ΄λ‹€. μ–΄λ–€ μƒν™©μ—μ„œ μ΄λΌ μœ λ„ν•˜λŠ” λ‹€λ₯Έ ν΄λž˜μŠ€κ°€ 곡유 flagλΌ λΉ„ν™œμ„±ν™” μ‹œν‚€λŠ”μ§€ ν™•μ‹  ν• μˆ˜ μ—†κΈ° λ•Œλ¬Έμ—, 일단 아무것도 ν•˜μ§€ μ•Šκ³ , ν˜„μž¬μ˜ κ°μ²΄λΌ λ°˜ν™˜ν•˜κ²Œ ν•΄ λ‘μ—ˆλ‹€.

계속 ν•΄κΉ”λ¦΄μˆ˜ μžˆλŠ”λ°, λ‹€μŒκ³Ό 같은 μ½”λ“œλΌ λ³΄λ©΄

~cpp 
sv1 = sv2;          // μ–΄λ–»κ²Œ sv1κ³Ό sv2의 μ°Έμ‘° μΉ΄μš΄ν„°κ°€ 영ν–₯ λ°›μ„κΉŒ?
자, RCObjectλŠ” 기초 클래슀이며, μ°¨ν›„ λ°μ΄ν„°λΌ μ €μž₯ν•˜λŠ” 곡간은 RCObject의 μžμ‹μ—κ²Œμ„œ κ΅¬ν˜„λ˜μ–΄ 진닀. κ·Έλž˜μ„œ opreator=은 μœ λ„ 된 ν΄λž˜μŠ€μ—μ„œ μ›ν• λ•Œ 이 μ°Έμ‘° κ°μ²΄λΌ λ°”κΎΈκΈ° μœ„ν•˜μ—¬, RCObject::operator= ν˜•μ‹μœΌλ‘œ λΆˆλŸ¬μ•Ό ν•œλ‹€. μ΄ν•˜, κ±°μ˜λ‹€ μ†ŒμŠ€ μžμ²΄λΌ μ΄ν•΄ν•΄μ•Ό ν•œλ‹€. ν•΄λ‹Ή RCObjectλΌ μ΄μš©ν•΄μ„œ μœ λ„λœ 클래슀의 λͺ¨μ–‘은 λ‹€μŒκ³Ό κ°™μœΌλ©°, λͺ¨λ“  μ½”λ“œλŠ” κ΅¬ν˜„ν•˜μ§€ μ•Šκ² μ§€λ§Œ, μ§€κΈˆκΉŒμ§€μ˜ κ°œλ…μœΌλ‘œ μ΄ν•΄ν• μˆ˜ μžˆμ„ 것이닀.

~cpp 
class String {
private:
  struct StringValue: public RCObject {
    char *data;

    StringValue(const char *initValue);
    ~StringValue();

  };

...

};

String::StringValue::StringValue(const char *initValue)
{
  data = new char[strlen(initValue) + 1];
  strcpy(data, initValue);
}

String::StringValue::~StringValue()
{
  delete [] data;
}
이 StringValue버전은 이전에 λ³΄λ˜κ²ƒκ³Ό 거의 λ™μΌν•œ ν˜•νƒœλΌ λ³΄μΈλ‹€.단, refCountλΌ λ‹€λ£¨λŠ” κ²ƒλ§Œμ΄ RCObject의 멀버 ν•¨μˆ˜λ‘œμ„œ λͺ¨λ“  κΈ°λŠ₯을 ν›„ν–‰ν•˜λŠ” 것이닀. 이 κ²ƒμ˜ 더 ꡬ체적인 μ½”λ“œλŠ” λ‹€μŒ μž₯μ—μ„œ λ³΄μ΄λŠ” 슀마트 ν¬μΈν„°λΌ μ΄μš©ν•œ μžλ™ μ°Έμ‘° μΉ΄μš΄νŒ… κΈ°λŠ₯을 λ§λΆ™μ΄λ©΄μ„œ μ–΄μ°¨ν”Ό λͺ¨λ“  μ½”λ“œλΌ μ„œμˆ ν•  것이닀.

이번 주제의 nested class의 λͺ¨μŠ΅μ„ 보면 그리 쒋와 λ³΄μ΄μ§€λŠ” μ•Šμ„ 것이닀.μ²˜μŒμ—λŠ” μƒμ†Œν•˜κ² μ§€λ§Œ, μ΄λŸ¬ν•œ 방버은 μ •λ‹Ήν•œ 것이닀. nested클래슀의 방법은 μˆ˜λ§Žμ€ 클래슀의 μ’…λ₯˜μ€‘ 단지 ν•˜λ‚˜ 뿐이닀. 이것을 νŠΉλ³„νžˆ μ·¨κΈ‰ν•˜μ§€λŠ” 말아라.

1.6. Automating Reference Count Manipulations : μžλ™ μ°Έμ‘° μ„ΈκΈ° 방법

RCObjectλŠ” μ°Έμ‘° μΉ΄μš΄ν„°λΌ λ³΄κ΄€ν•˜κ³ , μΉ΄μš΄ν„°μ˜ 증감을 ν•΄λ‹Ή 클래슀의 멀버 ν•¨μˆ˜λ‘œ μ§€μ›ν•œλ‹€. ν•˜μ§€λ§Œ 이건 μœ λ„λ˜λŠ” λ‹€λ₯Έ ν΄λž˜μŠ€μ— μ˜ν•˜μ—¬, μ†μˆ˜ 코딩이 λ˜μ–΄μ•Ό ν•œλ‹€. 즉, μœ„μ˜ 경우라면, StirngValue ν΄λž˜μŠ€μ—μ„œ addReference, removeReference λΌ ν˜ΈμΆœν•˜λ©΄μ„œ, 일일이 μ½”λ”©ν•΄ μ£Όμ–΄μ•Ό ν•œλ‹€. 이것은 μž¬μ‚¬μš© ν΄λž˜μŠ€λ‘œμ„œ 보기 μ•Šμ’‹μ€λ°, 이것을 μ–΄λ–»κ²Œ μžλ™ν™” ν• μˆ˜λŠ” μ—†μ„κΉŒ? C++λŠ” 이런 μž¬μ‚¬μš©μ„ μ§€μ›ν• μˆ˜ μžˆμ„κΉŒ.

μœ λ„ 받은 ν΄λž˜μŠ€μ—μ„œ μ°Έμ‘° μΉ΄μš΄ν„°μ— κ΄€λ ¨ν•œ μ½”λ“œλΌ μ—†μ• κΈ°, 말처럼 μ‰¬μš΄ 일은 μ•„λ‹Œκ²ƒ κ°™λ‹€. 거기에닀가 String같은 ν˜•μ˜ κ΅¬ν˜„μ„ μœ„ν•΄μ„œλŠ” non-const operator[]에 λ”°λ₯Έ 곡유 μ—¬λΆ€κΉŒμ§€ λ”°μ Έ μ€˜μ•Ό ν•˜μ§€ λ”μš± λ³΅μž‘ν•΄ μ§€λŠ” λ¬Έμ œμ΄λ‹€. ν˜„μž¬ String의 값을 λ³΄κ΄€ν•˜λŠ”κ²ƒμ€ StringValue인데 μ΄λ ‡κ²Œ ν‘œν˜„λ˜μ–΄ μžˆλ‹€.

~cpp 
class String {
private:
  struct StringValue: public RCObject { ... };
  StringValue *value;                // 이 String의 κ°’
  ...
};
StringValue 객체의 refCount의 증감을 μœ„ν•΄μ„œλŠ” μƒλ‹Ήνžˆ λ§Žμ€ μ–‘μ˜ 코딩이 λ“€μ–΄κ°€μ•Ό 할것이닀. λ³΅μ‚¬ν• λ•Œ, 재 ν• λ‹Ή λ°›μ„λ•Œ, 그리고 ν•΄λ‹Ή 값이 νŒŒκ΄΄λ˜μ–΄ μ§ˆλ•Œ, 이런 λͺ¨λ“ κ²ƒμ„ μžλ™ν™” μ‹œν‚€λŠ” 것이 이번 μž₯의 λͺ©μ μ΄λ‹€. μ—¬κΈ°μ„œ StringValueλΌ μ „μž₯에 μ–ΈκΈ‰ν–ˆλ˜ 슀마트 포인터 κΈ°μˆ μ„ μ μš©ν•΄μ„œ 이런 μ°Έμ‘° 세기에 κ΄€ν•΄μ„œ μž¬μ‚¬μš©μ„ λ„˜μ–΄ μžλ™ν™”λΌ μ‹œμΌœ 보자.

슀마트 포인터에 λŒ€ν•œ μ„λͺ…은 λ„ˆλ¬΄ λ°©λŒ€ν•˜λ‹€. ν•˜μ§€λ§Œ μ—¬κΈ° RCObjectλΌ κ°€λ¦¬ν‚¬ 슀마트 포인터가 가지고 μžˆμ„ λŠ₯λ ₯은 멀버 선택(->), μ—­μ°Έμ‘°(deferencing, *) μ—°μ‚°μž μ •λ„λ§Œ 있으면 μΆ©λΆ„ν•˜λ‹€. λ¬Όλ‘  λ³΅μ‚¬λ‚˜, 생성은 기본이고 말이닀. μ°Έμ‘° μ„ΈκΈ°λΌ μœ„ν•œ 슀마트 포인터 ν…œν”Œλ¦Ώμ„ RCPtr이라고 λͺ…λͺ…ν•˜κ³ , 기본적인 λΌˆλŒ€λΌ λ‹€μŒκ³Ό 같이 κ΅¬μ„±ν•œλ‹€.

~cpp 
// T κ°μ²΄λΌ κ°€λ¦¬ν‚€κΈ° μœ„ν•œ 슀마트 포인터 클래슀 ν…œν”Œλ¦Ώ
// μ—¬κΈ°μ—μ„œ TλŠ” RCObjectλ‚˜, 이것을 상속 λ°›λŠ” ν΄λž˜μŠ€μ΄λ‹€.
template<class T>
class RCPtr {
public:
    RCPtr(T* realPtr = 0);
    RCPtr(const RCPtr& rhs);
    ~RCPtr();
    RCPtr& operator=(const RCPtr& rhs);
    T* operator->() const;              // Item 28 μ°Έκ³ 
    T& operator*() const;               // Item 28 μ°Έκ³ 
private:
    T *pointee;                         // μ›λž˜μ˜ ν¬μΈν„°λΌ κ°€λ¦¬ν‚€κΈ°μœ„ν•œ
                                        // 더λΈ(dumb) 포인터 pointer this
    void init();                        // λ³΄ν†΅μ˜ μ΄ˆκΈ°ν™” 루틴
};
μœ„μ—μ„œ μ–ΈκΈ‰ν–ˆλ“이, ν…œν”Œλ¦Ώμ˜ λͺ©μ μ€ RCObject의 refCount의 증감을 μžλ™ν™”ν•˜κΈ° μœ„ν•œ 것이닀. μ˜ˆλΌ λ“€μ–΄μ„œ, RCPtr이 μƒμ„±λ λ•Œ κ°μ²΄λŠ” μ°Έμ‘° μΉ΄μš΄ν„°λΌ μ¦κ°€μ‹œν‚€ν‚€ 원할 것이고, RCPtr의 μƒμ„±μžκ°€ μ΄λΌ μˆ˜ν–‰ν•˜κΈ° λ•Œλ¬Έμ— μ΄μ „μ²˜λŸΌ 일일이 μ½”λ”©ν•  ν•„μš”κ°€ 없을 것이닀. 일단, 생성 λΆ€λΆ„μ—μ„œμ˜ μ¦κ°€λ§Œμ„ 생각해 λ³Έλ‹€.

~cpp 
template<class T>
RCPtr<T>::RCPtr(T* realPtr): pointee(realPtr)
{
    init();
}
template<class T>
RCPtr<T>::RCPtr(const RCPtr& rhs): pointee(rhs.pointee)
{
    init();
}
template<class T>
void RCPtr<T>::init()
{
    if (pointee == 0) {                // λ§Œμ•½ 더λΈ(dumb)포인터가 null이라면
        return;                          
    }
    if (pointee->isShareable() == false) {      // κ³΅μœ λΌ ν—ˆμš©ν•˜μ§€ μ•ŠμœΌλ©΄
        pointee = new T(*pointee);              // λ³΅μ‚¬λΌ ν•œλ‹€.     
    }
    pointee->addReference();             // μƒˆλ‘œμš΄ μ°Έμ‘° μΉ΄μš΄ν„°λΌ μ˜¬λ¦°λ‹€.
}
κ³΅ν†΅λœ μ½”λ“œλΌ init()으둜 λ¬Άμ—ˆλŠ”λ°, 이런 경우 μ •ν™•νžˆ λ™μž‘ν•˜μ§€ μ•Šμ„μˆ˜ μžˆλ‹€. initν•¨μˆ˜κ°€ μƒˆλ‘œμš΄ 볡사본을 ν•„μš”λ‘œ ν• λ•Œκ°€ 그것인데, κ³΅μœ λΌ ν—ˆμš©ν•˜μ§€ μ•Šμ„ κ²½μš°μ— λ³΅μ‚¬ν•˜λŠ” λ°”λ‘œ 이뢀뢄,

~cpp 
pointee = new T(*pointee);
opintee의 ν˜•μ€ pointer-to-T이닀. μ•„λ§ˆ Stringν΄λž˜μŠ€μ—μ„œλŠ” 이것이 String::StringValue의 볡사 μƒμ„±μž 일것인데, μš°λ¦¬λŠ” StringValue의 볡사 μƒμ„±μžλΌ μ„ μ–Έν•΄ 주지 μ•Šμ•˜λ‹€. κ·Έλž˜μ„œ μ»΄νŒŒμΌλŸ¬κ°€ μžλ™μ μœΌλ‘œ C++λ‚΄μ˜ κ·œμΉ™μ„ μ§€ν‚€λ©΄μ„œ 볡사 μƒμ„±μžλΌ λ§Œλ“€μ–΄ 낼것이닀. κ·Έλž˜μ„œ λ³΅μ‚¬λŠ” 였직 StringValue의 data 포인터에 ν•΄λ‹Ήν•˜λŠ” κ²ƒλ§Œμ΄ 이루어 μ§ˆκ²ƒμ΄λ‹€. dataλŠ” 볡사가 μ•„λ‹Œ μ°Έμ‘°κ°€ ν–‰ν•΄ μ§ˆκ²ƒμ΄λ‹€. μ΄λŸ¬ν•œ 이유둜 이후, μž‘μ„±λ˜λŠ” λͺ¨λ“  ν‹€λž˜μŠ€μ— 볡사 μƒμ„±μžκ°€ μΆ”κ°€λ˜μ–΄μ•Ό ν•œλ‹€.

RCPtr<T>의 μ •ν™•ν•œ μˆ˜ν–‰μ„ μœ„ν•΄μ„œ TλŠ” 볡사 μƒμ„±μžλΌ κ°€μ§€κ³  μžˆμ–΄μ„œ, λ…λ¦½μ μœΌλ‘œ λ³΅μ‚¬λΌ μˆ˜ν–‰ν•΄μ•Ό ν•œλ‹€.(λ‹€λ₯Έ 말둜 deep copyκ°€ 이루어 μ Έμ•Ό ν•œλ‹€.)

~cpp 
class String {
private:
    struct StringValue: public RCObject {
        StringValue(const StringValue& rhs);
        ...
    };
    ...
};
String::StringValue::StringValue(const StringValue& rhs)
{
    data = new char[strlen(rhs.data) + 1];      // 이 뢀뢄이 deep copy의 
    strcpy(data, rhs.data);                     // μˆ˜ν–‰ 뢀뢄이닀.
}
μ΄λŸ¬ν•œ deep-copy 볡사 μƒμ„±μžμ˜ μ‘΄μž¬λŠ” RCPtr<T>κ°€ κ°€λ¦¬ν‚€λŠ” T에 κ΅­ν•œ λ˜λŠ” 것이 μ•„λ‹ˆλΌ, RCObjectλΌ μƒμ†ν•˜λŠ” ν΄λž˜μŠ€μ—λ„ μ§€μ›λ˜μ–΄μ•Ό ν•œλ‹€. 사싀 RCPtr 객체 κ΄€μ μ—μ„œ λ³Όλ•ŒλŠ” 였직 μ°Έμ‘° μ„ΈκΈ°λΌ ν•˜λŠ” κ°μ²΄λΌ κ°€λ¦¬ν‚€κΈ°λ§Œ ν•˜λŠ” κ΄€μ μ—μ„œ 이것은 λ‚©λ“ν• μˆ˜ μ—†λŠ” 사싀일지 λͺ¨λ₯Έλ‹€. κ·Έλ ‡μ§€λ§Œ μ΄λŸ¬ν•œ 사싀이 κΌ­ λ¬Έμ„œν™” λ˜μ–΄ ν΄λΌμ΄μ–ΈνŠΈμ—κ²Œ μ•Œλ €μ Έμ•Ό ν•œλ‹€.

λ§ˆμ§€λ§‰ RCPtr<T>μ—μ„œμ˜ 가성은 T에 λŒ€ν•œ ν˜•μ— κ΄€ν•œ 것이닀. 이것은 ν™•μ‹€ν•˜κ²Œ 보인닀. κ²°κ΅­ pointeeλŠ” T* ν˜•μœΌλ‘œ μ„ μ–Έλ˜λŠ”λ°, pointeeλŠ” μ•„λ§ˆ T둜 λΆ€ν„° μœ λ„λœ 클래슀 일지 λͺ¨λ₯Έλ‹€. μ˜ˆλΌ λ“€μžλ©΄ λ§Œμ•½ 당신이 SepcialStringValueλΌλŠ” String::StringValueμ—μ„œ μœ λ„λœ ν΄λž˜μŠ€κ°€ 쑴재 ν•œλ‹€λ©΄

~cpp 
class String {
private:
  struct StringValue: public RCObject { ... };
  struct SpecialStringValue: public StringValue { ... };
  ...
};
일단 μš°λ¦¬λŠ” String에 RCPtr<StingValue> ν¬μΈν„°λ‘œ SpecialStringValue κ°μ²΄λΌ κ°€λ¦¬ν‚€λŠ” μƒνƒœλ‘œ λ§Œλ“€μ–΄μ„œ ν¬ν•¨μ‹œν‚¬μˆ˜ μžˆλ‹€. μ΄λŸ¬ν•œ κ²½μš°μ— μš°λ¦¬κ°€ μ›ν•˜λŠ” initκ°€ λ˜μ§€ μ•ŠλŠ”λ‹€.

~cpp 
pointee = new T(*pointee);      // T λŠ” StringValue이닀, ν•˜μ§€λ§Œ 
                                // pointeeκ°€ κ°€λ¦¬ν‚€λŠ”κ±΄ SpecialStringVale이닀.
즉, SepcialStringValue의 볡사 μƒμ„±μžλΌ λΆ€λ₯΄κΈΈ μ›ν•˜λŠ”λ°, StringValue의 볡사 μƒμ„±μžκ°€ λΆˆλ¦°λ‹€. μš°λ¦¬λŠ” μ΄λŸ¬ν•œ 것을 가상 볡사 μƒμ„±μžλΌ μ¨μ„œ (Item 25 μ°Έκ³ ) ν• μˆ˜ μžˆλ‹€. ν•˜μ§€λ§Œ, 이것은 λ””μžμΈλ•Œ μ˜λ„ν•œ 것이 μ•„λ‹ˆκΈ° λ•Œλ¬Έμ—, μ΄λŸ¬ν•œ κ²½μš°μ— λŒ€ν•΄μ„œλŠ” λ‚©λ“ν• μˆ˜κ°€ μ—†λ‹€.

이제, μƒμ„±μžμ— κ΄€ν•œ λ‚΄μš©μ„ λ²—μ–΄λ‚˜, ν• λ‹Ή(assignment) μ—°μ‚°μžμ— κ΄€ν•œ λ‚΄μš©μ„ 닀룬닀. ν• λ‹Ή κ²½μš°λ„ 곡유의 κ²½μš°λΌ μƒκ°ν•΄μ•Ό ν•˜κΈ° λ•Œλ¬Έμ— κΉŒλ‹€λ‘­κ²Œ 될수 μžˆλŠ”λ°, λ‹€ν–‰μŠ€λŸ½κ²Œ μ΄λΈ κ·ΈλŸ¬ν•œ 과정은 init에 κ³΅ν†΅μ μœΌλ‘œ λ“€μ–΄ 있기 λ•Œλ¬Έμ— μ½”λ“œκ°€ 간단해 진닀.

~cpp 
template<class T>
RCPtr<T>& RCPtr<T>::operator=(const RCPtr& rhs)
{
    if (pointee != rhs.pointee) {       // λ‘˜μ΄ 같은 κ°μ²΄λΌ κ³΅μœ ν•˜λ©΄ 
                                        // 할당을 μƒλž΅ν•œλ‹€.
        if (pointee) {
            pointee->removeReference(); // ν˜„μž¬ μ°Έμ‘° 객체 μ°Έμ‘° μΉ΄μš΄ν„° κ°μ†Œ or 파괴
        }
        pointee = rhs.pointee;          // κ°€λ¦¬ν‚€λŠ” μ°Έμ‘°λΌ μ˜κΈ΄λ‹€.
        init();                         // κ³΅μœ κ°€ λΆˆκ°€λŠ₯ ν• κ²½μš°λŠ” μžλ£ŒλΌ 
                                        // μƒμ„±ν•˜κ³ , μ–΄μ°Œλ˜μ—ˆλ“  μ°Έμ‘°λΌ μ¦κ°€
    }
    return *this;
}
파괴자느 γ„΄κ°„λ‹¨ν•˜λ‹€. κ·Έλƒ₯ μ°Έμ‘° μΉ΄μš΄ν„° ν•˜λ‚˜λΌ κ°μ†Œν•˜λ©΄ λœλ‹€.

~cpp 
template<class T>
RCPtr<T>::~RCPtr()
{
    if (pointee)pointee->removeReference(); // μ•Œμ•„μ„œ 파괴됨
}
μ£Όμ„μ—μ„œμ™€ 같이 removeReference() λŠ” μ°Έμ‘° μΉ΄μš΄ν„°κ°€ 0이되면 ν•΄λ‹Ή κ°μ²΄λΌ νŒŒκ΄΄ν•œλ‹€.

λ§ˆμ§€λ§‰μœΌλ‘œ, 이제 슀마트 ν¬μΈν„°μ˜ λ°±λΈμΈ 포인터 ν‰λ‚΄λ‚΄λŠ” 뢀뢄이닀.

~cpp 
template<class T>
T* RCPtr<T>::operator->() const { return pointee; }
template<class T>
T& RCPtr<T>::operator*() const { return *pointee; }

1.7. Putting it All Together : μ§€κΈˆκΉŒμ§€μ˜ 방법둠 정리

λ“œλ””μ–΄ 끝이닀. 슀마트 포인터와, 기초 클래슀둜 이제 μž¬μ‚¬μš© κ°€λŠ₯ν•œ μ°Έμ‘°μ„ΈκΈ°λΌ κ΅¬ν˜„ν• μˆ˜ μžˆλ‹€. μ§€κΈˆκΉŒμ§€ ν•˜λ‚˜ν•˜λ‚˜ λ§Œλ“€μ–΄μ˜¨ 것을 μ΄λ²ˆμ— λͺ¨λ‘ ν•©μ³μ„œ ν‘œν˜„ν•΄ 보자.

λ¨Όμ €, RCObject와 RCPtr을 μ΄μš©ν•΄μ„œ String에 κ΅¬ν˜„ν•œ μ°Έμ‘°μ„ΈκΈ°μ˜ κ°œλ…λ„λΌ μ•Œμ•„ 보자.


μ΄λŸ¬ν•œ κ°œλž΅λ„λŠ” λ‹€μŒκ³Ό 같은 클래슀둜 μ •μ˜ λ˜μ–΄ 진닀.

~cpp 
template<class T>                       // TλΌ κ°€λ¦¬ν‚€λŠ” 슀마트 포인터
class RCPtr {                           // μ—¬κΈ°μ—μ„œ TλŠ” RCObjectλΌ μƒμ†ν•΄μ•Ό ν•œλ‹€.
public:                                 
    RCPtr(T* realPtr = 0);              // μ΄ˆκΈ°ν™”, μƒμ„±μž κ²Έ(κΈ°λ³Έκ°’ λ•Œλ¬Έμ—)
    RCPtr(const RCPtr& rhs);            // 볡사 μƒμ„±μž
    ~RCPtr();

    RCPtr& operator=(const RCPtr& rhs); // ν• λ‹Ή μ—°μ‚°μž

    T* operator->() const;              // 포인터 흉내
    T& operator*() const;               // 포인터 흉내

private:
    T *pointee;                         // 더λΈ(dumb) 포인터
    void init();                        // μ°Έμ‘° μ„ΈκΈ° μ΄ˆκΈ°ν™”(μ°Έμ‘° μΉ΄μš΄ν„° 증가)
};

class RCObject {                        // μ°Έμ‘°μ„ΈκΈ°μ˜ 기초 클래슀
public:
    void addReference();                // μ°Έμ‘° μΉ΄μš΄ν„° 증가
    void removeReference();             // μ°Έμ‘° μΉ΄μš΄ν„° κ°μ†Œ

    void markUnshareable();             // 곡유 막기
    bool isShareable() const;           // 곡유 해도 λ˜λŠ”κ°€ 묻기
    bool isShared() const;              // ν˜„μž¬ 곡유 쀑인가 묻기

protected:
    RCObject();                         // μƒμ„±μž
    RCObject(const RCObject& rhs);      // 볡사 μƒμ„±μž
    RCObject& operator=(const RCObject& rhs);   // ν• λ‹Ή μ—°μ‚°μž
    virtual ~RCObject() = 0;            // 순수 가상 파괴자
                                        // (μˆœμˆ˜μ§€λ§Œ λ°˜λ“œμ‹œ κ΅¬ν˜„λ˜μ–΄μ•Ό ν•œλ‹€.)
private:
    int refCount;                       // μ°Έμ‘° μΉ΄μš΄ν„°
    bool shareable;                     // 곡유 ν”Œλž˜μŠ€(곡유 ν—ˆμš© μ—¬λΆ€)
};

// 처음 보닀 많이 빠진 String같지 μ•Šμ€κ°€? ^^;
class String{                           // application κ°œλ°œμžκ°€ μ‚¬μš©ν•˜λŠ” 클래슀
public:
    String(const char *value = "");     // μƒμ„±μž, μ΄ˆκΈ°ν™”

    const char& operator[](int index) const;    // const operator[] 
    char& operator[](int index);                // non-const operator[]

private:
    // 클래슀 내뢀에 ν‘œν˜„μ„ μœ„ν•œ λ¬Έμžμ—΄ κ°’
    struct StringValue: public RCObject {
        char *data;                             // 데이터 포인터

        StringValue(const char *initValue);     // μƒμ„±μž
        StringValue(const StringValue& rhs);    // 볡사 μƒμ„±μž
        void init(const char *initValue);       // μ°Έμ‘° μ„ΈνŒ…
        ~StringValue();                         // 파괴자
    };

    RCPtr<StringValue> value;                   // StringValue의 슀마트 포인터                
};
λŒ€λ‹€μˆ˜μ˜ λΆ€λΆ„μ—μ„œ μš°λ¦¬λŠ” μ΄λΈ λ§Œλ“€ 것을 κ·ΈλŒ€λ‘œ 썼닀. κ·Έλž˜μ„œ νŠΉλ³„νžˆ 신경써야 할것은 μ—†λ‹€. 유의 ν•΄μ•Ό 할것은 String::StringValue에 λ³΄μ΄λŠ” init ν•¨μˆ˜μ—μ„œ μ‹ κ²½ 써야 ν•œλ‹€.

자 μ€‘μš”ν•œ 이 μ•„μ΄ν…œμ„ 처음 μ‹œμž‘ν• λ•Œ String클래슀의 μΈν„°νŽ˜μ΄μŠ€μ™€ λ‹€λ₯Έ 점은, 볡사 μƒμ„±μžλŠ” μ–΄λ”” μžˆλŠ”κ°€? ν• λ‹Ή(assignment) μ—°μ‚°μžλŠ” μ–΄λ”” μžˆλŠ”κ°€? νŒŒκ΄΄μžλŠ” μ–΄λ”” μžˆλŠ”κ°€? 정말 μ‹¬κ°ν•œ 잘λͺ»μœΌλ‘œ 보이지 μ•ŠλŠ”κ°€?ν•˜μ§€λ§Œ κ±±μ • 할것없닀. 사싀 이 κ΅¬ν˜„ ν˜•νƒœλŠ” μ™„μ „ν•˜λ‹€. λ§Œμ•½ μ΄μœ λΌ λͺ¨λ₯΄κ² μœΌλ©΄, C++을 μ€λ” 곡뢀해라. (μž‘μ„±μžμ£Ό:이런 건방진 말투둜 λ°”κΎΈλŠ”..)

μ΄μ œλŠ” μ΄λŸ¬ν•œ ν•¨μˆ˜λ“€μ΄ 더이상 ν•„μš” μ—†λ‹€. λ¬Όλ‘  아직 String객체의 λ³΅μ‚¬λŠ” μ§€μ›λœλ‹€. λ³΅μ‚¬λŠ” μ°Έμ‘°μ„ΈκΈ°λΌ κΈ°λ°˜ν•œ StringValueκ°μ²΄μ—μ„œ 이루어 μ§€λŠ” κ²ƒμœΌλ‘œ, Stringν΄λž˜μŠ€λŠ” 닀이성 이런 것을 μœ„ν•΄μ„œ ν•œμ„도 μ½”λ”©ν•  ν•„μš”κ°€ μ—†λ‹€. κ·Έ μ΄μœ λŠ” 이제 μ»΄νŒŒμΌλŸ¬κ°€ λ§Œλ“€ 볡사 μƒμ„±μžμ—κ²Œ λͺ¨λ‘ 맑겨 버리면 κΈ°νƒ€μ˜ 것듀은 λͺ¨λ‘ μžλ™μœΌλ‘œ μƒμ„±λœλ‹€. RCPtr은 슀마트 포인터이닀. 그사싀을 κΈ°μ–΅ν•˜λΌ, λ³΅μ‚¬μ‹œ λͺ¨λ‘ 슀마트 포인터가 μ°Έμ‘°λΌ κ΄€λ¦¬ν•΄μ€λ‹€.

자, 이제 μ „μ²΄μ˜ κ΅¬ν˜„μ½”λ“œλΌ λ³΄μ—¬μ„ μ°¨λ€μ΄λ‹€. λ¨Όμ € RCObject의 κ΅¬ν˜„ 상황이닀.

~cpp 
RCObject::RCObject()
: refCount(0), shareable(true) {}

RCObject::RCObject(const RCObject&)
: refCount(0), shareable(true) {}

RCObject& RCObject::operator=(const RCObject&)
{ return *this; }

RCObject::~RCObject() {}

void RCObject::addReference() { ++refCount; }

void RCObject::removeReference() 
{ if (--refCount == 0) delete this; }

void RCObject::markUnshareable() { shareable = false; }

bool RCObject::isShareable() const { return shareable; }

bool RCObject::isShared() const { return refCount > 1; }
λ‹€μŒμ€ RCPtr의 κ΅¬ν˜„ μ½”λ“œμ΄λ‹€.

~cpp 
template<class T>
void RCPtr<T>::init()
{
    if (pointee == 0) return;
    if (pointee->isShareable() == false) {
        pointee = new T(*pointee);
    }
    pointee->addReference();
}

template<class T>
RCPtr<T>::RCPtr(T* realPtr)
: pointee(realPtr)
{ init(); }

template<class T>
RCPtr<T>::RCPtr(const RCPtr& rhs)
: pointee(rhs.pointee)
{ init(); }

template<class T>
RCPtr<T>::~RCPtr() 
{ if (pointee)pointee->removeReference(); }

template<class T>
RCPtr<T>& RCPtr<T>::operator=(const RCPtr& rhs)
{
    if (pointee != rhs.pointee) {
        if (pointee) pointee->removeReference();
        pointee = rhs.pointee;
        init();
    }
    return *this;
}

template<class T>
T* RCPtr<T>::operator->() const { return pointee; }

template<class T>
T& RCPtr<T>::operator*() const { return *pointee; }
λ‹€μŒμ€ String::StringValue의 κ΅¬ν˜„ μ½”λ“œμ΄λ‹€.

~cpp 
void String::StringValue::init(const char *initValue)   // deep copyλΌ μœ„ν•΄μ„œ 
{
    data = new char[strlen(initValue) + 1];             // μžλ£ŒλΌ λ³΅μ‚¬ν•˜λŠ”
    strcpy(data, initValue);                            // κ³Όμ • κ°œλ°œμžκ°€ 신경써야 ν•œλ‹€.
}

String::StringValue::StringValue(const char *initValue) // 볡사 μƒμ„±μž(자료 기반)
{ init(initValue); }

String::StringValue::StringValue(const StringValue& rhs)// 볡사 μƒμ„±μž(같은 객체 기반)
{ init(rhs.data); }

String::StringValue::~StringValue()
{ delete [] data; }

이제 λͺ¨λ“ κ±Έ κ°μ‹ΈλŠ” String 클래슀의 κ΅¬ν˜„ μ½”λ“œμ΄λ‹€.

~cpp 
String::String(const char *initValue) 
: value(new StringValue(initValue)) {}      // valueλŠ” RCPtr<StringValue> 이닀.

const char& String::operator[](int index) const
{ return value->data[index]; }

char& String::operator[](int index)
{
    if (value->isShared()) {
        value = new StringValue(value->data);
    }
    value->markUnshareable();
    return value->data[index];
}
이 Stringν΄λž˜μŠ€λΌ μœ„ν•œ μ½”λ“œμ™€, κ·Έλƒ₯ 더λΈ(dumb)ν¬μΈν„°λΌ μ‚¬μš©ν•œ 클래슀(μ²˜μŒμ— μ°Έμ‘°μ„ΈκΈ° κ΅¬ν˜„ν•œκ²ƒ)μ™€λŠ” λ‘κ°€μ§€μ˜ 큰 차이점이 μžˆλ‹€. 첫번째둜 이 클래슀의 μ½”λ“œκ°€ ꡉμž₯히 μ λ‹€λŠ” 점이닀. μ΄μœ λŠ”, RCPtr이 μ°Έμ‘°μ„ΈλŠ” μž‘μ—…μ„ λͺ¨λ‘ λ§‘μ•„μ„œ 이닀. λ‘λ²ˆμ§Έλ‘œλŠ” 슀마트 ν¬μΈν„°λ‘œ κ΅μ²΄ν–ˆμ§€λ§Œ, String의 μ½”λ“œκ°€ 거의 μœ μ§€λœλ‹€λŠ” 점이닀. 사싀 λ³€ν™”λŠ” operator[]μ—μ„œλ§Œ 곡유의 κ²½μš°λΌ μ²΄ν¬ν•˜λŠ” 루틴 λ•Œλ¬Έμ— λ°”λ€Œμ—ˆλ‹€. μ΄λ ‡κ²Œ 슀마트 ν¬μΈν„°λ‘œμ„œ μ†μˆ˜ ν•΄μ•Όν•˜λŠ” μž‘μ—…λ“€μ΄ 많이 μ„μ–΄ λ“ λ‹€.

λŒ€λ‹¨ν•˜μ§€ μ•Šμ€κ°€? λˆ„κ°€ κ°μ²΄λΌ μ‚¬μš©ν•˜μ§€ μ•Šμ„κΉŒ? λˆ„κ°€ μΊ‘μŠν™”λΌ λ°˜λŒ€ν• κΉŒ? ν•˜μ§€λ§Œ, μ΄λŸ¬ν•œ μ‹ κΈ°ν•œ String ν΄λž˜μŠ€μ— κ΄€ν•œ 기반 생각은 ν΄λΌμ΄μ–ΈνŠΈ μΈ‘μ—μ„œ μƒˆλΆ€μ‚¬ν•­μ„ μ•Œν•„μš”κ°€ μ—†μ–΄μ•Ό 밑이 λ‚˜λŠ” 것이닀. μ•Œμ•„μ•Ό 할것이 μ—†μ„μˆ˜λ‘ 더 쒋은 μƒνƒœμ΄λ‹€. ν˜„μž¬, String을 μ“°λŠ” κΈ°λ³Έ μΈν„°νŽ˜μ΄μŠ€λŠ” 바뀐것이 μ—†λ‹€. 단지 μ°Έμ‘°μ„ΈκΈ°μ˜ κΈ°λŠ₯이 μΆ”κ°€λ˜μ—ˆμ„ 뿐이닀. κ·Έλž˜μ„œ ν΄λΌμ΄μ–ΈνŠΈλŠ” κΈ°μ‘΄ μ½”λ“œλΌ κ³ μΉ  ν•„μš”κ°€ μ—†λ‹€. 단, 재 컴파일(recompile)κ³Ό 재링크(relink) κ³Όμ •λ§Œμ΄ 남아 μžˆμ„ 것이닀. μ΄λŸ¬ν•œ λΉ„μš©μ€ μ°Έμ‘°μ„ΈκΈ°κ°€ μ£ΌλŠ” 이득에 λΉ„ν•˜λ©΄ 정말 μ™„μ „νžˆ μ—†λŠ” λΉ„μš©μ΄λ‚˜ λ§ˆμ°¬κ°€μ§€μ΄λ‹€. μΊ‘μŠν™”λŠ” 정말 쒋은거닀. (μž‘μ„±μžμ£Ό:뭐야 이 결둠은..)

1.8. Adding Reference Counting to Exitsting Classes : μ°Έμ‘° μ„ΈκΈ°λΌ μ΄λΈ μ‘΄μž¬ν•˜λŠ” ν΄λž˜μŠ€μ— λ”ν•˜κΈ°

휴, μš°λ¦¬λŠ” μ§€κΈˆκΉŒμ§€ ν₯λΈλ‘œμš΄ ν΄λž˜μŠ€μ— κ΄€ν•΄μ„œ λ…Όμ˜ ν–ˆλŠ”λ°, μ΄λ²ˆμ—λŠ” Widget같은 μ΄λΈ μ •μ˜λ˜μ–΄ μžˆλŠ” ν΄λž˜μŠ€λΌ μ „ν˜€ κ±΄λ“œλ¦¬μ§€ μ•Šκ³ , μ°Έμ‘° μ„ΈκΈ°λΌ μ μš© μ‹œν‚¬μˆ˜λŠ” μ—†μ„κΉŒ? κ·ΈλŸ¬λ‹ˆκΉŒ. λΌμ΄λΈŒλŸ¬λ¦¬λΌ κ³ μΉ˜μ§€ μ•Šκ³  말이닀. Widget에 μ°Έμ‘° μ„ΈκΈ°λΌ μ μš© μ‹œμΌœμ•Ό ν•˜λŠ”λ°, μ•žμ˜ 방법 처럼 RCObjectλΌ Widget이 상속 μ‹œν‚¬ 방법은 μ „ν˜€ μ—†λ‹€. κ·Έλž˜μ„œ RCPtr도 μ μš©ν• μˆ˜ μ—†λ‹€. 방법이 μ—†λŠ” 걸까?

κ°„λ‹¨νžˆ 말해 μš°λ¦¬λŠ” 쑰금만 우리의 λ””μžμΈμ„ 손봐야 ν•œλ‹€. 일단 λ§Œμ•½ 우리의 κΈ°μ‘΄ λ””μžμΈμΈ String/StringValueκ΄€κ³„λΌ κ³ μˆ˜ν•˜λ©΄μ„œ Widget을 적용 μ‹œν‚€λŠ” 것을 κ°€μ •ν•΄ 보자. 그러면 λ””μžμΈμ€ λ‹€μŒκ³Ό 같을 것이닀.


이제 컴퓨터 우회적으둜 λ°©ν–₯을 λ°”κΎΈλŠ” λΆ€λΆ„(level)을 μΆ”κ°€ν•˜λŠ” λ°©λ²•μœΌλ‘œ 컴퓨터 과학이 μ²˜ν•œ μ»€λ‹€λž€ λ¬Έμ œλΌ ν•΄κ²°ν•΄ 보자. μƒˆλ‘œ 좔가될 ContHolderλŠ” μ°Έμ‘° μ„ΈκΈ° κΈ°λŠ₯을 κ΅¬ν˜„ν•˜κ³  있으며, λŒ€μ‹  RCPtr 클래슀 μ—­μ‹œ RCIPtr 클래슀둜 ν•œλ‹€.("I"λŠ” indirection(우회)의 의λΈλ‘œ 뢙은거닀.) 이런 λ””μžμΈμ€ λ‹€μŒκ³Ό 같은 λͺ¨μŠ΅μ„ 보일 것이닀.


κΈ°λ³Έ κ°œλ…μ€ StringValueμ—μ„œ 적용된 방식과 λΉ„μŠ·ν•˜λ‹€. CountHolderλŠ” RCWidget의 ν΄λΌμ΄μ–ΈνŠΈλ‘œ λΆ€ν„° κ΅¬ν˜„ 상황을 숨겨 버릴 것이닀. 사싀 μžμ„Έν•œ κ΅¬ν˜„μ€ RCIPtr에 거의 λ‹€λ˜μ–΄ μžˆλ‹€. κ·Έλž˜μ„œ 이 클래슀의 κ΅¬ν˜„ 상황을 보자.

~cpp 
template<class T>
class RCIPtr {
public:
    RCIPtr(T* realPtr = 0);
    RCIPtr(const RCIPtr& rhs);
    ~RCIPtr();

    RCIPtr& operator=(const RCIPtr& rhs);

    const T* operator->() const;                // μ„λͺ…κ³Ό κ΅¬ν˜„ μ½”λ“œλΌ λ³΄μž.
    T* operator->();                            //  ''
    const T& operator*() const;                 //  ''
    T& operator*();                             //  ''

private:
    struct CountHolder: public RCObject {
        ~CountHolder() { delete pointee; }
        T *pointee;
    };

    CountHolder *counter;

    void init();
    void makeCopy();                                // κ΅¬ν˜„ μ½”λ“œλΌ λ³΄μž.
};

template<class T>
void RCIPtr<T>::init()
{
    if (counter->isShareable() == false) {
        T *oldValue = counter->pointee;
        counter = new CountHolder;
        counter->pointee = new T(*oldValue);
    }

    counter->addReference();
}

template<class T>
RCIPtr<T>::RCIPtr(T* realPtr)
: counter(new CountHolder)
{
    counter->pointee = realPtr;
    init();
}

template<class T>
RCIPtr<T>::RCIPtr(const RCIPtr& rhs)
: counter(rhs.counter)
{ init(); }

template<class T>
RCIPtr<T>::~RCIPtr()
{ counter->removeReference(); }

template<class T>
RCIPtr<T>& RCIPtr<T>::operator=(const RCIPtr& rhs)
{
    if (counter != rhs.counter) {
        counter->removeReference();
        counter = rhs.counter;
        init();
    }
    return *this;
}

template<class T>
void RCIPtr<T>::makeCopy()                 // copy-on-write μƒν™©μ˜ κ΅¬ν˜„
{
    if (counter->isShared()) {
        T *oldValue = counter->pointee;
        counter->removeReference();
        counter = new CountHolder;
        counter->pointee = new T(*oldValue);
        counter->addReference();
    }
}

template<class T>                           // const μ ‘κ·Ό;
const T* RCIPtr<T>::operator->() const      // copy-on-writeλΌ λΉ„κ°μ•ˆν• 
{ return counter->pointee; }

template<class T>                           // non-const μ ‘κ·Ό
T* RCIPtr<T>::operator->()                  // copy-on-write κ°μ•ˆ
{ makeCopy(); return counter->pointee; }

template<class T>                           // const μ ‘κ·Ό;
const T& RCIPtr<T>::operator*() const       // copy-on-writeλΌ λΉ„κ°μ•ˆν• 
{ return *(counter->pointee); }

template<class T>                           // non-const μ ‘κ·Ό
T& RCIPtr<T>::operator*()                   // copy-on-write κ°μ•ˆ
{ makeCopy(); return *(counter->pointee); }
RCPPtr을 RCPtrκ³Ό 였직 두가지 μ μ—μ„œ λ‹€λ₯Έλ‹€. μ²«λ²ˆμ§ΈλŠ” RCIPtr이 쀑간 μ‘°μ •μžμΈ CountHolderν†΅ν•΄μ„œ μ ‘κ·Όν•˜λŠ” 것과 달리 RCPtr κ°μ²΄λŠ” 값을 직접 κ°€λ¦¬ν‚¨λ‹€λŠ” 점이닀. λ‘λ²ˆμ§Έλ‘œλŠ” operator->와 operator*을 μ˜€λ²„λ‘œλ“œ(overload)ν•΄μ„œ copy-on-write에 μžλ™μ μœΌλ‘œ λŒ€μ‘ν• μˆ˜ 있게 ν•˜μ˜€λ‹€.

그럼 RCIPtr에 λΉ„ν•˜μ—¬ RCWidget은 μƒλ‹Ήνžˆ κ΅¬ν˜„μ΄ κ°„λ‹¨ν•˜λ‹€. 거의 λͺ¨λ“  κΈ°λŠ₯이 RCIPtrμ—μ„œ κ΅¬ν˜„λ˜μ—ˆκ³ , RCWidget은 그것을 ν†΅ν•΄μ„œ 인자만 μ „λ‹¬ν•˜λ©΄ 되기 λ•Œλ¬Έμ΄λ‹€.(delegate) λ§Œμ•½ Widget이 λ‹€μŒκ³Ό 같이 생겼닀면

~cpp 
class Widget {
public:
    Widget(int size);
    Widget(const Widget& rhs);
    ~Widget();
    Widget& operator=(const Widget& rhs);
    void doThis();
    int showThat() const;
};

RCWidget은 μ•„λ§ˆ μ΄λ ‡κ²Œ κ΅¬ν˜„λ  것이닀.

~cpp 
class RCWidget {
public:
    RCWidget(int size): value(new Widget(size)) {}
    void doThis() { value->doThis(); }                  // delegateμ‹œμΌœ μ€λ‹€.
    int showThat() const { return value->showThat(); }  // delegateμ‹œμΌœ μ€λ‹€.
private:
    RCIPtr<Widget> value;
};
(μž‘μ„±μžμ£Ό: 기타 λ‚΄μš©μ€ κ·Έλƒ₯ 내뢀에 λŒ€ν•œ μ„λͺ…이닀. μƒλž΅ν•œλ‹€. λ‚˜μ€‘μ— μ‹œκ°„μ΄λ˜λ©΄ μΆ”κ°€)

1.9. Evaluation : 평가

μ§€κΈˆκΉŒμ§€, widget, string, κ°’(value), 슀마트 포인터(smart pointer), μ°Έμ‘° μ„ΈκΈ° κΈ°λ³Έ 클래슀(reference-counting base class)에 κ΄€ν•΄μ„œ ꡬ체적인 뢀뢄을 닀루어 μ™”λ‹€. 이 λͺ¨λ“  것은 μš°λ¦¬μ—κ²Œ μ°Έμ‘° μ„ΈκΈ°λΌ μ μš©ν• μˆ˜ μžˆλŠ” 넓은 폭을 κ°€μ Έλ‹€ μ£Όμ—ˆλ‹€. 이제 쑰금 일반적인 μ΄μ•ΌκΈ°λ‘œ, μ§ˆλ¬Έν•΄ 보자. λ‹€μ‹œ λ§ν•˜μžλ©΄, λŒ€μ²΄ μ–Έμ œ μ°Έμ‘° μ„ΈκΈ°μ˜ κΈ°μˆ μ„ 적용 μ‹œμΌœμ•Ό ν• κΉŒ?

μ°Έμ‘°μ„ΈκΈ°μ˜ κ΅¬ν˜„μ€ κ³΅μ§œκ°€ μ•„λ‹ˆλ‹€. λͺ¨λ“  μ°Έμ‘°μ„ΈκΈ°λŠ” 참쑰세기에 λŒ€ν•œ κ·Έλ§Œν•œ λΉ„μš©μ„ μ§€μΆœν•˜μ•Ό ν•˜λŠ”λ°, μ‚¬μš©μžλŠ” μ΄λŸ¬ν•œ λ°©λ²•λ‘ μ˜ μ μš©μ—, 검증을 μ›ν•œλ‹€. λ‹¨μˆœνžˆ 보면, μ°Έμ‘°μ„ΈκΈ°λŠ” 더 λ§Žμ€ λ©”λͺ¨λ¦¬λΌ μž‘μ•„λ¨Ήκ²Œλ„ ν• μˆ˜ μž‡κ³ , 더 λ§Žμ€ μ½”λ“œλΌ μž‘μ•„ 먹게 ν• μˆ˜ μžˆλ‹€. 거기에닀 λͺ¨μž˜λΌ, μ½”λ“œλΌ λ” λ³΅μž‘ν•˜κ²Œ ν•˜κ³ , μ •μ„±λ“€μ—¬ λ§Œλ“  μ½”λ“œμ— λŒ€ν•˜μ—¬ 망쳐 λ²„λ¦΄μˆ˜λ„ μžˆλ‹€. λ§ˆμ§€λ§‰μ— μ΅œμ’… κ΅¬ν˜„λœ String(StringValue, RCObject, RCPtr이 적용된 버전) 클래슀 λ³΄λ‹€λŠ”, 보톡 μž‘μ‘°μ„ΈκΈ°κ°€ 적용 μ•ˆλœ μ½”λ“œλ“€μ„ μ“΄λ‹€. 사싀 μš°λ¦¬κ°€ λ””μžμΈν•œ μ€λ” λ³΅μž‘ν•œ λ””μžμΈμ€ 자료의 곡유둜 더 쒋은 νš¨μœ¨μ„ λŒμ–΄ 듀인닀. 그것은 객체의 μ†Œμœ κΆŒλ“€μ„ 주리고, μ°Έμ‘°μ„ΈκΈ°μ˜ μž¬μ‚¬μš© 방법에 λŒ€ν•œ 생각듀을 μ œμ‹œν•œλ‹€. κ·ΈλŸΌμ—λ„, λ„κ°€μ§€μ˜ ν΄λž˜μŠ€λΌ μ‚¬μš©ν•΄μ•Ό ν•˜κ³ , νƒœμŠ€νŠΈν•˜κ³ , λ¬Έμ„œν™”ν•˜κ³ , μœ μ§€ γ…—μŠ€ν•˜λŠ”λ°μ—λŠ”, ν•˜λ‚˜μ˜ ν΄λž˜μŠ€λΌ μž‘μ„±,λ¬Έμ„œν™”,μœ μ§€λ³΄μˆ˜ ν•˜λŠ”κ²ƒλ³΄λ‹€ 더 λ§Žμ€ 일을 λΆ€λ‹΄ν•˜κ²Œ λ§Œλ“ λ‹€.

μ°Έμ‘°μ„ΈκΈ°λŠ” λ³΄ν†΅μ˜ 객체듀을 κ³΅μœ ν•΄μ„œ μ‹œμŠ€ν…œμ˜ λΉ„μš©μ„ μ„μ΄κ³ μž ν•˜λŠ” μ΅œμ ν™” κΈ°μˆ μ΄λ‹€. 즉, κ³΅μœ λΌ λ§Žμ΄ ν•˜μ§€ μ•Šμ€ ν”„λ‘œκ·Έλž¨ 객체에 λŒ€ν•˜μ—¬ μ΄λΌ μ μš©ν•˜λ©΄ 더 λ§Žμ€ λΉ„μš©κ³Ό, 더 λ³΅μž‘ν•œ ν”„λ‘œκ·Έλž¨μ„ μž‘μ„±ν• μˆ˜ 밖에 μ—†λ‹€λŠ” 결둠이 λ‚˜λŠ” 것이닀. κ·Έ λ°˜λŒ€λΌλ©΄, μ‹œκ°„, 곡간 λΉ„μš© λͺ¨λ‘λΌ μ•„λΌκ²Œ ν•΄μ„ κ²ƒμ΄λ‹€. κ·ΈλŸ¬ν•œ 상황을 생각해 λ³Έλ‹€.

  • 적은 μžλ£ŒλΌ λ§Žμ€ 객체듀이 μ‚¬μš©ν•˜κ³ μž ν•œλ‹€. μ΄λŸ¬ν•œ κ²½μš°μ—λŠ” μƒμ„±μžμ™€ 볡사에 κ΄€ν•œ λΉ„μš©μ΄ 많이 λ“ λ‹€. 이런 경우 μ°Έμ‘°μ„ΈκΈ°μ˜ 적용이 더 놓은 νš¨μœ¨μ„ λŒμ–΄ λ‚Όμˆ˜ μžˆμ„ 것이닀.
    • Relatively few values are shared by relatively many objects.
  • κ°μ²΄λΌ μƒμ„±ν•˜κ³ , νŒŒκ΄΄ν•˜λŠ”λ° 높은 λΉ„μš©μ„ μ§€λΆˆν•΄μ•Ό ν•˜κ±°λ‚˜, λ§Žμ€ λ©”λͺ¨λΌ μ‚¬μš©ν•œλ‹€. μ΄λŸ¬ν•œ 경우 μ‘°μ°¨ μ°Έμ‘°μ„ΈκΈ°λŠ” λ§Žμ€ 객체가 κ³΅μœ ν•˜λ©΄ ν• μˆ˜λ‘ λΉ„μš©μ„ μ„μ—¬ μ„것이닀.
    • Object values are expensive to create or destroy, or they use lots of memory.


κ°€μž₯ ν™•μ‹€ν•œ 방법은 ν”„λ‘œνŒŒμΌ(profile)을 ν†΅ν•΄μ„œ μ°Έμ‘°μ„ΈκΈ°κ°€ ν•„μš”ν•œ 납득할 λ§Œν•œ μ΄μœ λΌ μ°ΎλŠ” 것이닀. μ΄λŸ¬ν•œ μ‹ λ’°κ°€ κ°€λŠ” 방법은 μ„±λŠ₯의 병λͺ© ν˜„μƒμ΄ μΌμ–΄λ‚˜λŠ” 뢀뢄에 μ°Έμ‘°μ„ΈκΈ°μ˜ μ μš©μ„ ν†΅ν•΄μ„œ 더 높은 νš¨μœ¨μ„ 가지고 올수 μžˆλ‹€λŠ” 증거가 될것이닀. 였직 감으둜 μ˜μ‘΄ν•˜μ—¬, μ°Έμ‘°μ„ΈκΈ°μ˜ μ μš©μ€ μ—­νš¨κ³ΌλΌ κ°€μ Έλ‹€ μ˜¬λΏμ΄λ‹€.

μ§€κΈˆκΉŒμ§€μ˜ κ΅¬ν˜„μœΌλ‘œ 참쑰세이긔 μžλ£ŒλŠ” heapμ˜μ—­μ—λ§Œ μ„ μ–Έν• μˆ˜ μžˆλ‹€. 아무리 지역 λ³€μˆ˜λ‘œ μ„ μ–Έν•œλ“€, λ‚΄λΆ€μ—μ„œ μžλ£ŒλŠ” heapμ˜μ—­μ— μ„ μ–Έν•˜μ—¬, κ΄€λ¦¬λœλ‹€. μ •ν™•ν•œ μ„ μ–Έμ΄λœ ν΄λž˜μŠ€λΌ λ§Œλ“€μ–΄μ•Ό ν•˜κ³ , κ·Έλž˜μ„œ ν™•μ‹€νžˆ λ™μž‘ν•˜λŠ”κ°€ ν™•μ‹ ν• μˆ˜ μžˆλŠ” 버그 μ—†λŠ” μ½”λ“œλΌ λ§Œλ“€μ–΄μ•Όν•œλ‹€. 그리고 이 μ°Έμ‘°μ„ΈκΈ° κΈ°λ³Έν΄λž˜μŠ€κ°€ 우리의 손을 μ˜¬λ°”λ₯΄κ²Œ λ– λ‚˜λŠ” 것은 μ‚¬μš© 방법에 λŒ€ν•˜μ—¬ μ •ν™•ν•œ 기술이 ν•„μš”ν•˜λ‹€.

DeleteMe)μ˜μ–΄κ°€ 짧은지 λ’€μ˜ λ‚΄μš©μ€ μ§€λ£¨ν•œ 같은 ν† λ‘ μ˜ 연속같닀. μΆ”κ°€ν•  κΈ°μš΄λ„ μ•ˆλ‚œλ‹€.

2. Item 30: Proxy

  • Item 30: λŒ€λ¦¬μž
보톡 μš°λ¦¬λŠ” 일차원 배열을 μ‚¬μš©ν•œλ‹€. ν•˜μ§€λ§Œ 일반적으둜 μ‹€μ œλ‘œ 자료의 ν‘œν˜„μ—λŠ” κ·ΈλŸ¬μ§€κ°€ λͺ»ν•˜λ‹€. λΆˆν–‰νžˆλ„ C++λŠ” 이차원 μ΄μƒμ˜ μžλ£Œκ΅¬μ‘°λΌ μž˜ μ·¨κΈ‰ν•˜μ§€ λͺ»ν•œλ‹€. μ΅œμ†Œν•œ 배열에 λŒ€ν•œ 지원은 μ–Έμ–΄κ°€ κ°€μ Έμ•Όν•˜λŠ” μ΅œμ†Œν•œμ˜ 배렀이닀.(μž‘μ„±μžμ£Ό:λ©‹λŒ€λ‘œ μ˜μ—­) FORTRANμ΄λ‚˜ BASIC, COBOLμ—μ„œ 일차원, 이차원, ... 닀차원 배열을 λ§Œλ“€μˆ˜ μžˆλ‹€. (그래, FORTRAN은 7차원 κΉŒμ§€λ°–μ— μ•ˆλœλ‹€. 이거 딴지닀. λ„˜μ–΄κ°€μž) ν•˜μ§€λ§Œ C++μ—μ„œ ν• μˆ˜ μžˆλ‚˜? λ•Œλ‘  였직 정렬을 μœ„ν•΄μ„œλ§Œ 일것이닀.

이 μ½”λ“œλŠ” 합법이닀.:

~cpp 
int data[10][20];       // 2차원 λ°°μ—΄ 10 by 20
ν•˜μ§€λ§Œ μ°¨μ›μ˜ ν¬κΈ°λŠ” λ³€μˆ˜κ°€ 될수 μ—†λ‹€. μ΄λŸ°κ²ƒμ΄ μ•ˆλœλ‹€.:

~cpp 
void processInput(int dim1, int dim2)
{
    int data[dim1][dim2];   // μ—λŸ¬! λ°°μ—΄μ˜ 차원은 단지 컴파일 μ€‘μ—λ§Œ κ²°μ •λœλ‹€.
    ...
}
거기에 Heap μ˜μ—­μ— κΈ°λ°˜ν•œ ν• λ‹Ή μ—­μ‹œ κ·œμΉ™μ— μ–΄κΈ‹λ‚œλ‹€.

~cpp 
int *data =  new int[dim1][dim2];   // μ—λŸ¬!

2.1. Implementing Two-Dimensional Arrays 이차원 λ°°μ—΄μ˜ κ΅¬ν˜„

λ‹€μ°¨μ›μ˜ 배열은 C++에 λΏμ•„λ‹ˆλΌ. λ‹€λ₯Έ μ–Έμ–΄μ—μ„œλ„ μœ μš©ν•˜λ‹€. κ·Έλž˜μ„œ 닀차원 배열은 μ΅œκ·Όμ— 이것듀에 μ§€μ›ν•˜λŠ” 방법에 λŒ€ν•œ μ€‘μš”μ„±μ΄ λŒ€λ‘λ˜κ³  μžˆλ‹€. λ³΄ν†΅μ˜ 방법은 C++μ—μ„œ ν‘œμ€ μ€‘μ— ν•˜λ‚˜μ΄λ‹€.(ν•„μš”λ‘œν•œ κ°μ²΄λΌ ν‘œν˜„ν•˜κΈ° μœ„ν•΄ ν΄λž˜μŠ€λΌ λ§Œλ“ λ‹€. ν•˜μ§€λ§Œ μ•Œλ§žκ²Œ κ΅¬ν˜„ν•˜κΈ°κ°€ μ–΄λ ΅λ‹€. ) λ°”λ‘œ 이차원 배열에 λŒ€ν•œ ν…œν”Œλ¦Ώμ„ μ •μ˜ν• μˆ˜ μžˆλ‹€.

~cpp 
template<class T>
class Array2D {
public:
    Array2D(int dim1, int dim2);
    ...

};
μ›ν•˜λŠ”λ°λ‘œ 배열을 μ •μ˜ν• μˆ˜ μžˆλ‹€.

~cpp 
Array2D<int> data(10, 20);          // μ˜³λ‹€

Array2D<float> *data = new Array2D<float>(10, 20);  // μ˜³λ‹€

void processInput(int dim1, int dim2)
{
    Array2D<int> data(dim1, dim2);  // μ˜³λ‹€
    ...
}
ν•˜μ§€λ§Œ μ΄λŸ¬ν•œ λ°°μ—΄ 객체의 μ‚¬μš©μ€ μ™„λ²½ν•˜μ§€ μ•Šλ‹€. C와 C++μƒμ—μ„œ κΈ°λ³Έ 문법을 μ μš©μ‹œν‚¨λ‹€λ©΄, κ΄„ν˜ΈλΌ μ‚¬μš©ν•΄μ„œ 객체의 indexλΌ μ‚¬μš©ν• μˆ˜ μžˆμ–΄μ•Ό ν•œλ‹€.

~cpp 
cout << data[3][6];
κ·Έλ ‡μ§€λ§Œ Array2D상에 μΈν…μŠ€μ— κ΄€ν•œμΈμžλΌ μ–΄λ–»κ²Œ μ •μ˜ν•˜κ³ , μ‚¬μš©ν• κΉŒ?

첫번째 ν•˜κ³  싢은건 μ•„λ§ˆ operator[][]λΌ μ„ μ–Έν•΄ λ²„λ¦¬λŠ” 것이닀. μ΄λ ‡κ²Œ

~cpp 
template<class T>
class Array2D {
public:
    // μ΄λŸ¬ν•œ 선언은 컴파일 ν• μˆ˜ μ—†λ‹€.
    T& operator[][](int index1, int index2);
    const T& operator[][](int index1, int index2) const;
    ...
};
보자마자 이 μ—°μ‚°μžμ˜ μ˜λ„λΌ μ•Œκ²ƒμ΄λ‹€. ν•˜μ§€λ§Œ operator[][]λž€κ±΄ μ„ μ–Έν• μˆ˜κ°€ μ—†λ‹€. 그리고 λ‹Ήμ‹ μ˜ μ»΄νŒŒμΌλŸ¬μ—­μ‹œ 이것을 κ°μ•ˆν•˜μ§€ μ•Šμ„ 것이닀. (μ˜€λ²„λ‘œλ“œ(overload)와 κ΄€λ ¨ν•œ μ—°μ‚°μžλ“€μ— κ΄€ν•œ μ •λ³΄λŠ” Item 7을 μ°Έκ³ ν•˜λΌ) μš°λ¦¬λŠ” κ·Έμ™Έμ˜ λ‹€λ₯Έ λ°©μ•ˆμ„ μ°Ύμ•„μ•Ό ν•œλ‹€.

λ§Œμ•½ 문법 λ•Œλ¬Έμ— 골머리가 μ•„ν”„λ‹€λ©΄, 배열을 μ§€μ›ν•˜λŠ” λ§Žμ€ μ–Έμ–΄μ—μ„œ μ‚¬μš©ν•˜κ³  μžˆλŠ” 방법을 λ”°λΌμ„œ, ()λΌ μ΄μš©ν•˜λŠ” μΈν…μŠ€μ˜ 접근을 λ§Œλ“€μ–΄ λ³Όμˆ˜λ„ μžˆλ‹€. ()의 μ΄μš©μ€ 단지 operator()λΌ μ˜€λ²„λ‘œλ“œ(overload)ν•˜λ©΄ λœλ‹€.

~cpp 
class Array2D {
public:
    // 이런 사항은 잘 컴파일 λœλ‹€.
    T& operator()(int index1, int index2);
    const T& operator()(int index1, int index2) const;
    ...
};
ν΄λΌμ΄μ–ΈνŠΈμ—μ„œλŠ” μ΄λ ‡κ²Œ μ‚¬μš©ν•œλ‹€.

~cpp 
cout << data(3, 6);
μ΄λŸ¬ν•œ κ΅¬ν˜„μ€ 쉽고, 당신이 μ‚¬μš©ν•˜κ³ μž ν•˜λŠ” λ§Žμ€ μ°¨μ›μ—μ„œ μΌλ°˜ν™” μ‹œν‚€κΈ°λ„ μš©μ΄ν•˜λ‹€. ν•˜μ§€λ§Œ 결점이 μžˆλŠ”λ°, Array2D κ°μ²΄λŠ” built-in 배열같이 보이지 μ•ŠλŠ”λ‹€λŠ” 점이닀. 사싀 μœ„μ˜, 각 data의 μΈμžλ“€μ— λŒ€ν•˜μ—¬ (3,4)κ³Ό 같은 μ ‘κ·Ό 방법은 ν•¨μˆ˜ 호좜과 같은 λͺ¨μŠ΅μ„ ν•˜κ³  μžˆλ‹€.

FORTRANκ³Ό 같이 λ³΄μ΄λŠ” μ΄λŸ¬ν•œ λ°°μ—΄ ν‘œν˜„λ²•μ΄ λ§ˆμŒμ— 듀지 μ•ŠλŠ”λ‹€λ©΄, index μ—°μ‚°μžμ™€ 같은 κ°œλ…μ„ 으둜 λ‹€μ‹œ λŒμ•„κ°€ λ³Έλ‹€. operator[][]λΌ μ‚¬μš©ν•˜μ§€ μ•Šκ³ λ„ ν•©λ²•μ μœΌλ‘œ λ‹€μŒκ³Ό 같이 λ³΄μ΄λŠ” μ½”λ“œλΌ κ΅¬ν˜„ν• μˆ˜λŠ” μ—†μ„κΉŒ?

~cpp int data[10][20];
...
cout << data[3][6];
μ–΄λ–»κ²Œ ν•˜λ©΄ 될까? λ³€μˆ˜μΈ dataλŠ” μ‹€μ œλ‘œ 이차원 배열이 κ²°μ½” μ•„λ‹ˆλ‹€. 그것은 10개-μΈμžκ°€ ν•˜λ‚˜μ˜ μ°¨μ›μœΌλ‘œ 된 λ°°μ—΄μœΌλ‘œ 주어진 것이닀. 10개 μΈμžλŠ” 각기 20개의 μΈμžλΌ κ°€μ§„ λ°°μ—΄λ‘œ λ˜μ–΄ μžˆλ‹€. κ·Έλž˜μ„œ data36은 μ‹€μ œλ‘œλŠ” (data3)6λΌ μ˜λΈν•˜λŠ”것이닀. λ‹€μ‹œ λ§ν•˜μžλ©΄, data의 λ„λ²ˆμ§Έ 인자인 λ°°μ—΄μ˜ 일곱번째 인자. 짧게 말해 값은 처음 κ΄„ν˜Έμ˜ 의λΈλŠ” λ˜λ‹€λ₯Έ 배열이닀. κ·Έλž˜μ„œ λ‘λ²ˆμ§Έ κ΄„ν˜Έμ˜ μ μš©μ€ λ‘λ²ˆμ§Έμ˜ λ°°μ—΄λ‘œ λΆ€ν„° μΈμžλΌ κ°€μ§€κ³  μ˜€λŠ” 것이닀.

같은 방식을 Array2D에 operaotr[]κ°€ μƒˆλ‘œμš΄ 객체인, Array1DλΌ λ°˜ν™˜μ‹œν‚€λŠ” λ°©μ‹μœΌλ‘œ ν’€μ–΄λ‚˜κ°€ 보자. μ›λž˜ 이차원 λ°°μ—΄μ˜ μ•ˆμ— μ‘΄μž¬ν•˜λŠ”, λ°˜ν™˜λ˜λŠ” 인자 Array1Dμ—μ„œ operator[]λΌ μ˜€λ²„λ‘œλ“œν• μˆ˜ μžˆλ‹€.

~cpp 
template<class T>
class Array2D {
public:
    // 2번째 차원에 μœ„μΉ˜ν•˜λŠ” Array1D
    class Array1D {
    public:
        T& operator[](int index);
        const T& operator[](int index) const;
        ...
    };

    // μœ„μ˜ 1차원 λ°°μ—΄ 객체 Array1D
    Array1D operator[](int index);  
    const Array1D operator[](int index) const;
    ...

};
이러게 ν•˜λ©΄ λ‹€μŒκ³Ό 같은 문법이 합법적이닀.

~cpp 
Array2D<float> data(10, 20);
...
cout << data[3][6];          // μ˜³λ‹€.
μ—¬κΈ°μ—μ„œ data3은 Array1DλΌ μ΄μ•ΌκΈ° ν•˜λŠ” 것이고, operator[]λŠ” λ‘λ²ˆμ§Έ 차원에 μœ„μΉ˜ (3,6)에 μžˆλŠ” floatλΌ ν˜ΈμΆœν•œλ‹€.

Array2D 클래슀의 ν΄λΌμ΄μ–ΈνŠΈλŠ” Array1Dν΄λž˜μŠ€μ— κ΄€ν•΄μ„œ μ‹ κ²½ μ“Έν•„μš” μ—†λ‹€. μ΄λŸ¬ν•œ κ°μ²΄λŠ” 1μ°¨μ›μ˜ λ°°μ—΄μ—λŒ€ν•œ 객체의 ν‘œμ€μ΄μ§€λ§Œ, κ°œλ…μ μœΌλ‘œλŠ” μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ”λ‹€. 이것듀을 μ‹€μ œλ‘œ μ“°λŠ” κ·ΈλŸ¬ν•œ ν΄λΌμ΄μ–ΈνŠΈλ“€μ€ 이차원 배열을 μ •ν™•νžˆ ν”„λ‘œκ·Έλž¨ ν•œλ‹€. C++의 μ—‰λš±ν•œ 짓을 만μ±μ‹œν‚€κΈ° μœ„ν•˜μ—¬, 일차원 배열을 λ‹€λ£¨λŠ”λ° λ¬Έλ²•μ μœΌλ‘œ μ •ν™•νžˆ κ΅¬ν˜„ν•œ Array2D의 ν΄λΌμ΄μ–΄νŠΈλ“€μ΄ κ±±μ •ν•˜λŠ” 일이 μ—†λ‹€.

Array2D의 ν΄λΌμ΄μ–ΈνŠΈμ— μ˜ν•΄ μ‚¬μš©λ˜μ–΄μ§€λŠ” κ°œλ…μ μΈ λͺ¨λΈμ˜ λΆ€μ œλ‘œ, Array1D 각각의 κ°μ²΄λŠ” 1차원 배열을 의λΈν•œλ‹€. λ‹€λ₯Έ κ°μ²΄λΌ μœ„ν•΄ μ‘΄μž¬ν•˜λŠ” 객체듀을 보톡 proxy object라고 뢈리이고, oproxyκ°μ²΄λŠ” proxy class에 μ˜ν•΄ ν˜ΈμΆœλœλ‹€. proxy 클래슀 or 의 μΈμŠ€ν„΄μŠ€λŠ” 일차원 λ°°μ—΄μ˜ 근간이 λ˜λŠ”λ°, κ°œλ…μ μœΌλ‘œ μ‘΄μž¬ν•˜μ§€ μ•Šμ€λ‹€. (proxy κ°μ²΄λΌ μœ„ν•œ 기술과 ν΄λž˜μŠ€λŠ” μ „μ²΄μ—μ„œ 동떨어진 λͺ¨μŠ΅μ΄λ‹€.; κ·ΈλŸ¬ν•œ 클래슀의 객체 μ—­μ‹œ λ•Œλ‘œ surrogate(λŒ€λ¦¬μž) 라고도 뢈릴 것이닀.

2.2. Distinguishing Reads from Writes via operator[] : operator[]의 쓰기에 κΈ°λ°˜ν•œ μ½κΈ°λΌ κ΅¬λ³„

닀차원 λ°°μ—΄κ³Ό 같은 μΈμŠ€ν„΄μŠ€λΌ λ§Œλ“œλŠ” ν”„λ‘μ‹œμ˜ μ‚¬μš©μ€ μΌλ°˜μ μ΄λ‹€. ν•˜μ§€λ§Œ ν”„λ‘μ‹œ ν΄λž˜μŠ€λ“€μ€ 일반 배열보닀 μœ μ—°ν•˜μ§€ λͺ»ν•˜λ‹€. Item 5μ—μ„œ μ˜ˆλΌ λ“€μ–΄ 보면 μ–΄λ–»κ²Œ ν”„λ‘μ‹œ ν΄λž˜μŠ€λ“€μ΄ μ˜λ„ν•˜μ§€ μ•Šμ€ μƒμ„±μžμ˜ μ‚¬μš©μ„ λ§‰μ„μˆ˜ μžˆλŠ”μ§€ 방법을 보여μ€λ‹€. ν•˜μ§€λ§Œ ν”„λ‘μ‹œ 클래슀의 λ‹€μ±„λ‘œμš΄ μ‚¬μš©μ΄ κ°€μž₯ μž˜μ•Œλ €μ§„ 것은 마둜 operator[]μ—μ„œ write와 readλΌ κ΅¬λΆ„ν•˜λŠ” 것이닀.

operator[]λΌ μ§€μ›ν•˜λŠ”, μ°Έμ‘°μ„ΈκΈ°κ°€ 적용된 λ¬Έμžμ—΄ ν˜•μ— κ΄€ν•΄μ„œ 생각해 보자. μžμ„Έν•œ μ„λͺ…은 Item 29λΌ μ°Έκ³ ν•˜λΌ, λ§Œμ•½ Item 29의 λ°©λ²•λŒ€λ‘œ μ°Έμ‘°μ„ΈκΈ°μ˜ κ°œλ…μ„ μ μš©ν•΄μ„œ, 그것을 배열에 μΌλ°˜ν™” μ‹œν‚€λŠ” 것은 쒋은 생각이닀.

operator[]λΌ μ§€μ›ν•˜λŠ” λ¬Έμžμ—΄ ν˜•μ€ ν΄λΌμ΄μ–ΈνŠΈμ—κ²Œ λ‹€μŒκ³Ό 같은 μ½”λ“œλΌ ν—ˆμš©ν•œλ‹€.

~cpp 
String s1, s2;		// λ¬Έμžμ—΄κ³Ό λΉ„μŠ·ν•œ 클래슀;ν”„λ‘μ‹œμ˜ μ“°μž„μ€ 
                    // ν‘œμ€ λ¬Έμžμ—΄ μΈν„°νŽ˜μ΄μŠ€λΌ λ”°λ₯΄λŠ” 클래슀 ν˜•νƒœλΌ
					// μœ μ§€ν•œλ‹€.
...

cout << s1[5];      // s1 읽기

s2[5] = 'x';        // s2 μ“°κΈ°

s1[3] = s2[8];      // s1 μ“°κΈ°, s2 읽기
operator[] λŠ” 각기 λ‹€λ₯Έ λͺ©μ μœΌλ‘œ 호좜될수 μžˆμŒμ„ μœ μ˜ν•˜λΌ: λ¬ΈμžλΌ μ½κ±°λ‚˜ ν˜Ήμ€ λ¬ΈμžλΌ μ“°κ±°λ‚˜, μ½κΈ°λŠ” rvalue의 ν˜•νƒœλ‘œ 쓰여지도둝 μ•Œλ €μ Έ μžˆλ‹€.; 그럼 μ“°κΈ°λŠ” lvalueν˜•νƒœ(r은 right hand value, l은 left μ΄ν•˜ κ°™μŒ) 일반적으둜 lvalue의 의λΈλŠ” κ°μ²΄μ—μ„œ κ·ΈλŸ¬ν•œ 객체의 μˆ˜μ •μ„ 의λΈν•˜λ©°, rvalueλŠ” μˆ˜μ •μ„ ν• μˆ˜ μ—†λŠ” 것을 의λΈν•œλ‹€.

μ΄λŸ¬ν•œ μƒνƒœ, 즉 perator[]μ—μ„œ lvalue와 rvalueλΌ κ΅¬λΆ„ν•΄μ•Όλ§Œ ν•œλ‹€. μ™œλƒν•˜λ©΄ μ°Έμ‘°μ„ΈκΈ°κ°€ 적용된 자료ꡬ쑰의 κ²½μš°μ— μ½κΈ°λŠ” 쓰기에 λΉ„ν•˜μ—¬ 훨씬 적은 λΉ„μš©μ„ μ†Œλͺ¨ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€. Item 29μ—μ„œ μ°Έμ‘°μ„ΈκΈ° 객체의 μ“°κΈ°λŠ” μ•„λ§ˆ 전체 자료ꡬ쑰의 λ³΅μ‚¬λΌ μœ λ„ν•˜μ§€λ§Œ, μ½κΈ°λŠ” κ°„λ‹¨ν•œ κ°’μ˜ λ°˜ν™˜μ„ 의λΈν•œλ‹€κ³  μ„λͺ…ν–ˆλ‹€. λΆˆν–‰νžˆλ„, operator[]의 λ‚΄λΆ€μ—μ„œ, μ΄λ“€μ˜ 호좜 λͺ©μ μ„ ꡬ뢄할 방법은 μ—†λ‹€. operator[]λŠ” lvalue와 rvalue의 μ“°μž„μ˜ μ°¨μ΄λΌ κ΅¬λΆ„ν• μˆ˜ μ—†λ‹€.

"κ·Έλ ‡μ§€λ§Œ μž μ‹œ!" ν•˜κ³  당신이 λ§ν•œλ‹€. "κΌ­ 그럴 ν•„μš”κ°€ μ—†λ‹€. operator[]의 μƒμˆ˜ν™” 된 κ°œλ…μ„ λ°›μ•„λ“€μ—¬μ„œ operator[]의 읽기와 μ“°κΈ°λΌ κ΅¬λΆ„ν•˜λ©΄ λ˜μ§€ μ•Šμ€κ°€?" μ΄λŸ¬ν•œ λ‹€λ₯Έ μΈ‘λ³€μœΌλ‘œ 당신은 μš°λ¦¬μ—κ²Œ 문제의 ν•΄κ²° 방식을 μ œμ•ˆν•œλ‹€.

~cpp 
class String {
public:
  const char& operator[](int index) const;       // 읽기 μœ„ν•΄ 쑴재
  char& operator[](int index);                   // μ“°κΈ° μœ„ν•΄ 쑴재
  ...

};
λΆˆν–‰νžˆλ„ μ΄λŸ¬ν•œ 것은 μˆ˜ν–‰λ˜μ§€ μ•ŠλŠ”λ‹€. μ»΄νŒŒμΌλŸ¬λŠ” const와 non-const 멀버 ν•¨μˆ˜μ˜ ꡬ뢄을 였직 κ·Έ 객체가 constμΈκ°€μ˜ 여뢀에 λ”°λΌλ§Œ νŒλ‹¨ν•œλ‹€. μ΄λŸ¬ν•œ κ΅¬ν˜„μ€, constκ΅¬λ³„μ˜ λͺ©μ μ„ μœ„ν•΄ μ•„λ¬΄λŸ° 영ν–₯을 λͺ» λΌμΉœλ‹€.

~cpp 
String s1, s2;

...

cout << s1[5];      // non-const operator[] 호좜 μ™œλƒν•˜λ©΄
                    // s1이 non-const이기 λ•Œλ¬Έμ—

s2[5] = 'x';        // μ—­μ‹œ non-const operator[] 호좜: s2λŠ” non-const이닀.

s1[3] = s2[8];      // λ‘˜λ‹€ non-const operator[] 호좜 μ™œλƒν•˜λ©΄ s1,s2λͺ¨λ‘
                    // non-const 객체이닀.
그러λ€λ‘œ 이런 λ°©μ‹μ˜ operator[]의 μ˜€λ²„λ‘œλ“œλŠ” 읽기와 μ“°κΈ°μ˜ ꡬ뢄에 μ‹€νŒ¨ν•œλ‹€.


Item 29μ—μ„œ μš°λ¦¬λŠ” operator[]λΌ μ“°κΈ°λΌ μœ„ν•΄μ„œ 재 λ””μžμΈν–ˆλ‹€. μ•„λ§ˆ 이걸 μ‰½κ²Œ ν¬κΈ°ν• μˆ˜λŠ” 없을 κΊΌλ‹€.(μž‘μ„±μžμ£Ό:μ–Όλ§ˆλ‚˜ κ³ μƒν•˜λ©΄μ„œ λ΄€λŠ”λ°, λ°”κΎΈκΈ° 싫지.) Item 29의 λ””μžμΈμ€ lvalue와 rvalue의 μ‚¬μš©μ„ κ΅¬λΆ„ν•˜λŠ” 것이 μ•„λ‹ˆλΌ, operator[]λΌ ν˜ΈμΆœν•˜λ©΄ 무쑰건 μ“°κΈ°λ‘œ μ·¨κΈ‰ν•΄ λ²„λ¦¬λŠ” 것이닀.

operator[]κ°€ 읽기와 μ“°κΈ°λΌ κ΅¬λΆ„ λͺ»ν•˜μ§€λ§Œ 일단 μš°λ¦¬λŠ” κ°€λŠ₯ν•œν•œ 이것을 κ΅¬ν˜„ν•΄ 보고자 ν•˜λŠ” μž…μž₯μ—μ„œ μ ‘κ·Όν•΄ 보자. operator[]κ°€ λ°˜ν™˜ν•œ 이후에 읽기와 μ“°κΈ°μ˜ μƒνƒœλΌ μ•Œμ•„λ‚΄λŠ” 방법을 ν•„μš”λ‘œ ν•œλ‹€. μ΄κ²ƒμ˜ 의λΈλŠ” μ•žμ— λ‹€λ£¨μ—ˆλ˜, lazy evaluation의 κ°œλ…κ³Ό λΉ„μŠ·ν•˜μ§€ μ•Šμ„κΉŒ?

ν”„λ‘μ‹œ ν΄λž˜μŠ€λŠ” μš°λ¦¬κ°€ ν•„μš”ν•œ μ‹œκ°„μ„ λ²Œμ–΄ μ„μˆ˜ μžˆλ‹€. μš°λ¦¬λŠ” operator[]의 λ°˜ν™˜μΈμžλΌ λ¬ΈμžλŒ€μ‹ μ— λ¬Έμžμ—΄μ„ μœ„ν•œ ν”„λ‘μ‹œ κ°μ²΄λΌ λ°˜ν™˜ν•˜λ„λ‘ μˆ˜μ •ν• μˆ˜ 있기 λ•Œλ¬Έμ΄λ‹€. μš°λ¦¬λŠ” μ΄λ ‡κ²Œ ν”„λ‘μ‹œλΌ μ‚¬μš©ν•΄μ„œ μ‹œκ°„μ„ 벌수 μžˆλ‹€. 이 ν”„λ‘μ‹œ ν΄λž˜μŠ€κ°€ μ½νžλ•Œ, operator[]κ°€ 읽기인지 μ“°κΈ°μΈμ§€λΌ μ•Œμˆ˜ μžˆλ‹€.

일단 μ†ŒμŠ€λΌ λ³΄κΈ°μ „μ— μš°λ¦¬κ°€ ν”„λ‘μ‹œλΌ μ–΄λ–»κ²Œ 써야할지 μ„Έκ°€μ§€μ˜ μˆœμ„œλ‘œ λ‚˜λˆ„μ–΄ μƒκ°ν•˜μž.

  • ν”„λ‘μ‹œλΌ λ§Œλ“ λ‹€. λ‹€μ‹œ 말해 λ¬Έμžμ—΄μ—μ„œ λ¬ΈμžλΌ λŒ€μ‹ ν•˜λŠ” 것에 μ•Œλ§žλ„λ‘ λ§Œλ“ λ‹€.
  • ν”„λ‘μ‹œλΌ μ¨μ•Όν•  κ³³, 즉 λ¬Έμžμ—΄μ˜ κΈ€μžλΌ ν• λ‹Ήν•  곳에 μ μš©ν•œλ‹€. μ μš©μ„ ν• λ•Œ ν”„λ‘μ‹œλŠ” operaotr[]μ—μ„œ lvalue의 μ“°μž„μœΌλ‘œ μ‚¬μš©λœλ‹€.
  • 또 λ‹€λ₯Έ λ°©μ‹μœΌλ‘œ ν”„λ‘μ‹œλΌ μ‚¬μš©ν•œλ‹€. μ΄λ ‡κ²Œ μ‚¬μš©λ˜λ©΄ ν”„λ‘μ‹œλŠ” operator[]에 λŒ€ν•œ rvalue의 μ“°μž„μ„ κ΅¬ν˜„ν•œλ‹€.

μ—¬κΈ°μ—μ„œλŠ” μ°Έμ‘°μ„ΈκΈ°κ°€ 적용된 Stringν΄λž˜μŠ€μ— lvalue와 rvalue의 μ“°μž„μ„ κ΅¬λ³„ν•˜λŠ” operator[]의 λŠ₯λ ₯을 λΆ€μ—¬ν–ˆλ‹€.

~cpp 
class String {                    // μ°Έμ‘°μ„ΈκΈ°κ°€ 적용된 λ¬Έμžμ—΄, Item 29μ°Έκ³ 
public:                           

    class CharProxy {               // 문자의 ν”„λ‘μ‹œ
    public:
        CharProxy(String& str, int index);                // 생성

        CharProxy& operator=(const CharProxy& rhs);       // lvalue
        CharProxy& operator=(char c);                     // 의 μ“°μž„μ— λ°˜μ‘

        operator char() const;                            // rvalue의 μ“°μž„μ— λ°˜μ‘
                                                      // use
    private:
        String& theString;            // ν”„λ‘μ‹œμ—μ„œ λ¬Έμžμ—΄μ„ μ°Έμ‘°ν•  κ²½μš°κ°€ ν•„μš”ν• μ‹œ

        int charIndex;                // 문자의 인덱슀
    };

    // Stringν΄λž˜μŠ€κ°€ ν¬ν•¨ν•˜κ³  μžˆλŠ” 것
    const CharProxy  operator[](int index) const;   // const String에 λ°˜μ‘

    CharProxy operator[](int index); // non-const String을 μœ„ν•΄μ„œ
    ...

    friend class CharProxy;

private:
    RCPtr<StringValue> value;
};
Item 29μ—μ„œμ˜ String클래슀의 μ΅œμ’…λ²„μ „κ³Ό 이 μ†ŒμŠ€κ°€ λ‹€λ₯Έμ μ€ 였직 μΆ”κ°€λœ CharProxyν΄λž˜μŠ€μ— operator[] ν•¨μˆ˜κ°€ κ΅¬ν˜„λ˜μ—ˆλ‹€λŠ” 점이닀. ν•˜μ§€λ§Œ, ν΄λΌμ΄μ–ΈνŠΈ μž…μž₯μ—μ„œλŠ” 보톡 μ“°λŠ”κ²ƒμ²˜λŸΌ Stringν΄λž˜μŠ€λΌ μ‚¬μš©ν• λ•Œ μ΄λŸ¬ν•œ 사싀을 λ¬΄μ‹œν•˜κ³ , operator[]ν•¨μˆ˜κ°€ κΈ€μžλΌ λ°˜ν™˜ν•˜λŠ” κ²ƒμœΌλ‘œ μ·¨κΈ‰ν•œλ‹€.


~cpp 
String s1, s2;  // ν”„λ‘μ‹œλΌ μ‚¬μš©ν•˜λŠ” μ°Έμ‘° μ„ΈκΈ°κ°€ 적용된 λ¬Έμžμ—΄
...
cout << s1[5];  // μ˜³λ‹€.

s2[5] = 'x';    // μ—­μ‹œ μ˜³λ‹€.

s1[3] = s2[8];  // μ—­μ‹œλ‚˜ μž˜λŒμ•„κ°„λ‹€.
μ΄λ ‡κ²Œ μ›€μ§μ΄λŠ” 것이 κ΄€μ‹¬μ‚¬κ°€μ•„λ‹ˆλΌ, μ–΄λ–»κ²Œ μ›€μ§μ΄λŠλƒκ°€ 관심사 일것이닀.

λ§¨μ²˜μŒμ— 이 ꡬ문을 생각해 보자.

~cpp 
cout << s1[5];

s15의 ν‘œν˜„μ€ CharProxy κ°μ²΄λΌ λ°˜ν™˜ν•œλ‹€. s15κ°€ output(<<) μ—°μ‚°μžμ— λŒ€ν•˜μ—¬ 객체에 λŒ€ν•˜μ—¬ μ •μ˜λœκ²ƒμ€ μ—†λ‹€. κ·Έλž˜μ„œ λ‹Ήμ‹ μ˜ μ»΄νŒŒμΌλŸ¬λŠ” operator<<에 μ μš©ν• μˆ˜ μžˆλŠ” μ•”μ‹œμ (implicit) ν˜•λ³€ν™˜μ„ μ°ΎλŠ”λ‹€. μ»΄νŒŒμΌλŸ¬λŠ” κ·Έλž˜μ„œ ν”„λ‘μ‹œ 클래슀 내뢀에 μ„ μ–Έλ˜μ–΄ μžˆλŠ” char()λ₯Ό μ°Ύμ„μˆ˜ μžˆλ‹€. μ»΄νŒŒμΌλŸ¬λŠ” 이(char) ν˜•λ³€ν™˜μ„ μˆ˜ν–‰ν•˜κΈ°λ₯Ό μš”μ²­ν•˜κ³ , 결과적으둜 CharProxyλŠ” 문자둜 λ³€ν™˜λ˜μ–΄μ„œ μΆœλ ¨λ˜μ–΄ 진닀. λ‹€μ‹œ λ§ν•˜μ§€λ§Œ, 이것은 CharProxy-to-char 둜의 ν˜•λ³€ν™˜μ΄ CharProxy내뢀에 μ•”μ‹œμ (implicit) ν˜•λ³€ν™˜μ΄ μ„ μ–Έλ˜μ–΄ 있기 λ•Œλ¬Έμ΄λ‹€.

lvalue의 μ‚¬μš©μ€ μ€ λ‹€λ₯΄κ²Œ μž‘νžˆλŠ”λ°, 이 ꡬ문을 λ‹€μ‹œ 보자.

~cpp 
s2[5] = 'x';
s25의 ν‘œν˜„μ€ CharProxyκ°μ²΄λΌ λ°˜ν™˜ν•œλ‹€. 그리고 ν• λ‹Ή(assignment)μ—°μ‚°μžμ˜ λͺ©ν‘œκ°€ λœλ‹€.μ–΄λ–€ ν• λ‹Ή(assignment) μ—°μ‚°μžκ°€ λΆˆλ €μ§€λŠ” 걸까? ν• λ‹Ήμ˜ λͺ©ν‘œλŠ” CharProxy이닀. κ·Έλž˜μ„œ ν• λ‹Ήμ—°μ‚°μžλŠ” CharProxy 클래슀 μ•ˆμ—μ„œ λΆˆλ €μ§„λ‹€. 이것은 μ€‘μš”ν•œ 것이닀. μ™œλƒν•˜λ©΄ CharProxy의 ν• λ‹Ή(assignment) μ—°μ‚°μžλΌ μ‚¬μš©ν•˜λŠ”κ²ƒμœΌλ‘œ μš°λ¦¬λŠ” Stirngμ—μ„œ lvalueλ‘œμ„œ 이번 연산이 μˆ˜ν–‰λœλ‹€λŠ” 것을 μ•Œμˆ˜μžˆλ‹€. κ·Έλž˜μ„œ μš°λ¦¬λŠ” λ¬Έμžμ—΄ ν΄λž˜μŠ€κ°€ μ΄λ²ˆμ—λŠ” lvalue에 μ•Œλ§žλŠ” λ™μž‘μ„ ν•΄μ•Ό ν•œλ‹€λŠ” 결둠을 μ–»λŠ”λ‹€.

λΉ„μŠ·ν•˜κ²Œ λ‹€μŒκ³Ό 같은 ꡬ문을 보면

~cpp 
s1[3] = s2[8];
이것은 λ‘κ°œμ˜ CharProxyλΌ μœ„ν•΄μ„œ ν• λ‹Ή μ—°μ‚°μžκ°€ λ™μž‘ν•˜κ³ , ν•˜λ‚˜λŠ” char으둜 μ•”μ‹œμ  λ³€ν™˜, 또 ν•˜λ‚˜λŠ” CharProxy객체의 ν• λ‹Ή μ—°μ‚°μžλΌ μ‚¬μš©ν•˜λŠ” 것
으둜 λ‘˜μ€ lvalue와 rvalueλΌ κ΅¬λΆ„ν•˜κ³  μ˜¬λ°”λ₯΄κ²Œ μ‚¬μš©ν•˜κ²Œ λœλ‹€.
자, μ—¬κΈ° 그럼 String의 operator[]에 λŒ€ν•œ μƒˆλ‘œμš΄ κ΅¬ν˜„ μ½”λ“œκ°€ μžˆλ‹€.

~cpp 
const String::CharProxy String::operator[](int index) const
{
    return CharProxy(const_cast<String&>(*this), index);
}
String::CharProxy String::operator[](int index)
{
    return CharProxy(*this, index);
}
각 ν•¨μˆ˜λŠ” 문자 μš”κ΅¬μ‹œμ— CharProxy κ°μ²΄λΌ λ§Œλ“€μ–΄μ„œ λ°˜ν™˜ν•œλ‹€. λ¬Έμžμ—΄μ€ μŠ€μŠ€λ‘œλŠ” 아무 일을 ν•˜μ§€ λͺ»ν•œλ‹€. μš°λ¦¬λŠ” κ·ΈλŸ¬ν•œ λ™μž‘μ„ 읽기와 μ“°κΈ°μ˜ 접근을 μ•Œμˆ˜μžˆμ„λ•Œ κΉŒμ§€ μ§€μ—°μ‹œν‚€λ„λ‘ λ§Œλ“€μ–΄μ•Ό ν•œλ‹€.

const λ²„μ „μ˜ operator[] λŠ” const proxy κ°μ²΄λΌ λ°˜ν™˜ν•΄μ•Ό ν•˜λŠ” 것을 보자. CharProxy::operator=은 const 멀버 ν•¨μˆ˜κ°€ μ΄λ‹ˆκΈ° λ•Œλ¬Έμ— ν• λ‹Ή(assignment)의 λͺ©ν‘œκ°€ λ˜μ§€ μ•ŠλŠ”λ‹€. 그러λ€λ‘œ, proxyλŠ” const λ²„μ „μ˜ operator[]λ‚˜, lvalueλ‘œμ„œμ˜ λ¬Έμžμ—΄μ˜ μ‚¬μš©μ„ κ³ λ €ν•˜μ§€ μ•Šμ•„λ„ λœλ‹€. κ°„λ‹¨νžˆ, const λ²„μ „μ˜ operator[]μ—μ„œλ„ μ •ν™•νžˆ λŒμ•„κ°„λ‹€λŠ” 이야기이닀.

μ΄λ²ˆμ—λŠ” CharProxyλΌ λ§Œλ“€λ•Œ constλ²„μ „μ˜ operator[]μ—μ„œ const_cast(Item 2μ°Έκ³ )λΌ μ‚¬μš©ν•΄μ„œ *thisλΌ λ„˜κΈ°λŠ”κ±Έ μ£Όλͺ©ν•˜μž.저것은 CharProxyμƒμ„±μžμ— 쑰건에 λΆ€ν•©ν•˜κΈ° μœ„ν•œ μˆ˜ν–‰μœΌλ‘œ, non-const String만 인자둜 λ°›κΈ°μœ„ν•΄μ„œ ν˜•λ³€ν™˜μ„ μˆ˜ν–‰ν•œλ‹€. ν˜•λ³€ν™˜μ€ 보톡은 κ·€μ°λ‹€. κ·Έλ ‡μ§€λ§Œ μ΄λŸ¬ν•œ κ²½μš°μ— CharProxy κ°μ²΄λŠ” 그것 μžμ²΄κ°€ const이기 λ•Œλ¬Έμ— Stringκ°€ ν¬ν•¨ν•˜κ³  μžˆλŠ” proxyκ°€ μ°Έμ‘°ν•˜λŠ” String은 μˆ˜μ •λ˜μ–΄μ§€λŠ” 걱정이 없을 것이닀.

operator[]에 μ˜ν•΄ λ°˜ν™˜λ˜λŠ” 각 proxyλŠ” ν‘œν˜„μ„ μœ„ν•˜μ—¬ λ¬Έμžλ‘œμ„œ ν•„μš”ν•œ μΈλ±μŠ€μ™€ κΈ€μž μ •λ³΄λΌ μˆ˜λ‘ν•˜κ³  μžˆλ‹€.

~cpp 
String::CharProxy::CharProxy(String& str, int index)
: theString(str), charIndex(index) {}
rvalue둜의 proxy의 ν˜•λ³€ν™˜μ€ κ³§λ°”λ‘œ 일어 λ‚œλ‹€. 즉, 단지 proxyμ—μ„œ ν˜•λ³€ν™˜μœΌλ‘œ ν•΄λ‹Ήν•˜λŠ” μˆ˜ν–‰λ§Œ ν•΄μ£Όλ©΄ λœλ‹€.

~cpp 
String::CharProxy::operator char() const
{
    return theString.value->data[charIndex];
}
λ§Œμ•½ String객체와 κ΄€κ³„λΌ μ½μ–΄ 버렸닀면, value 멀버와 data λ©€λ²„μ™€μ˜ 관계에 λŒ€ν•˜μ—¬ Itmem 29λΌ λ³΄κ³  기얡해라. 이 ν•¨μˆ˜λŠ” λ¬ΈμžλΌ κ°’μœΌλ‘œ(by-value)둜 μ „λ‹¬ν•œλ‹€. 그리고 C++은 κ·ΈλŸ¬ν•œ κ°’μœΌλ‘œ(by-value) 전달을 λ°˜ν™˜ κ°’μœΌλ‘œλ§Œ λ°˜ν™˜ 값을 μ‚¬μš©ν•˜λ„λ‘ μ œν•œν•˜κΈ° λ•Œλ¬Έμ— λ‹€μŒκ³Ό 같은 ν˜•λ³€ν™˜ ν•¨μˆ˜λŠ” 였직 rvalueλ•Œλ§Œ μœ νš¨ν•˜λ‹€.

κ·Έλž˜μ„œ CharProxy의 ν• λ‹Ή(assignment) μ—°μ‚°μž κ΅¬ν˜„μœΌλ‘œ lvalue에 ν•΄λ‹Ήν•˜λŠ” μž‘μ—…λ§Œ κ°€λŠ₯ν•˜κ²Œ κ΅¬ν˜„ν• μˆ˜ μžˆλ‹€. 이제 CharProxy의 ν• λ‹Ή μ—°μ‚°μžλΌ λ‹€μŒκ³Ό 같이 κ΅¬ν˜„ν•œλ‹€.

~cpp 
String::CharProxy&
String::CharProxy::operator=(const CharProxy& rhs)
{
    // λ§Œμ•½ λ¬Έμžμ—΄μ΄ λ‹€λ₯Έ String객체와 값을 κ³΅μœ κ°€ κ°€λŠ₯ν•  경우
    if (theString.value->isShared()) {
        theString.value = new StringValue(theString.value->data);
    }

    // 문자 κ°’μ˜ 볡사 κ³Όμ •
    theString.value->data[charIndex] 
        = rhs.theString.value->data[rhs.charIndex];

    return *this;
}

Item 29에 λ‚˜μ™€μžˆλŠ”, non-const String::operator[]κ³Ό 비ꡐ해보면 인상적인 λŠλ‚Œμ΄ μ˜¬κ²ƒμ΄λ‹€. 이것은 μ˜ˆμΈ‘ν• μˆ˜ μžˆλ‹€. Item29μ—μ„œλŠ” μ΄λŸ¬ν•œ 쓰기와 μ½κΈ°λΌ κ΅¬λΆ„ν•˜μ§€ λͺ»ν•΄μ„œ 무쑰건 non-const operator[]λΌ μ“°κΈ°λ‘œ μ·¨κΈ‰ν–ˆλ‹€. 그리고, CharProxyλŠ” String에 λŒ€ν•œ 자유둜운 접근을 μœ„ν•΄ friend둜 μ„ μ–Έν–ˆκΈ°μ— λ¬Έμžκ°’μ˜ 볡사 κ³Όμ •μ˜ μˆ˜ν–‰μ΄ κ°€λŠ₯ν•˜λ‹€.

이제 λ‚˜λ¨Έμ§€ 직접 λ¬Έμžμ—΄μ΄ μž…λ ₯λ λ•ŒλΌ κ΅¬ν˜„ν•œλ‹€.

~cpp 
The second CharProxy assignment operator is almost identical:  Β€ Item M30, P58 

String::CharProxy& String::CharProxy::operator=(char c)
{
    if (theString.value->isShared()) {
        theString.value = new StringValue(theString.value->data);
    }

    theString.value->data[charIndex] = c;

    return *this;
}
Software Engineer을 μˆ˜ν–‰ν•˜λŠ” μž…μž₯이라면 λ¬Όλ‘  이와 같이 CharProxyλΌ ν†΅ν•΄μ„œ 읽기와 μ“°κΈ°λΌ κ΅¬λΆ„ν•΄μ„œ, 볡사에 ν•΄λ‹Ήν•˜λŠ” μ½”λ“œλΌ μ‚­μ œν•΄μ•Ό ν•œλ‹€.ν•˜μ§€λ§Œ 이것에 λŒ€ν•œ 결점을 생각해 보자.

2.3. Limitations : μ œν•œ

proxy 클래슀의 μ‚¬μš©μ€ operator[] μ‚¬μš©μ‹œ lvalue와 rvalue의 ꡬ뢄을 λͺ…λ£Œν•˜κ²Œ ν•œλ‹€. κ·Έλ ‡μ§€λ§Œ λ¬΄μ‹œν• μˆ˜ μ—†λŠ” 결점을 가지고 μžˆλ‹€. proxyκ°μ²΄λŠ” 그듀이 λͺ©ν‘œν•˜λŠ” κ°μ²΄λΌ μ™„μ „νžˆ κ΅μ²΄ν•˜λŠ” 것이 λͺ©ν‘œμ§€λ§Œ 정말 μ–΄λ ΅λ‹€. μ—¬κΈ°μ—μ„œ 문자처럼, lvalue와 rvalue의 λͺ©μ λΏ μ•„λ‹ˆλΌ. λ‹€λ₯Έ λ°©λ²•μœΌλ‘œ κ°μ²΄λŠ” μ ‘κ·Όλ μˆ˜ μžˆλ‹€.

Item 29에 μ–ΈκΈ‰λœ κ³΅μœ ν”Œλž˜κ·ΈλΌ λ”ν•œ StringValue객체에 κ΄€ν•΄μ„œ λ‹€μ‹œ 생각해 보자. λ§Œμ•½ String::operator[] κ°€ char&λŒ€μ‹ μ— CharProxyλΌ λ°˜ν™˜ν•œλ‹€λ©΄ μ΄λŸ¬ν•œ κ²½μš°λŠ” 더이상 컴파일 ν• μˆ˜κ°€ μ—†λ‹€.

~cpp 
String s1 = "Hello";
char *p = &s1[1];            // μ—λŸ¬!
s11의 ν‘œν˜„μ€ CharProxyλΌ λ°˜ν™˜ν•˜κΈ° λ•Œλ¬Έμ—, λ‘λ²ˆμ§Έμ—μ„œ 였λ₯Έμͺ½μ˜ 의λΈλŠ” CharProxy*λΌ λ°˜ν™˜ν•˜λŠ” 것이닀. ν•˜μ§€λ§Œ CharProxy*λΌ char*둜 λ°”κΎΈλŠ” ν˜•λ³€ν™˜μ€ ν—ˆμš©λ˜μ§€ μ•ŠκΈ°λ•Œλ¬Έμ— p의 μ΄ˆκΈ°ν™”μ—μ„œ μ»΄νŒŒμΌλŸ¬λŠ” μ—λŸ¬λΌ λ‚Έλ‹€. 일반적으둜 proxy의 μ£Όμ†ŒλŠ” μ‹€μ œ 객체보닀 λ‹€λ₯Έ 포인터 νƒ€μž…μ„ 가진닀.

μ΄λŸ¬ν•œ λ¬Έμ œλΌ μ œκ±°ν•˜κΈ° μœ„ν•˜μ—¬ μ£Όμ†Œμ— κ΄€ν•œ μ—°μ‚°μžλΌ CharProxy ν΄λž˜μŠ€μ— μ˜€λ²„λ‘œλ“œ(overload)ν•œλ‹€.

~cpp 
class String {
public:
    class CharProxy {
    public:
        ...
        char * operator&();
        const char * operator&() const;
        ...
    };
    ...
};
이 ν•¨μˆ˜λŠ” κ΅¬ν˜„ν•˜κΈ° 쉽닀. const ν•¨μˆ˜λŠ” 단지 const λ²„μ „μ˜ 문자의 ν¬μΈν„°λΌ ν”„λ‘μ‹œ 클래슀둜 μ „λ‹¬ν•˜λ©΄ λœλ‹€.

~cpp 
const char * String::CharProxy::operator&() const
{
    return &(theString.value->data[charIndex]);
}

non-const ν•¨μˆ˜μ˜ 경우 μ€λ” 신경쓸것이 λ§Žμ€λ°, λ°˜ν™˜λ˜λŠ” ν¬μΈν„°μ˜ λ¬Έμžκ°€ μˆ˜μ • κ°€λŠ₯성이 있기 λ•Œλ¬Έμ΄λ‹€. μ΄λŸ¬ν•œ κ²½μš°λŠ” Item 29μ—μ„œ λ‹€λ£¨μ—ˆλ˜ non-const λ²„μ „μ˜ String::operator[]의 λͺ¨μŠ΅κ³Ό λΉ„μŠ·ν•˜λ‹€.그리고 κ΅¬ν˜„ μ—­μ‹œ λΉ„μŠ·ν•˜λ‹€.

~cpp 
char * String::CharProxy::operator&()
{
    // λ‹€λ₯Έ 객체와 μ •λ³΄λΌ κ³΅μœ ν• λ•ŒλŠ” μƒˆ μžλ£ŒλΌ λ§Œλ“ λ‹€.
    if (theString.value->isShared()) {
        theString.value = new StringValue(theString.value->data);
    }
    // 이제 이 ν•¨μˆ˜κ°€ λ°˜ν™˜ν•˜λŠ” κ°μ²΄λΌ ν†΅ν•˜μ—¬ μˆ˜μ •ν• μˆ˜ μžˆλ‹€. 그러λ€λ‘œ
    // 곡유 λͺ»ν•˜λ„둝 λ§‰λŠ”λ‹€.
    theString.value->markUnshareable();

    return &(theString.value->data[charIndex]);
}
이 μ½”λ“œλŠ” CharProxy의 λ‹€λ₯Έ 멀버 ν•¨μˆ˜λ“€κ³Ό 같이 ν‰λ²”ν•˜λ‹€.

λ‘λ²ˆμ§Έμ˜ κ²°κ³Ό, CharProxyκ°€ λ‹€λ₯Έμ μ€ lvalue와 rvalue의 ꡬ뢄을 μœ„ν•΄ operator[]κ°€ 적용된 ν”„λ‘μ‹œ ν΄λž˜μŠ€λΌ μ‚¬μš©ν•˜λŠ”, μ°Έμ‘°μ„ΈκΈ° λ°°μ—΄ ν…œν”Œλ¦Ώμ΄λΌλ©΄, ν™•μ—°νžˆ λ“œλŸ¬λ‚˜λŠ” 것이닀.

~cpp 
template<class T>                        // ν”„λ‘μ‹œλΌ μ‚¬μš©ν•˜λŠ”
class Array {                            // μ°Έμ‘°μ„ΈκΈ° 적용 λ°°μ—΄
public:
     class Proxy {
    public:
        Proxy(Array<T>& array, int index);
        Proxy& operator=(const T& rhs);
        operator T() const;
        ...
    };

    const Proxy operator[](int index) const;
    Proxy operator[](int index);
    ...
};
μ΄λ ‡κ²Œ κ΅¬ν˜„λœ 배열을 μ–΄λ–»κ²Œ μ‚¬μš©ν•˜λŠ”κ°€ 보자.

~cpp 
Array<int> intArray;
...
intArray[5] = 22;                    // μ˜³λ‹€.

intArray[5] += 5;                    // μ—λŸ¬!

++intArray[5];                       // μ—λŸ¬!
μ˜ˆμƒλ˜λŠ” λŒ€λ‘œ operator[]의 λͺ©ν‘œμΈ κ°„λ‹¨ν•œ ν• λ‹Ή(assignment)은 μ„±κ³΅ν•˜μ§€λ§Œ, left-hand의 operator[]μ—μ„œ operator+=μ΄λ‹ˆ operator-=λΌ ν˜ΈμΆœν•˜λŠ”κ±΄ μ‹€νŒ¨ν•œλ‹€. μ™œλƒν•˜λ©΄ operator[]κ°€ λ°˜ν™˜ν•˜λŠ” 것은 ν”„λ‘μ‹œ 객체 이기 떄문이닀. λΉ„μŠ·ν•œ κ²½μš°μ— μ‘΄μž¬ν•˜λŠ” λͺ¨λ“  μ—°μ‚°μžκ°€ μ‹€νŒ¨ν•œλ‹€. operator*=, operator/=, operator<<=, operator-= λ“± 말이닀. λ§Œμ•½ 이런 μˆ˜ν–‰μ„ μ›ν•œλ‹€λ©΄ μ΄λŸ¬ν•œ ν•¨μˆ˜λ₯Ό λͺ¨λ‘ μ„ μ–Έν•΄μ£Όμ–΄μ•Ό ν•˜λŠ”λ°, μ΄λŠ” λ„ˆλ¬΄ 일이 λ§Žλ‹€ 그리고 μ•„λ§ˆ μ›ν•˜μ§€λ„ μ•Šμ„ 것이닀. μΆ”κ°€ν•œλ“€ μΆ”κ°€ν•˜μ§€ μ•Šμ€λ“€ λ‘˜λ‹€ 괴둜운 일이닀.

κ΄€κ³„μžˆλŠ” 문제둜 ν”„λ‘μ‹œλΌ ν†΅ν•œ μ‹€μ œ 겍체의 ν˜ΈμΆœμ—μ„œ μΌμ–΄λ‚ μˆ˜ μžˆλŠ”λ°, ν• μˆ˜ μ—†λŠ”κ²ƒμ— κ΄€ν•œ λͺ¨ν˜Έμ„±μ΄λ‹€. 예λΌλ“€μ–΄μ„œ, 유리수 배열을 μ°Έμ‘°μ„ΈκΈ°λ‘œ κ΅¬ν˜„ν–ˆλ‹€κ³  ν•΄λ³΄μž. 이것을 Rational 클래슀둜 μ •μ˜ν•˜κ³  Array ν…œν”Œλ¦Ώμ„ μ‚¬μš©ν•œλ‹€. μ½”λ“œλŠ” λ‹€μŒκ³Ό κ°™λ‹€.

~cpp 
class Rational {
public:
    Rational(int numerator = 0, int denominator = 1);
    int numerator() const;
    int denominator() const;
    ...
};
Array<Rational> array;
μ΄λŠ” μ˜ˆμΈ‘ν• μˆ˜ μžˆλŠ” λ°°μ—΄μ˜ μ‚¬μš©μ΄λ‹€. ν•˜μ§€λ§Œ ν—ˆμš©λ˜μ§€ μ•ŠλŠ”λ‹€.

~cpp 
cout << array[4].numerator();                     // μ—λŸ¬!

int denom = array[22].denominator();              // μ—λŸ¬!
μ΄λŸ¬ν•œ 어렀움은 μΆ©λΆ„νžˆ μ˜ˆμƒλœλ‹€. operator[]κ°€ λ°˜ν™˜ν•˜λŠ” μœ λ¦¬μˆ˜μ— κ΄€ν•œ ν”„λ‘μ‹œμ΄μ§€ μ§„μ§œ Rational객체가 μ•„λ‹ˆλ‹€. numeratorκ³Ό denominator 멀버 ν•¨μˆ˜λŠ” Rationalμœ„ν•΄μ„œλ§Œ μ‘΄μž¬ν•˜μ§€ ν”„λ‘μ‹œλΌ μœ„ν•΄μ„œλŠ” μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ”λ‹€. 그러λ€λ‘œ μ»΄νŒŒμΌλŸ¬κ°€ 이에 λŒ€ν•œ μˆ˜ν–‰μ„ λͺ»ν•œλ‹€. ν”„λ‘μ‹œλΌ λ§Œλ“œλŠ”κ²ƒμ€ 그듀이 의λΈν•˜λŠ” 객체와 λΉ„μŠ·ν•œκ±°μ§€ μ™„μ „νžˆ λ™μΌν•œ 객체의 κΈ°λŠ₯을 μ œκ³΅ν• μˆ˜ μ—†λ‹€.

아직도 ν”„λ‘μ‹œκ°€ μ§„μ§œ κ°μ²΄λΌ κ΅μ²΄ν•˜κΈ° νž˜λ“  λ¬Έμ œλŠ” 남아 μžˆλ‹€. λ‹€μŒκ³Ό 같이 reference둜 λ„˜κΈΈλ•Œ μ‘°μ°¨ 말이닀.

~cpp 
void swap(char& a, char& b);    // a 와 b의 값을 λ°”κΎΌλ‹€.

String s = "+C+";               // 에ꡬ, 이거 이 λ¬Έμžμ—΄μ€ "C++" 일텐데.

swap(s[0], s[1]);               // 그것을 κ³ μΉ λ €κ³  ν•œλ‹€.
String::operator[]λŠ” CharProxyλΌ λ°˜ν™˜ν•˜μ§€λ§Œ swapκ°€ μ›ν•˜λŠ” 것은 char&이닀. CharProxyλŠ” μ•„λ§ˆ μ•”μ‹œμ (implicit) ν˜•λ³€ν™˜μœΌλ‘œ char둜 변화할것이고 char&λ‘œλŠ” λ³€ν™˜μ΄ ν•„μš” μ—†λ‹€. κ°œλ‹€κ°€ ν˜•λ³€ν™˜λœ char은 swapλ‚΄λΆ€μ—μ„œ μˆ˜ν–‰μ— μ†Œμš©μ΄ μ—†λ‹€. μ™œλƒν•˜λ©΄, char은 μž„μ‹œ 객체이기 λ•Œλ¬Έμ΄λ‹€. 이에 λŒ€ν•œκ²ƒμ€ Item 19에 μžμ„Ένžˆ μ–ΈκΈ‰λ˜μ–΄ μžˆλ‹€.

λ§ˆμ§€λ§‰ ν”„λ‘μ‹œκ°€ μ‹€νŒ¨ν•˜λŠ” μ§„μ§œ κ°μ²΄λΌ κ΅μ²΄ν•˜μ§€ λͺ»ν•˜λŠ” 상황은 μ•”μ‹œμ (implicit) ν˜•λ³€ν™˜μ—μ„œ κΈ°μΈν•œλ‹€. ν”„λ‘μ‹œ κ°μ²΄λŠ” μ•”μ‹œμ (implicit)으둜 μ§„μ§œ 객체둜 ν˜•λ³€ν™˜ν• λ•Œ user-defined ν˜•λ³€ν™˜ ν•¨μˆ˜κ°€ λΆˆλ¦°λ‹€. 예λΌλ“€μ–΄μ„œ CharProxyλŠ” char둜 operator char을 ν˜ΈμΆœν•΄μ„œ λ³€ν™”ν•œλ‹€. Item 5의 μ„λͺ…을 보면 μ»΄νŒŒμΌλŸ¬λŠ” user-defined ν˜•λ³€ν™˜ ν•¨μˆ˜λΌ λ°˜μ‘ν•˜λŠ” 인자둜의 ν•„μš”μ„±μ΄ μžˆλŠ” λΆ€λΆ„μ—μ„œ ν•΄λ‹Ή 연산을 ν˜ΈμΆœν•œλ‹€κ³  ν•œλ‹€. κ²°κ΅­ ν•¨μˆ˜ ν˜ΈμΆœμ€ ν”„λ‘μ‹œκ°€ μ „λ‹¬λ λ•Œ μ‹€νŒ¨ν•˜λ©΄ μ‹€μ œ κ°μ²΄λΌ λ„˜κΈ°λŠ” 것을 μ„±κ³΅μ‹œμΌœμ„œ κ°€λŠ₯ν•œ 것이닀. 예λΌλ“€μ–΄μ„œ TVStationλ¦¬ν•˜λŠ” ν΄λž˜μŠ€μ— watchTV ν•¨μˆ˜κ°€ μžˆλ‹€κ³  ν•œλ‹€λ©΄:

~cpp 
class TVStation {
public:
    TVStation(int channel);
     ...

};

void watchTV(const TVStation& station, float hoursToWatch);
μ•”μ‹œμ  μˆ˜ν–‰μ— μ˜ν•΄μ„œ μš°λ¦¬λŠ” λ‹€μŒκ³Ό 같이 μˆ˜ν–‰ν• μˆ˜ μžˆλ‹€.

~cpp 
watchTV(10, 2.5);   // 10번 λ³Έλ‹€. 2.5μ‹œκ°„ λ³Έλ‹€.
ν•˜μ§€λ§Œ ν”„λ‘μ‹œ μ‚¬μš©, μ°Έμ‘°μ„ΈκΈ° 적용 배열을 μ‚¬μš©ν•œλ‹€λ©΄

~cpp 
Array<int> intArray;

intArray[4] = 10;

watchTV(intArray[4], 2.5);
이 λ¬Έμ œλŠ” μ•”μ‹œμ  ν˜•λ³€ν™˜μ΄ μ£ΌλŠ” 또 ν•˜λ‚˜μ˜ λ¬Έμ œμ΄λ‹€. 이것을 ν•΄κ²°ν•˜κΈ°λŠ” μ–΄λ ΅λ‹€. 사싀 TVStation에 μƒμ„±μžλΌ explicit둜 μ„ μ–Έν•˜λŠ” 것이 더 쒋은 λ””μžμΈμΌ 것이닀. κ·Έλ ‡λ‹€λ©΄ watchTVμ—μ„œ 컴파일이 μ‹€νŒ¨ν•œλ‹€. explicit에 κ΄€ν•œ μžμ„Έν•œ μ„λͺ…은 Item 5λΌ μ°Έκ³ ν•˜λΌ

2.4. Evaluation : 평가

Proxyν΄λž˜μŠ€λŠ” κ΅¬ν˜„ν•˜κΈ° μ–΄λ €μš΄ μ–΄λ–€ μƒν™©μ—μ„œ 해결책이 될수 μžˆλ‹€. 닀차원 배열이 첫번째, lvaue/rvalueκ°€ λ‘λ²ˆμ§Έ , μ•”μ‹œμ  ν˜•λ³€ν™˜ κΈˆμ§€κ°€ μ„Έλ²ˆμ§Έ 이닀.

또 Proxy ν΄λž˜μŠ€λŠ” 단점도 λ§Žμ΄λ„ 가지고 μžˆλ‹€. ν•¨μˆ˜κ°€ 값을 λ°˜ν™˜ν• λ•Œ ν”„λ‘μ‹œ 객체듀은 μž„μ‹œ 인자(temporaries:Item 19μ°Έκ³ )둜 μ „λ‹¬λœλ‹€. κ·Έλž˜μ„œ 그듀은 생성, μ‚­μ œλœλ‹€. 이것은 κ³΅μ§œκ°€ μ•„λ‹ˆλ‹€. 읽기와 μ“°κΈ°μ˜ κ²½μš°λΌ κ°€λ¦¬κΈ° μœ„ν•œ μ‘°μΉ˜λ„, μž„μ‹œμΈμžλΌ λ§Œλ“€κΈ° λ•Œλ¬Έμ— λΉ„μš©μ΄ λ°œμƒν•œλ‹€. ν”„λ‘μ‹œ ν΄λž˜μŠ€κ°€ μžˆμ–΄μ„œ μ†Œν”„νŠΈμ›¨μ–΄ κ΅¬μ‘°λŠ” λ³΅μž‘ν•΄ 진닀. 더 μ–΄λ €μš΄ λ””μžμΈ, κ΅¬ν˜„, 이해 그리고 μœ μ§€ 보수..

λ§ˆμ§€λ§‰μœΌλ‘œ μ§„μ§œ 객체에 λŒ€ν•œ 일의 λŒ€ν–‰μ€ μ’…μ’… λ¬Έλ²•μ˜ μ œν•œμ„ 가지고 μ˜¨λ‹€. μ™œλƒν•˜λ©΄ ν”„λ‘μ‹œ 객체가 μ‹€μ œ κ°μ²΄λΌ μ™„μ „νžˆ κ΅μ²΄ν• λ§ŒνΌ λŠ₯λ ₯을 κ°€μ§ˆμˆ˜ μ—†κΈ° λ•Œλ¬Έμ΄λ‹€. λŒ€λ‘œ ν”„λ‘μ‹œλŠ” μ‹œμŠ€ν…œμ˜ λ””μžμΈμ‹œ 더 λ‚˜μœ 선택을 가지고 μ˜¨λ‹€. ν•˜μ§€λ§Œ λ§Žμ€ 경우 ν”„λ‘μ‹œμ˜ μ‘΄μ œλŠ” ν΄λΌμ΄μ–ΈνŠΈμ—κ²Œ κ·Έ μˆ˜ν–‰μ„ μ˜μ‹ν•˜κ²Œ ν•˜λŠ” κ²½μš°λŠ” 거의 μ—†λ‹€. μ˜ˆλΌ λ“€μ–΄μ„œ ν΄λΌμ΄μ–ΈνŠΈλŠ” 이차원 λ°°μ—΄μ˜ μ˜ˆμ œμ—μ„œ Array1D 객체의 μ£Όμ†ŒλΌ μ›ν•˜λŠ” ν΄λΌμ΄μ–ΈνŠΈλŠ” 거의 μ—†λ‹€. 그리고 ArrayIndex객체(Item 5μ°Έκ³ )λŠ” μ˜ˆμƒλ˜λŠ” λ‹€λ₯Έ ν˜•νƒœμ˜ ν•¨μˆ˜λ‘œ 전달 될것이닀. λ§Žμ€ κ²½μš°μ— ν”„λ‘μ‹œλŠ” μ§„μ§œ 객체의 μˆ˜ν–‰μ„ λŒ€ν–‰ν•œλ‹€. 그듀을 μ μš©ν• λ•Œ, 아무일 μ—†λŠ”κ±΄ 거의 λŒ€λΆ€λΆ„μ΄λ‹€.


Valid XHTML 1.0! Valid CSS! powered by MoniWiki
last modified 2021-02-07 05:23:49
Processing time 0.2079 sec