シンプルなstring実装
最近は何らかの理由で、やむを得ずStringをパッケージ化し、書き込み時のコピー、自動解放などの機能を実現し、貼り付けて共有しています.
String.h
String.h
ユニットテスト
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");
}