最近因为某些原因,迫不得已封装了个String,实现了写时复制,自动释放等功能,贴出来分享下。
String.h
#ifndef STRING_H #define STRING_H class String { private: struct MetaStr { char* str; unsigned refCount; }; void addRefCount(); void decRefCount(); private: MetaStr* mstrobj; void create(const char*); void destroy(); void replace(const String&); void replace(const char*); public: String(); String(const char*); String(const String&); ~String(); String& operator=(const char*); String& operator=(const String&); void operator+=(const char*); void operator+=(const String&); String operator+(const char*); String operator+(const String&); bool operator>(const char*) const; bool operator>(const String&) const; bool operator>=(const char*) const; bool operator>=(const String&) const; bool operator<(const char*) const; bool operator<(const String&) const; bool operator<=(const char*) const; bool operator<=(const String&) const; bool operator==(const char*) const; bool operator==(const String&) const; operator const char*(); char charAt(int); size_t length() const; bool startsWith(const char*); bool startsWith(const String&); bool endsWith(const char*); bool endsWith(const String&); String subString(int); String subString(int, int); int indexOf(char); int indexOf(const char*); int indexOf(const String&); //int lastIndexOf(char); //int lastIndexOf(const char*); //int lastIndexOf(const String&); bool isEmpty(); String toLowerCase(); String toUpperCase(); const char* toCStr(); }; #endif //STRING_H
String.h
#include <string.h> #include <stdlib.h> #include "string.h" // Function of MetaStr void String::addRefCount() { ++mstrobj->refCount; } void String::decRefCount() { --mstrobj->refCount; } void String::create(const char* str) { this->mstrobj = (MetaStr*)malloc(sizeof(MetaStr)); mstrobj->refCount = 1; mstrobj->str = strdup(str); } void String::destroy() { if(mstrobj == NULL) return; if(mstrobj->refCount == 1) { if(mstrobj->str != NULL) free(mstrobj->str); free(mstrobj); } else { decRefCount(); this->mstrobj = NULL; } } void String::replace(const String& obj) { destroy(); this->mstrobj = obj.mstrobj; addRefCount(); } void String::replace(const char* str) { destroy(); create(str); } String::String(): mstrobj(NULL) {} String::String(const char* str) { create(str); } String::String(const String& obj): mstrobj(NULL) { replace(obj); } String::~String() { destroy(); } String& String::operator=(const char* str) { replace(str); } String& String::operator=(const String& obj) { replace(obj); } void String::operator+=(const char* str) { int len = length() + strlen(str) + 1; char* temp = (char*)malloc(len); *temp = '\0'; strcat(temp, mstrobj->str); strcat(temp, str); destroy(); this->mstrobj = (MetaStr*)malloc(sizeof(MetaStr)); mstrobj->str = temp; mstrobj->refCount = 1; } void String::operator+=(const String& obj) { int len = length() + obj.length() + 1; char* temp = (char*)malloc(len); *temp = '\0'; strcat(temp, mstrobj->str); strcat(temp, obj.mstrobj->str); destroy(); this->mstrobj = (MetaStr*)malloc(sizeof(MetaStr)); mstrobj->str = temp; mstrobj->refCount = 1; } String String::operator+(const char* str) { int len = length() + strlen(str) + 1; char* temp = (char*)malloc(len); *temp = '\0'; strcat(temp, mstrobj->str); strcat(temp, str); String res; res.mstrobj = (MetaStr*)malloc(sizeof(MetaStr)); res.mstrobj->str = temp; res.mstrobj->refCount = 1; return res; } String String::operator+(const String& obj) { int len = length() + obj.length() + 1; char* temp = (char*)malloc(len); *temp = '\0'; strcat(temp, mstrobj->str); strcat(temp, obj.mstrobj->str); String res; res.mstrobj = (MetaStr*)malloc(sizeof(MetaStr)); res.mstrobj->str = temp; res.mstrobj->refCount = 1; return res; } bool String::operator>(const char* str) const { return strcmp(mstrobj->str, str) > 0; } bool String::operator>(const String& obj) const { return strcmp(mstrobj->str, obj.mstrobj->str) > 0; } bool String::operator>=(const char* str) const { return strcmp(mstrobj->str, str) >= 0; } bool String::operator>=(const String& obj) const { return strcmp(mstrobj->str, obj.mstrobj->str) >= 0; } bool String::operator<(const char* str) const { return strcmp(mstrobj->str, str) < 0; } bool String::operator<(const String& obj) const { return strcmp(mstrobj->str, obj.mstrobj->str) < 0; } bool String::operator<=(const char* str) const { return strcmp(mstrobj->str, str) <= 0; } bool String::operator<=(const String& obj) const { return strcmp(mstrobj->str, obj.mstrobj->str) <= 0; } bool String::operator==(const char* str) const { return strcmp(mstrobj->str, str) == 0; } bool String::operator==(const String& obj) const { return strcmp(mstrobj->str, obj.mstrobj->str) == 0; } String::operator const char*() { return mstrobj->str; } String String::subString(int begin) { //TODO: add exception return mstrobj->str + begin; } String String::subString(int begin, int len) { //TODO: add exception String res; // need one byte to store the '\0' res.mstrobj = (MetaStr*)malloc(sizeof(MetaStr)); res.mstrobj->str = (char*)malloc(len + 1); res.mstrobj->refCount = 1; strncpy(res.mstrobj->str, mstrobj->str + begin, len); res.mstrobj->str[len] = '\0'; return res; } char String::charAt(int index) { //TODO: add exception return mstrobj->str[index]; } size_t String::length() const { return strlen(mstrobj->str); } bool String::startsWith(const char* str) { int len = strlen(str); if(len > length()) return false; if(strncmp(mstrobj->str, str, len) == 0) return true; else return false; } bool String::startsWith(const String& obj) { int len = obj.length(); if(len > length()) return false; if(strncmp(mstrobj->str, obj.mstrobj->str, len)== 0) return true; else return false; } bool String::endsWith(const char* str) { int len = strlen(str); int localLen = length(); if(len > localLen) return false; if(strncmp(mstrobj->str + localLen - len, str, len) == 0) return true; else return false; } bool String::endsWith(const String& obj) { int len = obj.length(); int localLen = length(); if(len > localLen) return false; if(strncmp(mstrobj->str + localLen - len, obj.mstrobj->str, len) == 0) return true; else return false; } bool String::isEmpty() { //TODO: add exception return length() == 0; } String String::toLowerCase() { int len = length(); String res; res.mstrobj = (MetaStr*)malloc(sizeof(MetaStr)); res.mstrobj->str = (char*)malloc(len + 1); res.mstrobj->refCount = 1; int offset = 'A' - 'a'; for(char* ps = mstrobj->str, *pd = res.mstrobj->str; *ps != '\0'; ++ps, ++ pd) { if(*ps >= 'A' && *ps <= 'Z') *pd = *ps - offset; else *pd = *ps; } res.mstrobj->str[len] = '\0'; return res; } String String::toUpperCase() { int len = length(); String res; res.mstrobj = (MetaStr*)malloc(sizeof(MetaStr)); res.mstrobj->str = (char*)malloc(len + 1); res.mstrobj->refCount = 1; int offset = 'A' - 'a'; for(char* ps = mstrobj->str, *pd = res.mstrobj->str; *ps != '\0'; ++ps, ++ pd) { if(*ps >= 'a' && *ps <= 'z') *pd = *ps + offset; else *pd = *ps; } res.mstrobj->str[len] = '\0'; return res; } const char* String::toCStr() { return mstrobj->str; } int String::indexOf(char ch) { char* p = strchr(mstrobj->str, ch); if(p != NULL) return p - mstrobj->str; else return -1; } int String::indexOf(const char* str) { char* p = strstr(mstrobj->str, str); if(p != NULL) return p - mstrobj->str; else return -1; } int String::indexOf(const String& obj) { char* p = strstr(mstrobj->str, obj.mstrobj->str); if(p != NULL) return p - mstrobj->str; else return -1; } //int String::lastIndexOf(char ch) { // char* p = strrchr(mstrobj->str, ch); // if(p != NULL) // return p - mstrobj->str; // else // return -1; //} //int String::lastIndexOf(const char* str) { // //} //int String::lastIndexOf(const String& obj) { // //}
单元测试
#include <gtest/gtest.h> #include "string.h" TEST(String, testConstructor) { String str1 = "test"; String str2 = str1; ASSERT_EQ(str1, str2); ASSERT_EQ(str1, "test"); } TEST(String, testAssign) { String str1 = "proto"; str1 = "codesun"; ASSERT_EQ(str1, "codesun"); String str2 = "proto"; str1 = str2; ASSERT_EQ(str1, "proto"); } TEST(String, testAppend) { String str1 = "code"; String str2; str2= str1 + "sun"; String str3 = "sun"; String str4 = str1 + str3; ASSERT_TRUE(str2=="codesun"); ASSERT_TRUE(str4=="codesun"); ASSERT_EQ(str2, str4); } TEST(String, testAppendAssign) { String str1 = "code"; String str2 = "sun"; str1 += str2; ASSERT_EQ(str1, "codesun"); str1 += "hello"; ASSERT_EQ(str1, "codesunhello"); } TEST(String, testBiggerThen) { String str1 = "code"; String str2 = "sun"; ASSERT_FALSE(str1 > str2); ASSERT_FALSE(str1 > "sun"); String str3 = "code"; ASSERT_FALSE(str1 > str3); ASSERT_FALSE(str1 > "code"); String str4 = "ab"; ASSERT_TRUE(str1 > str4); ASSERT_TRUE(str1 > "ab"); } TEST(String, testBiggerOrEqualThen) { String str1 = "code"; String str2 = "sun"; ASSERT_FALSE(str1 >= str2); ASSERT_FALSE(str1 >= "sun"); String str3 = "code"; ASSERT_TRUE(str1 >= str3); ASSERT_TRUE(str1 >= "code"); String str4 = "ab"; ASSERT_TRUE(str1 >= str4); ASSERT_TRUE(str1 >= "ab"); } TEST(String, testSmallerThen) { String str1 = "code"; String str2 = "sun"; ASSERT_TRUE(str1 < str2); ASSERT_TRUE(str1 < "sun"); String str3 = "code"; ASSERT_FALSE(str1 < str3); ASSERT_FALSE(str1 < "code"); String str4 = "ab"; ASSERT_FALSE(str1 < str4); ASSERT_FALSE(str1 < "ab"); } TEST(String, testSmallerOrEqualThen) { String str1 = "code"; String str2 = "sun"; ASSERT_TRUE(str1 <= str2); ASSERT_TRUE(str1 <= "sun"); String str3 = "code"; ASSERT_TRUE(str1 <= str3); ASSERT_TRUE(str1 <= "code"); String str4 = "ab"; ASSERT_FALSE(str1 <= str4); ASSERT_FALSE(str1 <= "ab"); } TEST(String, testEqual) { String str1 = "code"; String str2 = "sun"; String str3 = "code"; ASSERT_TRUE(str1 == str3); ASSERT_TRUE(str1 == "code"); ASSERT_FALSE(str1 == str2); ASSERT_FALSE(str1 == "sun"); } TEST(String, testConvert) { String str1 = "codesun"; ASSERT_STREQ(str1, "codesun"); ASSERT_STREQ(str1.operator const char*(), "codesun"); } TEST(String, testCharAt) { String str = "codesun"; ASSERT_EQ(str.charAt(0), 'c'); ASSERT_EQ(str.charAt(2), 'd'); ASSERT_EQ(str.charAt(4), 's'); ASSERT_EQ(str.charAt(6), 'n'); ASSERT_EQ(str.charAt(7), '\0'); } TEST(String, testLength) { String str = "codesun"; ASSERT_EQ(str.length(), 7); } TEST(String, testStartsWith) { String str = "codesun"; String str1 = "code"; String str2 = "sun"; String str3 = "codesun"; String str4 = "codesunh"; ASSERT_TRUE(str.startsWith(str1)); ASSERT_TRUE(str.startsWith("code")); ASSERT_TRUE(str.startsWith(str3)); ASSERT_TRUE(str.startsWith("codesun")); ASSERT_FALSE(str.startsWith(str2)); ASSERT_FALSE(str.startsWith("sun")); ASSERT_FALSE(str.startsWith(str4)); ASSERT_FALSE(str.startsWith("codesunh")); } TEST(String, testEndsWith) { String str = "codesun"; String str1 = "code"; String str2 = "sun"; String str3 = "codesun"; String str4 = "hcodesun"; ASSERT_TRUE(str.endsWith(str2)); ASSERT_TRUE(str.endsWith("sun")); ASSERT_TRUE(str.endsWith(str3)); ASSERT_TRUE(str.endsWith("codesun")); ASSERT_FALSE(str.endsWith(str1)); ASSERT_FALSE(str.endsWith("code")); ASSERT_FALSE(str.endsWith(str4)); ASSERT_FALSE(str.endsWith("hcodesun")); } TEST(String, testSubString) { String str = "codesun"; ASSERT_STREQ(str.subString(2), "desun"); ASSERT_STREQ(str.subString(4, 3), "sun"); } TEST(String, testIndexOf) { String str = "codesun"; ASSERT_EQ(str.indexOf('s'), 4); ASSERT_EQ(str.indexOf('x'), -1); ASSERT_EQ(str.indexOf("sun"), 4); ASSERT_EQ(str.indexOf("xun"), -1); String str1 = "sun"; String str2 = "xun"; ASSERT_EQ(str.indexOf(str1), 4); ASSERT_EQ(str.indexOf(str2), -1); } TEST(String, testIsEmpty) { String str = ""; ASSERT_TRUE(str.isEmpty()); str = "codeusn"; ASSERT_FALSE(str.isEmpty()); } TEST(String, testToLowerCase) { String str = "CoDe@SuN-HeLlO"; ASSERT_STREQ(str.toLowerCase(), "code@sun-hello"); } TEST(String, testToUpperCase) { String str = "CoDe@SuN-HeLlO"; ASSERT_STREQ(str.toUpperCase(), "CODE@SUN-HELLO"); } TEST(String, testToCStr) { String str1 = "codesun"; ASSERT_STREQ(str1.toCStr(), "codesun"); }