2014-12-21 18 views
1

對於熟悉Accelerated C++一書的讀者,我正在編寫問題14-5的解決方案,並且遇到了一些有趣的行爲我無法解釋。加速C++ 14-5:自定義字符串類和引用計數器適用於一個構造函數,但不適用於另一個構造函數

問題涉及使用自定義字符串和指針/引用計數器類來實現一個程序,該程序可以連接字符串的向量並從中創建圖片。

從本質上講,有問題的程序的一部分如下:

int main() 
{ 
    vec<str> test; 
    str s; 

    while(getline(std::cin,s)) 
    { 
      test.push_back(str(s.begin(),s.end())); 
      //test.push_back(s); // This line doesn't work here - why? 

      // Using the above line results in every str in test being 
      // the empty string 
    } 

    // Use the vec<str> to make pictures 

} 

它好像我引用計數器不能正常工作時,我使用註釋行:結果我得到的是彷彿test中的每個str都是空字符串。

這裏是我的getline實現和strptr類的相關部分:

str類:

class str 
{ 
    friend std::istream& getline(std::istream &is, str &s); 

public: 
    typedef char* iterator; 
    typedef const char* const_iterator; 
    typedef size_t size_type; 

    str() : data(new vec<char>) { } 
    str(size_type n, char c) : data(new vec<char>(n,c)) { } 

    str(const char *cp) : data(new vec<char>) 
    { 
      std::copy(cp,cp+std::strlen(cp),std::back_inserter(*data)); 
    } 

    template <class InputIterator> 
    str(InputIterator b, InputIterator e) : data(new vec<char>) 
    { 
      std::copy(b,e,std::back_inserter(*data)); 
    } 

    // Other str member functions and operators 
private: 
    ptr< vec<char> > data; 
}; 

ptr類:

template <class T> 
class ptr 
{ 
public: 
    void make_unique() 
    { 
      if(*refptr != 1) 
      { 
        --*refptr; 
        refptr = new std::size_t(1); 
        p = p ? clone(p) : 0; 
      } 
    } 

    ptr() : p(0), refptr(new std::size_t(1)) { } 
    ptr(T* t) : p(t), refptr(new std::size_t(1)) { } 

    ptr(const ptr &h) : p(h.p), refptr(h.refptr) { ++*refptr; } 
    ptr& operator=(const ptr &); 
    ~ptr(); 

    T& operator*() const 
    { 
      if(p) 
      { 
        return *p; 
      } 

      throw std::runtime_error("unbound ptr"); 
    } 

    T* operator->() const 
    { 
      if(p) 
      { 
        return p; 
      } 

      throw std::runtime_error("unbound ptr"); 
    } 

private: 
    T* p; 
    std::size_t* refptr; 
}; 

template <class T> 
ptr<T>& ptr<T>::operator=(const ptr &rhs) 
{ 
    ++*rhs.refptr; 
    // free the left hand side, destroying pointers if appropriate 
    if(--*refptr == 0) 
    { 
      delete refptr; 
      delete p; 
    } 

    // copy in values from the right-hand side 
    refptr = rhs.refptr; 
    p = rhs.p; 
    return *this; 
} 

template <class T> 
ptr<T>::~ptr() 
{ 
    if(--*refptr == 0) 
    { 
      delete refptr; 
      delete p; 
    } 
} 

vec類基本上的子集0。如有必要,我也可以在這裏提供這些細節。

這裏是函數getline:

std::istream& getline(std::istream &is, str &s) 
{ 
    s.data->clear(); 
    char c; 
    while(is.get(c)) 
    { 
     if(c != '\n') 
     { 
      s.data->push_back(c); 
     } 
     else 
     { 
      break; 
     } 
    } 

return is; 
} 

回答

2

即使你正確引用計數,你仍然共享實例之間的相同的指針。所以getline正在修改相同的str對象。您需要在str中執行Copy-on-write

這裏是出了什麼問題:

std::istream& getline(std::istream &is, str &s) 
{ 
    s.data->clear();   //should make a copy of data first 
    char c; 
    while(is.get(c)) 
    { 
     if(c != '\n') 
     { 
      s.data->push_back(c); 
     } 
     else 
     { 
      break; 
     } 
    } 

return is; 
} 

所以,你應該做的:的

s.data = ptr(new vec<char>()); 

不是清除共享實例。

+0

@BartoszKP如果數據已經有一些舊信息,我想先擺脫它,所以也許調用clear(),然後新的將是最好的?換句話說,我確實希望getline覆蓋s中發生的任何事情。 – Adam27X

+0

@ Adam27X它不是「舊」的 - 它在'str'的​​其他實例中共享。你不想擺脫它。擺脫它是你的問題在這裏。如果參考計數器變爲「0」,'vec'將自動刪除。 – BartoszKP

+0

@BartoszKP,你在哪裏得到getline的實現?我沒有看到它的例子,除非我是盲目的... –

1

當你撥打:

test.push_back(s); // This line doesn't work here - why? 

現在sstest副本共享相同的數據。在while循環getline函數調用s.data->clear()的下一次迭代期間,其清除中的數據和s中的數據,並且在test中清除s的副本。

當你撥打:

test.push_back(str(s.begin(),s.end())); 

str(s.begin(),s.end())構造函數創建一個臨時str對象與在s並推動該對象到test數據的副本。因此,現在臨時對象和test中的副本共享相同的數據,這是s的非共享副本。臨時對象被破壞,並且test中的副本保持不變。

相關問題