程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 一個簡單的string類Str

一個簡單的string類Str

編輯:C++入門知識

看到《accelerated c++》的一個string類的簡單實現,學到了許多操作符和類型轉換的東西,記錄下來,備忘。。。
Str.h

#include <iostream>
#include <algorithm>
#include <cstring>
#include "Vec.h"
 
class Str
{
    //友元函數有與成員函數一樣的訪問權限,而且可以放到類定義中的任意位置
    friend std::istream& operator>> (std::istream&,Str&);
public:
    typedef Vec<char>::size_type size_type;
 
    Str() { } //默認構造函數
    Str(size_type n,char c):data(n,c) {} //有連個參數(數目,字符)構造數目的字符的字符串
    Str(const char* p){ //從字符串常量轉化為string的構造函數
        std::copy(p,p+std::strlen(p),std::back_inserter(data));
        //std::cout<<"測試1"<<std::endl;
    }
    //從迭代器b和e之間的元素創建一個string
    template <class In> Str(In b,In e){
        std::copy(b,e,std::back_inserter(data));
    }
 
    //operator[]索引操作符
    char& operator[] (size_type i) { return data[i]; }
    const char& operator[] (size_type i) const { return data[i]; }
     
    //連接操作符 ①s=s+s1;②s+=s1
    Str& operator += (const Str& s)
    {
        std::copy(s.data.begin(),s.data.end(),std::back_inserter(data));
        return *this;
    }
    //定義大小
    size_type size() const { return data.size(); }
     
private:
    Vec<char> data;
};
 
//輸入-輸出操作符
//判斷一個函數時候應該是成員函數,我們前面章節有個標准:看看這個操作是否會影響對象的狀態
//輸入操作當然改變對象了,應該把它當做Str的成員函數嗎?
//不可以,原因:對於一個二元操作符(如cin>>s),左操作數總是與第一個參數綁定,但是對於
//成員操作符函數來說,第一個參數隱式的為此成員函數的對象,所以cin>>s表示的應該是
//cin中得成員函數>>,但是istream是沒有定義權限的。所以我們要想使cin>>s成立,就必須
//決定:輸入操作符必須是一個非成員函數,同理,輸入也是
std::istream& operator>> (std::istream&,Str&);
std::ostream& operator<< (std::ostream&,const Str&);
Str operator + (const Str& ,const Str& );//加法運算符的重載(不會改變任何值,所以類外定義)
Str.cpp

#include "Str.h"
using namespace std;
 
//我們調用Str::operator[]-->Vec::operator[];類似調用s.size()-->Vec對象的size
ostream& operator<< (ostream& os,const Str& s)
{
    for (Str::size_type i=0;i!=s.size();++i)
    {
        os<<s[i];
    }
    return os;
}
istream& operator>> (istream& is,Str& s)
{
    s.data.clear(); //清空數據www.2cto.com
    char c; //一個字符一個字符的讀取
    while (is.get(c)&&isspace(c))
    {//一直讀取字符,知道不是空字符,或者讀取操作結束
        ;
    }
    if (is) //判斷是否讀取結束
    {
        do
        {
            s.data.push_back(c);
        } while (is.get(c)&&!isspace(c));
        if (is) //判斷是否讀取結束
        {
            is.unget(); //此時多讀取了一個空白字符,要放回到輸入流中,防止下次出錯
        }
    }
    return is;
}
//加法
Str operator + (const Str& s,const Str& t )
{
    Str rhs=s;
    rhs+=t;
    return rhs;
}
 
另外,再說明兩點:
1. 二元操作符問題
+=操作符,改變了它的左操作數,所以應該定義為類Str的成員;
注意:如果一個類支持類型轉換,那麼把二元操作符定義為非成員函數是一個很好的習慣。
如果一個操作符是一個類的成員,那麼這個操作符的左操作數就不能事自動類型轉換的結果。
非成員操作符的左操作數以及任意操作符的右操作數,都遵循與普通函數參數相同的規則:操作數可以是任意類型,只要它能轉換層參數類型。
+操作符,應該為非成員函數,應為如果是成員函數,左右操作數就不對稱了(右操作數可以自動類型轉換,左操作數不可以)
+操作符的返回應該是左值,不應該是引用。(c++ primer解釋:算術運算符通常產生一個新值,該值是兩個操作數的計算結果,它不同於任一操作數,並且在一個局部變量的計算中返回對那個變量的引用是一個運行時錯誤。)
2. 類型轉換操作符(conversion operator)(從類到其他類型,與構造函數相反)
定義:這種操作符可以說明如何把這個類的對象轉換為目標類型。類型轉換操作符必須被定義為類的成員。格式:operator 目標類型,如 operator double
事實上,我們每次編寫隱式的檢測一個istream對象的值的循環式,都會使用這種類型轉換操作符。如,while(cin)
原理:標准庫定義了從istream到void*的類型轉換istream operator void*() 。void*可以轉化為bool型,所以上例成立。
 
延伸:為什麼istream不定義operator bool 直接把cin轉換為bool?
考慮 int x; cin<<x;(正確為cin>>x;)  此時,operator bool 直接把cin轉換為bool,生成的的bool值會轉換成int類型,然後把這個值左移x位,最後丟棄生成的結果。
注:通過定義轉換為void*的操作,而不是轉換為算術類型,標准庫既可以使一個istream對象能用作一個條件式,也可以阻止它被用作一個算術值。

 

摘自   csqlwy 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved