1. 程式人生 > >【C++】定義自己的String類

【C++】定義自己的String類

我們自己寫的String類具有以下函式

1.建構函式

String(const char *s);    //用c字串s初始化
String(int n,char c);     //用n個字元c初始化

2.拷貝和賦值

String& String(String& str);
const String& operator=(String& str);

3.解構函式

~String();

4.下標訪問

過載下標訪問運算子

char &operator[](int n);
char &at(int n)const
;

5.String類提供的方法

int size()const;       
//返回當前字串的大小,string內部不會在我們每一次的字串連線的時候都會分配空間,
//它會多分配一些記憶體,下次連線時的字串小於剩下的空間它就不用再為這個字串分配另外的空間了。
//它分配的所有空間就是size,而字串的實際長度length
int length()const;       //返回當前字串的長度
bool empty()const;        //當前字串是否為空

6.過載流插入和提取運算子

為了方便輸入和輸出

istream& operator>>(istream& input, String& str);
ostream& operator
<<(ostream& output, String& str);

7.連線兩個字串

過載+運算子

String &operator+=(String &s);

8.字串比較

bool operator==(const String &s1,const String &s2)const;
int compare(const string &s) const;//比較當前字串和s的大小

這個實現的string類是比較簡單的,C++標準模板庫裡面的string類的方法是非常多的,而且非常複雜。
在這裡僅僅給大家演示string內部大概的實現方法

例項

String.h檔案:類的宣告

#ifndef STRING_H//標頭檔案衛士,避免標頭檔案被多次編譯
#define STRING_H
#include<iostream>  //輸入輸出標頭檔案
using namespace std; //名稱空間
class String
{
public://公有的宣告
    String(const char*); //建構函式
    String(int n,char c); //傳入大小的建構函式
    ~String(); //解構函式

public:
    String(String &str);  //拷貝建構函式
    const String& operator=(const String& str);//賦值運算子過載,我們在不需要修改引數的值的地方都應該宣告為const

    char operator[](int pos)const;//下標訪問運算子過載
    char at(int pos)const;//檢查越界的下標訪問

    int size()const; //字串的空間大小
    int length()const; //字串的字元大小
    bool empty()const; //判斷為空的函式

    const String& operator+=(const String& str); //字串的連線
    bool operator==(const String& str);//字串的相等判斷
    bool compare(const String& str);//字串的相等判斷

    friend istream& operator>>(istream& input, String& str); //輸入操作符的過載
    friend ostream& operator<<(ostream& output, String& str); //輸出操作符的過載
private:
    char* m_pBuff;//儲存字串的首地址
    int m_nRealLen;  //字串的字元大小
    int m_nBuffSize;  //字串的空間大小

};

#endif

String.cpp:類的實現

#include"String.h" //包含類的宣告標頭檔案

#define EXT_LEN 50  //定義一個巨集,使用者申請的記憶體大小,我們實際上為字串分配的空間要多EXT_LEN,以便字串的連線
String::String(const char* pStr)
{
    m_nRealLen = strlen(pStr); //獲得字串的實際大小
    m_pBuff = new char[m_nRealLen + EXT_LEN]; //實際分配的記憶體比字串多了EXT_LEN
    //strcpy(m_pBuff, pStr);  //C提供的一個字串函式,將後面引數的字元複製到第一個字串後面,並新增一個結束字元\0,C中字串的指標指向的是字串的首地址,這個函式遇到\0以為字串結束
    memcpy(m_pBuff, pStr, m_nRealLen);  //記憶體拷貝,遇到\0不會結束,提供了需要拷貝的資料的長度
    m_nBuffSize = m_nRealLen + EXT_LEN;  //計算字串的空間大小
}

String::String(int n, char c)  //構造一個由n個一種字串構成的字串
{
    m_pBuff = new char[n + EXT_LEN];  //實際分配的記憶體比字串多了EXT_LEN
    for (int i = 0; i < n; i++)
    {
        m_pBuff[i] = c;  //利用下標運算子和迴圈賦值
    }
    m_nRealLen = n;  //賦值字串大小
    m_nBuffSize = n + EXT_LEN;  //分配的空間大小
}

String::~String()
{
    if (m_pBuff)   //判斷字串指標是否為空
    {
        delete[] m_pBuff;
        m_pBuff = NULL;  //避免野指標的產生
    }

    m_nRealLen = 0;  //對字串的長度清零
    m_nBuffSize = 0; //對分配的空間清零 
}

String::String(String& str)
{
    //這裡不用判斷 左運算元的字串首地址是否指向記憶體,就銷燬再指向空
    //因為左運算元m_pBuff開始沒有賦值,預設會指向一個無法訪問的地址,在這裡銷燬無法訪問的地址,就會出現異常
    //if (m_pBuff) //如果左運算元的字串首地址指向記憶體,就銷燬再指向空
    //{
    //  delete[]m_pBuff;
    //  m_pBuff = NULL;
    //}
    m_pBuff = new char[str.length() + EXT_LEN];//重新分配一段空間
    //strcpy(m_pBuff, str.m_pBuff);
    memcpy(m_pBuff, str.m_pBuff, str.length());  //字串的拷貝
    m_nRealLen = str.length();   //實際長度等於字串的字元長度
    m_nBuffSize = m_nRealLen + EXT_LEN;
}

const String& String::operator=(const String& str)
{
    if (this == &str)  //避免自賦值
    {
        return *this;
    }

    if (m_pBuff) //如果左運算元的字串首地址指向記憶體,就銷燬再指向空
    {
        delete[]m_pBuff;
        m_pBuff = NULL;
    }

    m_pBuff = new char[str.length() + EXT_LEN];  //分配空間
    //strcpy(m_pBuff, str.m_pBuff);
    memcpy(m_pBuff, str.m_pBuff, str.length());
    m_nRealLen = str.length();
    m_nBuffSize = m_nRealLen + EXT_LEN;

    return *this;  //返回左運算元的引用
}

char String::operator[](int nPos)const
{

    return m_pBuff[nPos];  //使用了C裡面字串的下標訪問
}

char String::at(int nPos)const
{
    if (nPos >= m_nRealLen)  //如果給出的下標超出了字串的實際長度,就丟擲一個異常
    {
        //throw exception
    } 
    return m_pBuff[nPos];
}

int String::size()const
{
    return m_nBuffSize;  //返回字串的空間大小
}

int String::length()const
{
    return m_nRealLen;  //返回字串的字元大小
}

bool String::empty()const
{
    return !m_nRealLen;  //根據字串的實際長度返回是否為空
}

const String& String::operator+=(const String& str)
{
    if (m_nBuffSize - m_nRealLen >= str.length())  
//計算 空間大小減去字元大小 剩餘的分配了的但是沒有使用的空間 是否可以連線後面的字串,
//如果可以不用再次分配空間直接連線,就不用再分配空間了。
    {
        //strcat(m_pBuff, str.m_pBuff);
        memcpy(m_pBuff + m_nRealLen, str.m_pBuff, str.length());
        m_nRealLen = m_nRealLen + str.length();

    }   
    else  //需要重新分配空間情況
    {
        int nLen = m_nRealLen + str.length();
        char*p = new char[nLen + EXT_LEN];
        //strcpy(p, m_pBuff);
        memcpy(p, m_pBuff, m_nRealLen); //將前面的字串拷貝到新申請的空間中
        //strcpy(p + m_nRealLen, str.m_pBuff);
        memcpy(p + m_nRealLen, str.m_pBuff, str.length());  
        //因為新空間已經有了一個字串,我們不能再從首地址拷貝開始了,我們將首地址加上已經拷貝過去的字串的長度,
        //從這個位置開始後面沒有拷貝字串的位置拼接好第二個字串
        m_nRealLen = nLen;
        m_nBuffSize = nLen + EXT_LEN;

        if (m_pBuff)  //如果前面的字串不是空的,我們就刪除前面拼接之前的第一個字串
        {
            delete[]m_pBuff;
        }

        m_pBuff = p;  //將拼接好的字串賦值給物件指向字串首地址的指標
    }

    return *this;
}
istream& operator>>(istream& input, String& str)
{
    std::cin.get(str.m_pBuff, str.size(), '\n');  
    //不直接使用cin是怕cin的輸入沒有結束或者字串的長度導致越界,這個是c的一個讀入指定長度字串的函式,
    //該函式將str.size()長度的字串讀取到str.m_pBuff中,第三個字元是結束字元,即使沒有達到指定長度,遇到這個字元也會結束的。
    return input;
}
ostream& operator<<(ostream& output, String& str)
{
    for (int i = 0; i < str.length(); i++)  
    //因為cout對字串的輸出是以\0來結束輸出,我們使用的memcpy函式是不會在字串的結尾自動加入結束符號\0,
    //所以我們需要利用迴圈和它的實際長度來實現遍歷輸出
    {
        std::cout.put(str[i]);  //每次向螢幕輸出一個字元
    }
    return output;
}

main.cpp:測試String類的功能

#include<iostream>
#include"String.h"

int main()
{
    String str("Hello String!");  //一個引數的建構函式
    std::cout << str << std::endl;

    String str2(10, 'a');  //提供由一個字元組成的字串
    std::cout << str2 << std::endl;

    str2 = str;  //呼叫=運算子過載的函式
    std::cout << str2 << std::endl;

    String str3 = str2;   //呼叫拷貝建構函式
    std:cout << str3 << std::endl;

    std::cout << "size:" << str3.size() << ", lenght:" << str3.length() << std::endl;

    str3 += "abcdkdkd";   //拼接兩個字串,測試字串的連線
    std::cout << str3 << std::endl;
    std::cout << "size:" << str3.size() << ", lenght:" << str3.length() << std::endl;

    String str4(50, 'a');
    str3 += str4;
    std::cout << str3 << std::endl;
    std::cout << "size:" << str3.size() << ", lenght:" << str3.length() << std::endl;

    getchar();
    return 0;
}

演示結果

在這裡我們只是學習怎麼去建立一個類,實現這個類,運算子的過載和友元函式…進行了簡單的測試

原始碼下載地址:

GITHUB原始碼下載地址:

本文章由[諳憶]編寫, 所有權利保留。
歡迎轉載,分享是進步的源泉。