C++ string类字符串的惯用操作及实现

C++ string类字符串的常用操作及实现
#include <stdlib.h>
#include <string.h>

namespace myspace
{
    class string {
    public:
        //ctor 声明一个C++字符串

        string(); 
        string(const char* s);
        string(const char* s, size_t len);
        string(const string& rhs);
        string(size_t len, char ch);
        string(const string rhs, size_t begin, size_t len);

        // 重载操作符,复制赋值、比较、增长删除子串
        string& operator=(const string& rhs);
        bool operator==(const string& rhs);
        bool operator>(const string& rhs);
        bool operator<(const string& rhs);
        bool operator!=(const string& rhs);
        string& operator+(const string& rhs);
        string& operator-(const string& rhs);

        // 删除子串,从begin开始的len长度被删除
        string& erase(size_t begin = 0, size_t len = npos);


        // 查找在字符串中第一个与str中的某个字符匹配的字符
        // 返回它的位置。搜索从index开始,如果没找到就返回string::npos 
        size_t find_first_of(const basic_string &str, size_t index = 0);
        size_t find_first_of(const char *str, size_t index = 0);
        size_t find_first_of(const char *str, size_t index, size_t num);
        size_t find_first_of(char ch, size_t index = 0);
        // 在字符串中查找第一个与str中的字符都不匹配的字符
        // 返回它的位置。搜索从index开始。如果没找到就返回string::nops 
        size_t find_first_not_of( const string &str, size_t index = 0 );
        size_t find_first_not_of( const char *str, size_t index = 0 );
        size_t find_first_not_of( const char *str, size_t index, size_t num );
        size_t find_first_not_of( char ch, size_t index = 0 );
        // 在字符串中查找最后一个与str中的某个字符匹配的字符
        // 返回它的位置。搜索从index开始。如果没找到就返回string::nops 
        size_t find_last_of( const string &str, size_t index = npos );
        size_t find_last_of( const char *str, size_t index = npos );
        size_t find_last_of( const char *str, size_t index, size_t num );
        size_t find_last_of( char ch, size_t index = npos );
        // 在字符串中查找最后一个与str中的字符都不匹配的字符
        // 返回它的位置。搜索从index开始。如果没找到就返回string::nops 
        size_t find_last_not_of( const string &str, size_t index = npos );
        size_t find_last_not_of( const char *str, size_t index = npos );
        size_t find_last_not_of( const char *str, size_t index, size_t num );
        size_t find_last_not_of( char ch, size_t index = npos );


        //dtor
        ~string();

        void copy( char *str, size_t num, size_t index );

        //get 
        size_t size() const;
        // from C++11, the data() is same with c_str()
        const char* data() const;
        const char* c_str() const;

        // set & get
        char* operator[](size_t index);

    private:
        char*  data_;
        size_t len_;
    };
}

常用的C++string 函数,我自己实现的:

<span style="font-size:18px;">
</span><span style="font-size:14px;">
namespace myspace
{
    // ctor 默认构造函数,生成长度为0的空字符串"\0"
    // 而不是生成'\0',有利于统一析构
    string::string() : len_(0)
    {
        data_ = new char [1];
        *data_ = '\0';
    }

    // 复制构造C类字符串,并以'\0'结束
    string::string( const char* s )
    {
        if(NULL == s)
        {
            len_ = 0;
            data_ = new char [1];
            *data_ = '\0';
        }
        else
        {
            len_ = strlen(s);
            data_ = new char [len_ + 1];
            strcpy( data_, s );
        }
    }

    // 以一定长度(字符串长度和指定长度的最小值)复制构造C类字符串
    // size_t会确保传入的长度len为非负数
    string::string( const char* s, size_t len )
    {
		if( NULL == s )
        {
            len_ = 0;
            data_ = new char [1];
            *data_ = '\0';
        }
        else
		{
			len_ = (strlen(s) < len) ? strlen(s) : len;
			data_ = new char [len_ + 1];
			strncpy( data_, s, len_ );
			*(data_ + len_) = '\0';
		}
    }

	// 复制构造同类型的string
    string::string( const string& rhs )
    {
        len_ = rhs.size();
        data_ = new char [len_ + 1];
        strcpy( data_, rhs.c_str() );        
    }

    // 以len为长度的ch的拷贝(即有len个ch组成的string)
    // 注意:当ch=‘\0’时长度为0
    string::string(size_t len, char ch)
    {
        len_ = (ch) ? len : 0;
        data_ = new char [len_ + 1];
        for(int i=0; i<len_; ++i)
        {
            *(data_ + i) = ch;
        }
        *(data_ + len_) = '\0';
    }

    // 以rhs的begin开始唱len的字符串初始化
    string::string(const string rhs, size_t begin, size_t len)
    {
        len_ = len;
        data_ = new char [len_ + 1];
        strncpy(data_, rhs.c_str() + begin, len);
        *(data_ + len_) = '\0';
    }


    // copy assign
    // 改进,同时考虑到“自我赋值”和“异常”的安全性
    string& string::operator=( const string& rhs )
    {

        char *pOrig = data_;
        char *copy = new char [rhs.size() + 1];
        strcpy( copy, rhs.c_str() ); 
        data_ = copy;
        len_ = rhs.size();
        delete [] pOrig;
        return *this;
        /*
        if( this != &rhs )
        {
            delete [] data_;
            len_ = rhs.len_;
            data_ = new char [len_ + 1];
            strcpy( data_, rhs.data_ );        
        }
        return *this;
        */
    }

    // 只写一个其他类似
    bool string::operator==(const string& rhs)
    {
        return !strcmp(this->c_str(), rhs.c_str());
    }

    // 只写一个其他类似
    string& string::operator+(const string& rhs)
    {
        size_t lenth = len_ + rhs.size() + 1;
        char *ptr = new char [lenth];
        strcpy(ptr, data_);
        strcpy(ptr + len_, rhs.c_str());
        delete [] data_;
        data_ = ptr;
        len_ = lenth;
<span style="white-space:pre">	return *this;</span>
    }

    // 删除子串
    string& string::erase(size_t begin, size_t len)
    {
        size_t lenth = len_ - len;
        char *ptr = new char [lenth];
        strncpy(ptr, data_, begin);
        strcpy(ptr + begin, data_ + begin + len);
        delete [] data_;
        data_ = ptr;
        len_ = lenth;
<span style="white-space:pre">	</span>return *this;
    }

    // dtor
    string::~string()
    {
        delete [] data_;
    }

    // 牺牲效率换取异常安全性
    void string::copy(char *str, size_t len, size_t index)
    {
        char *orig = data_;
        char *ptr = new char [len + 1];
        strncpy(ptr, str + index, len);
        *(ptr + len) = '\0';
        data_ = ptr;
        delete [] orig;
    }

    size_t string::size() const
    {
        return len_;
    }

    // from C++11, the data() is same with c_str()
    const char* string::data() const
    {
        return c_str();
    }

    const char* string::c_str() const
    {
        return data_;
    }

    char* string::operator[]( size_t index )
    {
        return ( index > len_ ) ? NULL : data_ + index;
    }
}</span>

经过测试,都OK~ 若有问题请提出以便修改;

<span style="font-size:14px;">#include <stdio.h>
#include <assert.h>

void test_string1()
{
	// test string()
	qh::string str;
	assert( '\0' == *str[0] );
	assert( 0 == str.size() );
	assert( '\0' == *str.c_str() );
	assert( '\0' == *str.data() );

	// test copy assign
	str = qh::string();
	assert( '\0' == *str[0] );
	assert( 0 == str.size() );
	assert( '\0' == *str.c_str() );
	assert( '\0' == *str.data() );

	// test string(cahr *) && copy assign
	str = qh::string( "jhsf" );
	assert( 'j' == *str[0] );
	assert( 4 == str.size() );
	assert( 'j' == *str.c_str() );
	assert( 'j' == *str.data() );

    printf( "%s() all test OK!\n", __FUNCTION__ );
}

void test_string2()
{
	// test string(string& s) s=""
	qh::string s;
	qh::string str_test( s );
	assert( '\0' == *str_test[0] );
	assert( 0 == str_test.size() );
	assert( '\0' == *str_test.c_str() );
	assert( '\0' == *str_test.data() );

	// test string(string&)
	const qh::string str_test2 = "abcdefgfgdgdfgfd";
	qh::string str(str_test2);
	assert( 16 == str.size() );
	assert( 'a' == *str.c_str() );
	assert( 'a' == *str.data() );
	assert( !strcmp(str.c_str(), str_test2.c_str()) );

	// test operator[]
	assert( 'd' == *str[3] );
	assert( 'f' == *str[7] );
	assert( NULL == str[29] );
	*str[7] = 'd';
	assert( 'd' == *str[7] );

	printf( "%s() all test OK!\n", __FUNCTION__ );
}


void test_string3()
{
	const char str_test[] = "";
	// test string(char* s) s=""
	qh::string str(str_test);
	assert( '\0' == *str[0] );
	assert( 0 == str.size() );
	assert( '\0' == *str.c_str() );
	assert( '\0' == *str.data() );

	// test string(char* s, size_t) s=""
	qh::string str2(str_test, 4);
	// test operator[]
	assert( '\0' == *str2[0] );
	assert( 0 == str2.size() );
	assert( '\0' == *str2.c_str() );
	assert( '\0' == *str2.data() );

	printf( "%s() all test OK!\n", __FUNCTION__ );
}

void test_string4()
{
	// test string(char*, size_t)
	char str_test[] = "^%$^%$**&%*(^^#%";
	qh::string str(str_test, 4);
	assert( '^' == *str.c_str() );
	assert( '^' == *str.data() );

	// test operator[]
	assert( '^' == *str[3] );
	assert( NULL == str[29] );
	assert( 4 == str.size() );
	*str[2] = '^';
	assert( '^' == *str[2] );

	// test self copy assign
	qh::string str2 = "1234645765";
	str2 = str2;
	assert(str2.c_str() != NULL);
	assert( '1' == *str2[0] );
	assert( 10 == str2.size() );
	assert( '1' == *str2.c_str() );
	assert( '1' == *str2.data() );

	printf("%s() all test OK!\n", __FUNCTION__);
}

int main(int argc, char* argv[])
{
    //TODO 在这里添加单元测试,越多越好,代码路径覆盖率越全越好
    //TODO 单元测试写法请参考INIParser那个项目,不要写一堆printf,要用assert进行断言判断
    test_string1();
    test_string2();
    test_string3();
    test_string4();

#ifdef WIN32
    system("pause");
#endif

	return 0;
}</span>

C++ string类字符串的惯用操作及实现