[STL deep dive]迭代器的實(shí)現(xiàn)探究2

這里把STL里處理iterator的tag-dispatching + trait class機(jī)制提取一點(diǎn)出來(lái)并淺析之.
完成了一個(gè)非常簡(jiǎn)易版的迭代器STL原型.完整版請(qǐng)查看(STL-Port)http://www.stlport.org/download.html].

這里只是為了說(shuō)明:

  • STL如何實(shí)現(xiàn)迭代器:
    1. 指定一個(gè)空struct作為不同級(jí)別迭代器的tag;
    2. trait_class用來(lái)提取具體某個(gè)class(迭代器class)中的這些域:iterator_category,value_type,difference_type,pointer,reference.
  • STL的每個(gè)Container都需要自己定義一個(gè)迭代器乡恕,并顯式地指明s個(gè)域贰拿,STL目前的版本是需要指明iterator_category,value_type,difference_type,pointer,reference這幾個(gè)域
  • <algorithm>中針對(duì)不同級(jí)別的迭代器指定不同的worker函數(shù)(利用函數(shù)重載機(jī)制).

山寨STL的iterator框架

文件列表:

_algorithm.h  
_algorithm_impl.tcc  
_features.h  
_iterator_base.h  
MyString_MyTest_Portable.cc
  • _features.h:
  1 #ifndef MYTEST__FEATURES_H
  2 #define MYTEST__FEATURES_H
  3 # define MYSPACENAME MyTest
  4 # define MYTEST_NAMESPACE_BEGIN namespace MYSPACENAME{
  5 # define MYTEST_NAMESPACE_END }
  6 #endif
  • _iterator_base.h:
  1 /* a toy for investigating impl. of STL-iterator: the so-called `tag-dispatching` & **trait_class**.
  2 * author: WeijianYang<weijyang@foxmail.com>
  3 *
  4 */
  5 #ifndef MYTEST__ITERATOR_BASE_H
  6 #define MYTEST__ITERATOR_BASE_H
  7 #include <cstddef>
  8
  9 #include "_features.h"
 10
 11 MYTEST_NAMESPACE_BEGIN
 12 //tags
 13 //they are all empty struct declaration
 14 //not diff between STL-impl but add the prefix `` :)
 15 struct input_iterator_tag {};
 16 struct output_iterator_tag {};
 17 struct forward_iterator_tag : public input_iterator_tag {};
 18 struct bidirectional_iterator_tag : public forward_iterator_tag {};
 19 struct random_access_iterator_tag : public bidirectional_iterator_tag {};
 20
 21 //`the trait of a class` means the feature of a class.
 22 template <class Iterator>
 23 struct iterator_traits {
 24     typedef typename Iterator::iterator_category iterator_category;
 25     //typedef typename xxxxx aaaaa - `typename` keyword indicates `xxxxx` is a type. And `typedef` indicates     an alias of xxxxx.
 26     typedef typename Iterator::value_type        value_type;
 27     typedef typename Iterator::difference_type   difference_type;
 28     typedef typename Iterator::pointer           pointer;
 29     typedef typename Iterator::reference         reference;
 30 };
 31
 32 //partially specialized for a pointer
 33 template <class Tp>
 34 struct iterator_traits<const Tp*> {
 35     typedef random_access_iterator_tag  iterator_category;
 36     typedef Tp            value_type;
 37     typedef ptrdiff_t     difference_type;
 38     typedef const Tp*     pointer;
 39     typedef const Tp&     reference;
 40 };
 41
 42 template <class Tp>
 43 struct iterator_traits<Tp*> {
 44     typedef random_access_iterator_tag  iterator_category;
 45     typedef Tp                         value_type;
 46     typedef ptrdiff_t                  difference_type;
 47     typedef Tp*                        pointer;
 48     typedef Tp&                        reference;
 49 };
 50
 51 #define DIFF_TYPE(ITER) typename iterator_traits<ITER>::difference_type
 53 //ptr/non-ptr type traits.
 54 struct ptr_type{};
 55 struct non_ptr_type{};
 56
 57 template <typename T>
 58 struct is_pointer_type
 59 {
 60     static non_ptr_type _val(){ return non_ptr_type();}
 61 };
 62
 63 template <typename T>
 64 struct is_pointer_type<T*>
 65 {
 66     static ptr_type _val(){ return ptr_type();}
 67 };
 68
 69
 70 template<class _Tp>
 71 inline random_access_iterator_tag __iterator_category(const _Tp*, const ptr_type& ){
 72     return random_access_iterator_tag();
 73 }
 74
 75 template<class _Tp>
 76 inline typename iterator_traits<_Tp>::iterator_category
 77 __iterator_category(const _Tp &,const non_ptr_type& ){
 78     typedef typename iterator_traits<_Tp>::iterator_category _Cate;
 79     return _Cate();
 80 }
 81
 82 #define ITERATOR_CATEGORY(_IT, _TP) __iterator_category(_IT, is_pointer_type<_TP>::_val())
 83
 84 MYTEST_NAMESPACE_END
 85 #endif

簡(jiǎn)要說(shuō)明:
15~50行直接從STL里抄下來(lái)的,
53~80主要是簡(jiǎn)單版的STL的ITERATOR_CATEGORY宏,主要是利用偏特化 + 重載來(lái)獲取特定某個(gè)Iterator對(duì)象的iterator_category標(biāo)簽.

  • _algorithm_impl.tcc:
  1 MYTEST_NAMESPACE_BEGIN
  2
  3 //input iterator only support ++, ==, !=
  4 template <class _InputIterator>
  5 DIFF_TYPE(_InputIterator)
  6 __distance(const _InputIterator &start,
  7             const _InputIterator &end,
  8             const input_iterator_tag &){
  9     DIFF_TYPE(_InputIterator) steps = 0;
 10     _InputIterator itr(start);
 11     while(itr != end){
 12         ++itr; ++steps;
 13     }
 14     return steps;
 15 }
 16 template <class _ForwardIterator>
 17 DIFF_TYPE(_ForwardIterator)
 18 __distance(const _ForwardIterator & start,
 19             const _ForwardIterator & end,
 20             const forward_iterator_tag &){
 21
 22     DIFF_TYPE(_ForwardIterator) steps = 0;
 23     _ForwardIterator itr(start);
 24     while(start != end){
 25         ++itr; ++steps;
 26     }
 27     return steps;
 28 }
 29
 30 template <class _RandomAccessIterator>
 31 DIFF_TYPE(_RandomAccessIterator)
 32 __distance(const _RandomAccessIterator & start,
 33             const _RandomAccessIterator & end,
 34             const random_access_iterator_tag &){
 35     return end - start;
 36 }
 37
 38 template <class _Iterator>
 39 DIFF_TYPE(_Iterator) distance(_Iterator start, _Iterator end){
 40     return __distance(start, end, ITERATOR_CATEGORY(start, _Iterator));
 41 }
 42
 43 MYTEST_NAMESPACE_END

簡(jiǎn)要說(shuō)明:這里實(shí)現(xiàn)了一個(gè)distance做例子玷氏,實(shí)際上STL的distance是直接實(shí)現(xiàn)在iterator_base里的,與這個(gè)類似,std:copy()或者std::find()都是有一些worker函數(shù)破衔,用來(lái)針對(duì)不同級(jí)別的迭代器.反正就類似這樣,核心思想是利用參數(shù)重載實(shí)現(xiàn)編譯時(shí)綁定.

  • _algorithm.h:
  1 #ifndef MYTEST_ALGOR_H
  2 #define MYTEST_ALGOR_H
  3
  4 #include "_iterator_base.h"
  5
  6 MYTEST_NAMESPACE_BEGIN
  7
  8 template <class _Iterator>
  9 DIFF_TYPE(_Iterator) distance(_Iterator start, _Iterator end);//STL: _iterator_base.h
 10 /*
 11 template <class _InputIterator, class _Tp>
 12 _InputIterator find(_InputIterator start, _InputIterator end, const _Tp& what){ }
 13
 14 template <class _InputIterator, class _Predicate>
 15 _InputIterator find_if(_InputIterator start, _InputIterator end, _Predicate __pred){ }
 16 */
 17 MYTEST_NAMESPACE_END
 18
 19 #include "_algorithm_impl.tcc" //template function must be visible to user.
 20 #endif

對(duì)外接口聲明.

  • MyString_MyTest_Portable.cc:
  1 #include <cstdio>
  2 #include <cstring>
  3 #include <cassert>
  4 #include <iostream>
  5
  6 #include "_algorithm.h"     //for MyTest::distance()
  7
  8 class MyString{
  9     public:
 10         MyString() : data_(NULL), size_(0){ }
 11         MyString(const char* s);
 12         MyString(const MyString &b) : size_(b.size_), data_(NULL){
 13             //deep copy
 14             if(size_){
 15                 data_ = new char [BufSize(size_)];
 16                 memcpy(data_, b.data_, BufSize(size_));
 17             }
 18         }
 19         ~MyString(){
 20             if(data_){
 21                 delete [] data_;
 22             }
 23         }
 24
 25         MyString& operator=(const MyString &);
 26         bool operator!=(const MyString &);
 27         bool operator==(const MyString &);
 28         MyString operator+(const MyString &);
 29         MyString& operator+=(const MyString &);
 30         char& operator[](const int);
 31
 32
 33
 34         class Iterator{
 35             public:
 36                 typedef typename MyTest::random_access_iterator_tag iterator_category;
 37                 typedef char value_type;
 38                 typedef int difference_type;
 39                 typedef char* pointer;
 40                 typedef char& reference;
 41
 42                 Iterator() : ptr(NULL){ printf("[DBG] Iterator() is called\n"); }
 43                 Iterator& operator++(){
 44                     ptr++;
 45                     return *this;
 46                 }
 47                 Iterator& operator--(){
 48                     ptr--;
 49                     return *this;
 50                 }
 51                 Iterator& operator+(int x){
 52                     ptr += x;
 53                     return *this;
 54                 }
 55                 Iterator& operator-(int x){
 56                     ptr -= x;
 57                     return *this;
 58                 }
 59                 //error: no match for ‘operator-’ (operand types are ‘MyString::Iterator’ and ‘MyString::Ite    rator’)
 60                 //we have to impl the operator-(Iterator)
 61                 difference_type operator-(const Iterator& rhs) const{
 62                     return ptr - rhs.ptr;
 63                 }
 64                 bool operator!=(const Iterator &s){
 65                     return s.ptr != ptr;
 66                 }
 67                 bool operator==(const Iterator &s){
 68                     return !(*this != s);
 69                 }
 70                 //std::find() will use this operator
 71                 bool operator==(value_type c){
 72                     return *ptr == c;
 73                 }
 74                 char& operator*(){
 75                     return *ptr;
 76                 }
 77
 78             private:
 79                 friend MyString;
 80                 Iterator(char *s) :  ptr(s){
 81 //                  printf("[DBG] Iterator(char*) is called\n");
 82                 }
 83             protected:
 84                 char* ptr;
 85         };
 86
 87         class OuputIterator : public Iterator{
 88             public:
 89                 char& operator*(){
 90                     if(ptr == mptr_->end().ptr){
 91                         int offset = mptr_->size_;
 92                         mptr_->ReSizeCopyBuf((mptr_->size_ + 1) * 2);
 93                         ptr = mptr_->data_ + offset;
 94                     }
 95                     return *ptr;
 96                 }
 97                 void print(){
 98                     printf("[DBG] %p\n", ptr);
 99                 }
100             private:
101                 friend MyString;//friend is not inherited
102                 MyString* mptr_;
103                 OuputIterator(MyString* me) : mptr_(me){ }
104                 OuputIterator(char *s) : Iterator(s) { /*printf("[DBG] OuputIterator(char*) is called\n"); */}
105         };
106
107         Iterator begin(){
108             return Iterator(data_);
109         }
110         Iterator end(){
111             return Iterator(data_ + size_);
112         }
113         OuputIterator obegin(){
114             return OuputIterator(data_);
115         }
116         OuputIterator oend(){
117             return OuputIterator(data_ + size_);
118         }
119
120         int size(){ return size_; }
121     private:
122         char* data_;//end with '\0'
123         int size_;
124         int BufSize(const int s) const{ return s + 1; }
125         char* ReSizeBuf(int s){
126 //          std::cout << "[DBG]\n";
127 //          std::cout << s << size_ << std::endl;
128             if(s > size_){
129                 if(data_){ delete [] data_; }
130                 data_ = new char [BufSize(s)];
131             }
132             size_ = s;
133             return data_;
134         }
135         char* ReSizeCopyBuf(int s){
136             if(s > size_){
137                 char* new_data_ = new char [BufSize(s)];
138                 if(data_){
139                     memcpy(new_data_, data_, BufSize(size_));
140                     delete [] data_;
141                 }
142                 data_ = new_data_;
143             }
144             size_ = s;
145             return data_;
146         }
147         friend OuputIterator;
148         friend std::ostream & operator<<(std::ostream &out, const MyString& s);
149 };
150 MyString::MyString(const char* s)
151  : size_(strlen(s)),
152  data_(NULL)
153 {
154     if(size_){
155         data_ = new char [BufSize(size_)];
156         memcpy(data_, s, BufSize(size_));
157     }
158 }
159
160 MyString& MyString::operator=(const MyString &b)
161 {
162     //deep copy
163     //origin data is overwrote
164     if(&b != this){
165         ReSizeBuf(b.size_);
166         memcpy(data_, b.data_, BufSize(size_));
167     }
168     return *this;
169 }
170 bool MyString::operator!=(const MyString & b)
171 {
172     return !(*this == b);
173 }
174 bool MyString::operator==(const MyString & b)
175 {
176     if(b.size_ == size_){
177         return memcmp(b.data_, data_, size_) == 0;
178     }
179     return false;
180 }
181 //It's not good to do this because it will do 2 alloc.s & dealloc.s(temp. var in c++)
182 MyString MyString::operator+(const MyString &b)//will concat the two string.
183 {
184     MyString tmp;
185     memcpy(tmp.ReSizeBuf(size_ + b.size_), data_, size_);
186     memcpy(tmp.data_ + size_, b.data_, BufSize(b.size_));
187     return tmp;
188 }
189 MyString& MyString::operator+=(const MyString &b)
190 {
191     char* tmp = BufSize(size_) < BufSize(size_ + b.size_) ?
192         new char [BufSize(size_ + b.size_)] : data_ ;
193     if(tmp != data_){
194         memcpy(tmp, data_, size_);
195     }
196     memcpy(tmp + size_, b.data_, BufSize(b.size_));
197     if(tmp != data_){
198         delete [] data_;
199     }
200     data_ = tmp;
201     size_ = size_ + b.size_;
202     return *this;
203 }
204
205 char& MyString::operator[](const int idx)
206 {
207     assert(idx < size_);
208     return *(data_ + idx);
209 }
210 std::ostream & operator<<(std::ostream &out, const MyString& s)
211 {
212     return s.data_ ? out << s.data_ : out << "";
213 }
214
215
216 void test1(){
217     std::string ss = "12345";
218     std::string::iterator itr;
219     for(itr = ss.begin(); itr != ss.end(); itr++)
220     {
221         printf("%c ", *itr);
222     }
223     printf("\n");
224 }
225 void test_MyString()
226 {
227     MyString s1;
228     MyString s2("Hello");
229     MyString s3 = "1234";
230     MyString s4(s3);
231     std::cout << s1 << s2 << s3 << s4 << std::endl;
232 }
233
234 void test_operator()
235 {
236     MyString s1 = "Hello";
237     MyString s2 = "Hi";
238     MyString s3 = ", there";
239     MyString s4, s5("fff");
240     std::cout << s4 << " " << s4.size() << ";" << s5 << " " << s5.size() <<std::endl;
241     s4 = s5 = s1;
242     std::cout << s4 << " " << s4.size() << ";" << s5 << " " << s5.size()<<std::endl;
243
244     std::cout << (s2 == s1 ? "yes" : "no") << std::endl;
245     s2 = s1;
246     std::cout << (s2 != s1 ? "no" : "yes") << std::endl;
247
248     std::cout << s2 + s3 << std::endl;
249     s2 += s3;
250     std::cout << s2 << " " << s2.size() << std::endl;
251
252     s2[0] = 'K';
253     std::cout << s2 << std::endl;
254 }
255
256 void test_iterator()
257 {
258     MyString ssx = "Hi, My name is...";
259 /*
260 the post-increment ==> itr++ ==> will call MyString::Iterator::operator++(0)
261 pre-increment.cc:195:6: error: no ‘operator++(int)’ declared for postfix ‘++’ [-fpermissive]
262    itr++){
263       ^
264     for(MyString::Iterator itr = ssx.begin();
265         itr != ssx.end();
266         itr++){
267             std::cout << *itr << " " << std::endl;
268         }
269 */
270     for(MyString::Iterator itr = ssx.begin();
271         itr != ssx.end();
272         ++itr){
273             std::cout << *itr << " ";
274         }
275         std::cout << std::endl;
276 }
277 void test_MyTest()
278 {
279     MyString testss = "Hi, My Name is REM.";
280     std::cout << MyTest::distance<MyString::Iterator>(testss.begin(), testss.end()) <<std::endl;
281     //std::distance() is actually included at the <iterator>
282 }
283
284
285 void testobegin()
286 {
287     MyString testss = "Hi, I am Rem.Nice to meet you.";
288     MyString::OuputIterator oitr = testss.obegin();
289     oitr.print();
290 }
291 int main()
292 {
293     test_MyTest();
294     return 0;
295 }

用上一篇實(shí)現(xiàn)的那個(gè)MyString類做測(cè)試.(上一篇實(shí)現(xiàn)了適用于STL钱烟, 這里直接改成適用于MyTest)

  • 編譯 & 運(yùn)行:
g++ MyString_MyTest_Portable.cc -o test
./test
19

TODO

  • 3個(gè)遺留問(wèn)題
  • 實(shí)現(xiàn)一個(gè)模擬STL的tag-dispatching.

這部分終于嘗試了一下做了一點(diǎn).STL里除了原理部分還有一大堆的平臺(tái)相關(guān)代碼晰筛,看起來(lái)真是日了狗.

實(shí)現(xiàn)部分應(yīng)該算是理解得比較ok了,接下來(lái)需要認(rèn)真地研究一下各大container如何使用的.(無(wú)聊臉)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末拴袭,一起剝皮案震驚了整個(gè)濱河市读第,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拥刻,老刑警劉巖怜瞒,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異般哼,居然都是意外死亡吴汪,警方通過(guò)查閱死者的電腦和手機(jī)尘吗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)浇坐,“玉大人睬捶,你說(shuō)我怎么就攤上這事〗酰” “怎么了擒贸?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)觉渴。 經(jīng)常有香客問(wèn)我介劫,道長(zhǎng),這世上最難降的妖魔是什么案淋? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任座韵,我火速辦了婚禮,結(jié)果婚禮上踢京,老公的妹妹穿的比我還像新娘誉碴。我一直安慰自己,他們只是感情好瓣距,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布黔帕。 她就那樣靜靜地躺著,像睡著了一般蹈丸。 火紅的嫁衣襯著肌膚如雪成黄。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天逻杖,我揣著相機(jī)與錄音奋岁,去河邊找鬼。 笑死荸百,一個(gè)胖子當(dāng)著我的面吹牛闻伶,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播管搪,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼虾攻,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了更鲁?” 一聲冷哼從身側(cè)響起霎箍,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎澡为,沒(méi)想到半個(gè)月后漂坏,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年顶别,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了谷徙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡驯绎,死狀恐怖完慧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情剩失,我是刑警寧澤屈尼,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站拴孤,受9級(jí)特大地震影響脾歧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜演熟,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一鞭执、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧芒粹,春花似錦兄纺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)猎提。三九已至获三,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間锨苏,已是汗流浹背疙教。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留伞租,地道東北人贞谓。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像葵诈,于是被迫代替她去往敵國(guó)和親裸弦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 【轉(zhuǎn)載】原文地址:std::string詳解作者:kieven2008 之所以拋棄char*的字符串而選用C++標(biāo)...
    VAYY閱讀 644評(píng)論 0 2
  • 這門課主要偏重于泛型編程(generic programming)以及底層對(duì)象模型(this,vptr,vtbl,...
    無(wú)心浪子閱讀 452評(píng)論 0 1
  • 再讀高效c++作喘,頗有收獲理疙,現(xiàn)將高效c++中的經(jīng)典分享如下,希望對(duì)你有所幫助泞坦。 1窖贤、盡量以const \enum\i...
    橙小汁閱讀 1,223評(píng)論 0 1
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,719評(píng)論 0 9
  • 早睡早起,這已經(jīng)是前幾個(gè)月我給自己定的目標(biāo)了赃梧,可是滤蝠,一天也沒(méi)有堅(jiān)持過(guò),此時(shí)此刻授嘀,我有些懊惱物咳,我是一個(gè)有拖延癥的人...
    趙婉寧閱讀 188評(píng)論 0 0