2009-10-16 24 views
2

我想用C++在我創建的字符串類中創建substr方法。C++中的l值substr方法

字符串類當然是基於C風格的字符串,我會照顧內存管理。

我想寫一個substr(start, length)功能,可以在常規的工作方式:在這種方式

CustomString mystring = "Hello"; 

cout << mystring.substr(0,2); // will print "He" 

而且也:

mystring.substr(1,3) = "DD"; // mystring will be "HDDo" 

注意,即使我得到了3個字符長個子 - 字符串,我只分配了2個字符,輸出字符串仍然是HDDo。

任何想法如何做到這一點?

謝謝!

+14

C++需要什麼。 *另一個*字符串類。 – jalf 2009-10-16 19:52:51

+3

「......編寫字符串類是C++程序員中最受歡迎的室內運動之一。」 (P.J.Plauger,「The Draft C++ Standard Library」,p345) – sbi 2009-10-16 20:24:46

+0

* I *在一年或兩年前寫了一個鏈表類。因爲我知道C++ *需要另一個LL類。 – 2009-10-16 20:48:31

回答

6

爲了支持這一點,您可能必須編寫您的substr()以返回一個代理對象,用於跟蹤原始字符串的哪部分被引用。該代理對象將重載operator=,並將在其中將新引用的子字符串替換爲新分配的字符串。

編輯迴應評論:代理的想法是,它是足夠類似於它是代理的類返回代理仍然是一個封閉的操作 - 即從用戶的角度來看,所有可見的是原始類型的對象,但它具有在沒有代理的情況下不可能實現的功能(或者會更難實現)。在這種情況下,我們代理類對於字符串類是私有的,所以用戶不能創建代理類的實例,除非是臨時的。如果您指定了該臨時文件,則可以使用該臨時文件來修改其父字符串。以任何其他方式使用代理只會產生一個字符串。

至於這樣做會讓你試圖在原始字符串中做所有事情:每個代理對象都是一個臨時對象 - 編譯器可以/將會跟蹤如何根據需要創建臨時對象,並正確銷燬它們在完整表達式的末尾等。編譯器還會跟蹤特定分配引用的子字符串,當我們嘗試使用它的值時會自動將其轉換爲字符串,等等。簡而言之,編譯器可以處理幾乎所有涉及的艱苦工作。

這是一些工作代碼。周圍的字符串類是非常小的(例如它沒有搜索能力)。我希望能夠爲字符串類的有用版本添加相當的數量。然而,代理類是完整的 - 我不希望在字符串類的功能完整版中看到它發生很大變化(如果有的話)。

#include <vector> 
#include <algorithm> 
#include <iostream> 
#include <iterator> 

class string { 
    std::vector<char> data; 
public: 
    string(char const *init) { 
     data.clear(); 
     data.assign(init, init+strlen(init)); 
    } 

    string(string const &s, size_t pos, size_t len) { 
     data.assign(s.data.begin()+pos, s.data.begin()+pos+len); 
    } 

    friend class proxy; 

    class proxy { 
     string &parent; 
     size_t pos; 
     size_t length; 
    public: 
     proxy(string &s, size_t start, size_t len) : parent(s), pos(start), length(len) {} 

     operator string() { return string(parent, pos, length); } 

     proxy &operator=(string const &val) { 
      parent.data.erase(parent.data.begin()+pos, parent.data.begin()+pos+length); 
      parent.data.insert(parent.data.begin()+pos, val.data.begin(), val.data.end()); 
      return *this; 
     } 
    }; 

    proxy substr(size_t start, size_t len) { 
     return proxy(*this, start, len); 
    } 

    friend std::ostream &operator<<(std::ostream &os, string const &s) { 
     std::copy(s.data.begin(), s.data.end(), std::ostream_iterator<char>(os)); 
     return os; 
    } 
}; 

#ifdef TEST 

int main() { 
    string x("Hello"); 

    std::cout << x << std::endl; 

    std::cout << x.substr(2, 3) << std::endl; 

    x.substr(2, 3) = "DD"; 

    std::cout << x << std::endl; 

    return 0; 
} 

#endif 

編輯2: 至於子的子去,這要看情況。目前沒有涉及的一種情況是,如果您想要分配給子字符串的子字符串,並且會影響原始字符串。如果你想要像x=y.substr(1,4).substr(1,2);這樣的東西,它會正常工作。第一個代理將被轉換爲一個字符串,第二個substr將在該字符串上被調用。

如果你想要:x.substr(1,4).substr(1,2) = "whatever";它目前不會工作。我不知道它實現了,但這樣的假設是這樣,除了支持是相當小的 - 你一SUBSTR成員添加到代理:

proxy substr(size_t start, size_t len) { 
    return proxy(parent, pos+start, len); 
} 
+0

但是如果我想要一個子字符串的子字符串呢? – 2009-10-17 08:16:15

+0

@Earwicker:然後'proxy :: operator string()'應該踢進去。 – sbi 2009-10-17 13:09:13

+0

@sbi - 也許它應該,但它不會在C++中看到Jerry的更新答案。我認爲傑裏是對的 - 在這種形式下它並不是很成功;如果我們可以統一處理字符串(不管它們是否被實現)會更好,而不是有兩種類型來表示字符串。否則,我無法傳遞一個子字符串以使'foo(string&s)'無效以讓'foo'修改它。我必須編寫所有這些功能,如模板,(在大多數平臺上)將代碼壓入頭文件。 – 2009-10-17 15:51:54

0

想必你想substr返回一個字符串,而不是其他一些代理類。因此,您需要讓您的字符串類能夠保存一個指向其自己的字符串數據副本的指針,並且還需要一個指向另一個字符串對象的指針(它作爲返回值substr),以及關於哪個它是從它創建的字符串的一部分。

當您從substr的另一個調用返回的字符串上調用substr時,這可能會變得非常複雜。

複雜性可能不值得界面的吸引力。

+0

如果一個字符串引用另一個字符串,那麼它是什麼,如果不是代理對象?考慮到你描述的複雜性,那麼通過傑裏的代理人想法,你會買什麼? – sbi 2009-10-16 20:19:38

+0

重點在於子字符串的獲取應該是一個封閉的操作 - 它應該返回另一個相同類型的對象,而不是*另一個*代理類。即字符串本身將作爲代理。而且,在任何完全有能力的代理類中顯然都存在相同的複雜性。 – 2009-10-16 20:33:16

+0

這會給這種情況帶來問題:CustomString mystring =「Hello」; CustomString mysub = mystring.substr(1,3); mysub = 「DD」;想必你會希望將這個任務分配給mysub來打破與父母的連接。 – 2009-10-16 20:52:53

0

第一個要求很簡單;查看運營商的標準實施。

鬆散,c_string& substr(int, int)

第二部分,沒有這麼多,我不認爲。我相信它看起來很相似。不過,我會考慮一下,並在週末回覆你。