Die sechs Hauptkomponenten von stl für den Einstieg in C++ – Simulationsimplementierung von String-Bibliotheksfunktionen

Artikelverzeichnis

Vorwort

1. Simulationsimplementierung der String-Klasse

1.Bau

1) Konstruktion ohne Parameter

2) Parametrische Struktur

2.Konstruktion kopieren

3. Überlastung des Zuweisungsoperators

3. Zerstörung

4. Überlastung des Bedieners

1)Operator[ ]

2)Betreiber>

3)Operator==

3)Operator>=

4)Betreiber<

5)Operator<=

6)Betreiber!=

5. Einige Schnittstellenimplementierungen

1)Größe ändern

2)umgekehrt

3)Anfang+Ende

4) Rückstoß

5)anhängen

6)Operator+=

7)einfügen

8)tauschen

9)c_str

10)finden

11) klar

12)löschen

6. Stream-Einfügung und Stream-Extraktion

1)Betreiber<<

2)Betreiber>>

Zusammenfassen


Vorwort

Um ein tieferes Verständnis der zugrunde liegenden Prinzipien der String-Klasse zu erlangen, verwendet dieses Kapitel die Idee der Datenstruktur, um Ressourcen zu verwalten und die Implementierung einiger Schnittstellen der String-Klasse zu simulieren. Insbesondere 1. Variablen können wie int-Typen definiert werden und unterstützen Zuweisung und Zuweisung. 2. Kann als Parametertyp und Rückgabetyp der Funktion verwendet werden. 3. Der Elementtyp, der als Standardbibliothekscontainer verwendet werden kann, dh der Werttyp von vector/list/deque. (Auszug aus coolshell)


1. Simulationsimplementierung der String-Klasse

Im vorherigen Kapitel wurde hauptsächlich eine kurze Einführung in die String-Klasse und die Schnittstelle gegeben. In vielen Interviews bat der Interviewer die Schüler, die Implementierung der String-Klasse zu simulieren, wobei hauptsächlich die Konstruktion, Kopierkonstruktion, Operatorüberladung und der Destruktor der String-Klasse implementiert wurden In diesem Kapitel werden sie einzeln vorgestellt. Ideen und Implementierungsmethoden.

Zunächst muss klar sein, dass die String-Klasse gekapselt und aus einem String aufgebaut ist. Um das Hinzufügen, Löschen, Abfragen und Ändern in dieser Klasse zu implementieren, benötigen Sie die gleiche Größe und Kapazität wie die Sequenztabelle, die jeweils das darstellt Anzahl gültiger Zeichenfolgen und die Kapazität dieses Blocks. Die Größe von.

class string
{
    pubilc:
        //实现一些方法
    private:
        //成员变量
        char * _str;
        size_t _size;   //存多少个有效字符
        size_t _capacity;  //这段空间的大小
}

1. Flache Kopie

Der Compiler wird auch als Bitkopie bezeichnet und kopiert lediglich den Wert im Objekt. Wenn das Objekt Ressourcen verwaltet, führt dies schließlich dazu, dass mehrere Objekte eine Ressource gemeinsam nutzen. Wenn ein Objekt zerstört wird, wird die Ressource freigegeben und zu diesem Zeitpunkt andere Objekte wissen nicht, dass die Ressource freigegeben wurde, da sie davon ausgehen, dass sie noch gültig ist. Daher kommt es zu einer Zugriffsverletzung, wenn sie weiterhin mit dieser Ressource arbeiten.

2. Tiefe Kopie

Wenn eine Klasse Ressourcenverwaltung umfasst, müssen deren Kopierkonstruktor, Überladung des Zuweisungsoperators und Destruktor explizit angezeigt werden. Deep Copy kann verwendet werden, um das Problem der flachen Kopie zu lösen, d. h. jedes Objekt verfügt über eine unabhängige Ressource und wird nicht mit anderen Objekten geteilt.

1.Bau

1) Konstruktion ohne Parameter

Zweck: Erstellen Sie ein leeres String-Klassenobjekt, dh eine leere Zeichenfolge

string s1; //无参初始化 

Implementierungsidee: Die Verwendung einer parameterlosen Konstruktion besteht darin, eine leere Zeichenfolge zu übergeben. Wenn in der Konstruktionsliste ein Nullptr an str übergeben wird und wir c_str() // verwenden, um die Zeichenfolge str zurückzugeben, wird auf den Nullzeiger zugegriffen. ,cout kann nicht gedruckt werden.

string()
    :_str(new char[1])   // 这里 new char 也是一个 加[]是和析构delete[]匹配
    ,_size(0)
    ,_capacity(0)
{
    _str[0] = '\0';    //字符串里只有一个\0
}
    

2) Parametrische Struktur

string s2("hello world");
string(const char * str)
     //如果这里直接使用初始化列表new出来,就需要不停的扩容
    //:_str(str)    //如果这里直接用const str给了str 后面要对str进行修改 则不能修改
    //,
    _size(strlen(str)) //初始化顺序得按声明顺序来,为了避免这种,只初始化一个
    //,_capacity(strlen(str)+1)
{
    _capacity = _size;
    _str = new char[_capacity+1]; //new出char类型的capacity+1个大小的字节  +1是给\0
    strcpy(str,str); //按字节拷贝过来
}

private:
    char * str;
    size_t _size;
    size_t _capacity;


void test()
{
    string s1("hello world");
}

2.Konstruktion kopieren

Konstruktion kopieren, das heißt, das neue Objekt muss die gleiche Größe und Kapazität wie das alte Objekt haben. Sie können Werte direkt in der Konstruktionsliste zuweisen und die Zeichenfolge mit strcpy kopieren.

string(const string&s)
    :_size(s._size)
    ,_capacity(s._capacity)
{
    _str = new char[s._capacity+1];  //_str 是一个字符数组 new capacity个空间
    strcpy(_str,s._str);
}

3. Überlastung des Zuweisungsoperators

Es wird hier separat geschrieben, um es mit der Kopierstruktur zu vergleichen.

Der Parametertyp verwendet eine Referenz, und der Rückgabewert verwendet ebenfalls eine Referenz, um eine kontinuierliche Zuweisung zu unterstützen. Überprüfen Sie gleichzeitig, ob Sie sich selbst einen Wert zugewiesen haben und geben Sie *this zurück. Hier liegt ein Problem vor: Die Kapazität des zugewiesenen Objekts und des zugewiesenen Objekts kann unterschiedlich sein und größer, kleiner oder gleich sein. Es gibt eine Situation, in der der Raum erneut geöffnet werden muss. Die Verarbeitungsmethode des Compilers lautet: Öffnen Sie zuerst einen temporären Bereich, der mit dem kopierten Objekt identisch ist, kopieren Sie dann die Zeichenfolge hinein, zerstören Sie dann den ursprünglichen Bereich des zugewiesenen Objekts und weisen Sie die vom temporären Objekt kopierte Zeichenfolge dem zugewiesenen Objekt zu. Der Grund für das Öffnen eines temporären Speicherplatzes besteht darin, dass ein Speicherabschnitt erstellt und der Inhalt des eingehenden Objekts kopiert werden muss, um zu verhindern, dass derselbe Speicherabschnitt während der Zerstörung wiederholt freigegeben wird, was zum Absturz des Programms führt.

string& operator=(const string&s)
{
    //防止自己赋值给自己
    if(*this!= &s)
    {
        //先开空间,成功了再拷贝
        char * tmp = new char[s._capacity+1];
        strcpy(tmp,s._str);
    
        //释放掉原来的空间
        delete[] _str;
        //再拷贝给_str;
        _str = tmp;
        _size = s._size;
        _capacity = s._capacity;
    }
    
    return * this;
}

3. Zerstörung

Durch die Zerstörung wird hauptsächlich die Bereinigung der Ressourcen abgeschlossen. Beachten Sie, dass _str hier auf einen Nullptr verweisen sollte.

~string()
		{
			delete[] _str;
			_str = nullptr;
			_size = _capacity = 0;
		}

4. Überlastung des Bedieners

1)Operator[ ]

Funktion: Gibt das Zeichen an der Pos-Position zurück

const size_t & operator[](size_t pos) const   //传入的this不能修改
{
    return _str[pos];
}


char & operator[](size_t pos)
{
    assert(pos<_size);
    return _str[pos];
}

2)Betreiber>

Funktion: Vergleichen Sie die Größe zweier Zeichenfolgen. Der Vergleich erfolgt hier im ASCII-Code. Verwenden Sie die Funktion strcmp.

bool operator>(string&s)const
{
    return strcmp(_str,s._str)  >0 ;
}

3)Operator==

bool operator==(string& s) const
{
    return strcmp(str,s._str) == 0;
}

3)Operator>=

bool operator>=(string& s) const
{
    return *this>s || *this ==s;
}

4)Betreiber<

bool operator<(const string* s) const
{

    return !(this>=s);
}

5)Operator<=

bool operator<=(const string&s) const
{
    return *this < s || *this == s;
}

6)Betreiber!=

bool operator!=(const string &s) const
{
    return !(*this ==s);
}

5. Einige Schnittstellenimplementierungen

1)Größe ändern

Funktion: 1. Ändern Sie die gültigen Zeichen in str in n und füllen Sie den zusätzlichen Raum mit dem Zeichen c (Leerzeichen + Initialisierung).

void resize(size_t n,char ch= '\0')
{
 if(n<=_size)
    {
        _str[n] = '\0';
        _size = n;
    }
  else
    {
        if(n> _capacity)
        {
           reserve(n);
        }

        size_t i = _size;
        while(i<n)
        {
            _str[i] = ch;
            ++i;
        }
        _size = n;
        _str[_size] = '\0';
    
       }
}

2)umgekehrt

Funktion: Reservieren Sie Platz für eine Zeichenfolge und belegen Sie den neuen Platz mit \0. Erstellen Sie zunächst mit new einen temporären Speicherplatz, kopieren Sie die Zeichenfolge aus dem ursprünglichen Speicherplatz hinein, geben Sie dann den ursprünglichen Speicherplatz frei und weisen Sie _str den neuen Speicherplatz zu. Wenn der Platz kleiner als der ursprüngliche Platz ist, wird er nicht verkleinert.

void reserve(size_t n,char ch='\0')
{
    char * tmp = new char[n+1];
    strcpy(tmp,_str);
    delete[] str;
    _srt = tmp;
    
    _capacity = n;
}

3)Anfang+Ende

Funktion: Verwenden Sie einen Iterator. Beginnen Sie, um das erste Zeichen abzurufen, und beenden Sie, um die nächste Position des letzten Zeichens abzurufen

char* begin()
{
    return _str;
}

char* end()
{
    return _str+_size;
}

4) Rückstoß

Funktion: Fügen Sie ein Zeichen nach der Zeichenfolge hinzu. Um ein Zeichen hinzuzufügen, müssen Sie feststellen, ob die Kapazität ausreicht. Wenn nicht, erweitern Sie die Kapazität um das Zweifache und platzieren Sie das Zeichen dann an der _size-Position.

void pushback(char ch)
{
    if(_size+1 >_capacity)
    {
        reserve(2*_capacity);
    }
    
    _str[size] = ch;
    ++size;

    _str[_size] = '\0'; //注意这里有\0
}

5)anhängen

Funktion: Fügen Sie nach der Zeichenfolge eine Zeichenfolge hinzu

void append(const char * str)
{
    size len = strlen(str);
    if(_size+len>_capacity)
    {
        reverse(2*capacity);
    }

   strcpy(_str+_size,str);

    _size+= len;
}

6)Operator+=

1. Fügen Sie nach der ursprünglichen Zeichenfolge ein Zeichen hinzu

2. Fügen Sie nach der ursprünglichen Zeichenfolge eine Zeichenfolge hinzu

string& operator+=(char ch)
{
    push_back(ch);
    retrun *this;
}

string& operator+=(const char * str)
{
    append(str);
    return *this;
}

7)einfügen

1. Funktion: Fügen Sie an einer beliebigen Position in der Zeichenfolge ein Zeichen hinzu

2. Funktion: Fügen Sie an einer beliebigen Stelle der Zeichenfolge eine Zeichenfolge hinzu

string& insert(size_t pos,const char ch)
{
    assert(pos<_size);
    //插入要检查容量
    int len = strlen(str);
    if(1+_size >_capacity)
        reverse(2*_capacity);
    //开始插入 先要挪动数据 插入一个字符 就挪动一个位置
    //现在要插入len个字符,就要挪动len个位置
    size_t _end = _size;
    while(pos < end)
    {
        _str[end+1] = _str[end]
        --end;
    }
    //放上数据
      _str[pos] = ch;
      ++size;
   
    return *this;
}
string& insert(size_t pos, const char * str)
{
    assert(pos<_size);
    int len = strlen(str);
    if(_size+len >_capacity)
    {
        reverse(2*_capacity);
    }
    
    size_t end = _size;
    //挪动数据
    while(end>pos)
    {
        _str[end+len] = _str[end];
        --end;
    }

    strnpy(_str+pos,_str,len);
    return * this;
}

8)tauschen

Funktion: Strings in zwei String-Klassen austauschen, auch Größe und Kapazität nehmen am Austausch teil.

void swap(string &s)
{
    std::swap(_str,s._str);
    std::swap(_capacity,s._capacity);
    std::swap(_size,s._size);
}

9)c_str

Geben Sie die Zeichenfolge im Zeichenfolgenklassenobjekt zurück, geben Sie sie einfach direkt zurück

char& c_str(string& s)
{
    return s._str;
}

 

10)finden

1. Funktion: Suchen Sie das Zeichen c ausgehend von der NPOS-Position und geben Sie die Position des Zeichens in der Zeichenfolge zurück.

2. Funktion: Durchsuchen Sie die Zeichenfolge ausgehend von der NPOS-Position und geben Sie die Position des Zeichens in der Zeichenfolge zurück.

size_t find(char ch,size_t pos = 0)
{
    assert(pos<_size);
    for(int i =0; i<_size;++i)
    {
        if(_str[i] == ch)
            return i;
    }

    return npos;
}
size_t find(char * str,pos = 0)
{
    assert(pos<_size);
    
    //strstr(const char* str1, const char*str2) 在str1中寻找是否存在str2,如果存在返回str2的地址,不存在返回null
    char * p = strstr(_str+pos,str);
    if( p == nullptr)
        return npos;
    else
        return p- _ str; 
}

11) klar

Funktionsfunktion: Setzen Sie alle Werte in _str auf Leerzeichen, das gültige Zeichen ist 0 und die Kapazität bleibt unverändert

void clear()
{
    _str[0] = '\0';
    _size = 0;
}

12)löschen

Funktion: Len-Zeichen an der Pos-Position löschen. Wenn len>strlen(_str), so viele Zeichen löschen, wie an der Pos-Position vorhanden sind.

//比如现在有一段字符串 1234567890  10个字符 删除pos = 5位置的两个字符
或者删除pos位置的10个字符 或者删除npos个字符(全删除)
// pos = 5 ;len = 2  1234890 890三个数字挪动到pos = 5的位置 
// pos = 5 ; len = 10 pos = 5的位置开始删除 _str[pos] = '\0'; 
//这两种都只改变_size,不用改变_capacity 
string& eraser(size_t pos,size_t len= npos)
{
    assert(pos<_size);
    
    //注意这里 npos = -1 pos+len 就溢出了 所以需要单独判断
    if(len == npos || pos+len >= _size)
    {
        _str[pos] = '\0';
        _size = pos;
    }

    else
    {
        strcpy(_str+pos,_str+pos+len);
        _size -=len;
    }

    return *this;
}

6. Stream-Einfügung und Stream-Extraktion

1)Betreiber<<

cout<<string<<endl;

ostream& operator<<(ostream& out, string& s)
{
    //遍历这个字符串打印
    for(auto ch:s)
    {
        out<<ch;
    }
        
    return out;
}

2)Betreiber>>

cout>>string

void clear()
{
    _str[_size] = '\0';
    _size = 0;
}

instream& operator>>(istream& in , string& s)
{
    //每次清空
    s.clear();
    //从缓冲区拿到数据
    char ch = in.get();
    size_t i= 0;
    while(ch != ' ' && ch == '\n')
    {
        s+= ch;
        buff[i++] = ch;
        //拿到127个清空数组,然后再加后面的字符串
        if(i == 127)
        {
            buff[127] = '\0';
            s+= buff; 
            i= 0;
        }
        
        ch = in.get();
        
    }
    
    return in;
 }

Zusammenfassen

Dieser Artikel imitiert hauptsächlich einige Quellcodes in der STL-Bibliothek. Die Technologie ist begrenzt. Bitte korrigieren Sie mich, wenn es Fehler gibt.

Ich denke du magst

Origin blog.csdn.net/jolly0514/article/details/131804318
Empfohlen
Rangfolge