MyString이라는 우리만의 문자열 클래스를 만들어 보자.
우선 필요한 멤버 변수를 명시하자면,
1. 문자열 데이터가 저장된 공간을 가리키는 포인터
2. 문자열 데이터의 길이
가 된다.
이때 문자열은 개행문자를 포함하지 않는 것으로 저장하는 걸로 하자.
특이점: 생성자로 받으면 무조건 해당하는 문자열의 주소값을 그냥 복사하는 게 아니라,
deep copy 형식으로 복사한다.
// Example program
#include <iostream>
#include <string.h>
using namespace std;
class MyString{
char * string_content;
int string_length;
public:
MyString(char c);
MyString(const char* str);
MyString(const MyString& str);
~MyString();
void print() const;
void println() const;
};
MyString::MyString(char c){
string_content = new char[1];
string_content[0] = c;
string_length = 1;
}
MyString::MyString(const char* str){
string_length = strlen(str);
string_content = new char[string_length];
for (int i = 0; i != string_length; i++)
string_content[i] = str[i];
}
MyString::MyString(const MyString& str){
string_length = str.string_length;
for (int i = 0; i != string_length; i++){
string_content[i] = str.string_content[i];
}
}
MyString::~MyString(){
delete[] string_content;
}
void MyString::print() const{
for(int i=0; i<string_length; i++){
cout << string_content[i];
}
}
void MyString::println() const {
print();
cout << endl;
}
int main()
{
MyString str1("Hello World!");
MyString str2(str1);
str1.println();
str2.println();
}
이때, 기존과는 달리 string_length로 문자열의 길이를 상수 시간내에 확인할 수 있다.
int MyString::length() const { return string_length; }
내부 멤버 변수의 값을 바꾸지 않는다면 함수를 꼭 상수로 정의하자
endl은 개행문자 '\n'에다가 flush를 더한 것과 동일하다고 볼 수 있다.
즉, cout << endl;
은 cout << '\n' << flush
와 동일하다고 볼 수 있다.
여기서 flush란,
A buffer flush is the transfer of computer data from a temporary storage area to the computer’s permanent memory
으로, 버퍼에 있는 걸 비워내면서 동시에 그 내용물을 가져다가 쓰는 개념으로 이해하면 될 것 같다.
// Example program
#include <iostream>
#include <string.h>
using namespace std;
class MyString{
char * string_content;
int string_length;
public:
MyString(char c);
MyString(const char* str);
MyString(const MyString& str);
~MyString();
void print() const;
void println() const;
MyString& assign(const MyString& str);
MyString& assign(const char* str);
};
MyString::MyString(char c){
string_content = new char[1];
string_content[0] = c;
string_length = 1;
}
MyString::MyString(const char* str){
string_length = strlen(str);
string_content = new char[string_length];
for (int i = 0; i != string_length; i++)
string_content[i] = str[i];
}
MyString::MyString(const MyString& str){
string_length = str.string_length;
for (int i = 0; i != string_length; i++){
string_content[i] = str.string_content[i];
}
}
MyString::~MyString(){
delete[] string_content;
}
void MyString::print() const{
for(int i=0; i<string_length; i++){
cout << string_content[i];
}
}
void MyString::println() const {
print();
cout << endl;
}
// assign 함수 추가한 부분 1
MyString& MyString::assign(const MyString& str){
if (str.string_length > string_length){
delete[] string_content;
string_content = new char[str.string_length];
}
for (int i=0; i!=str.string_length; i++){
string_content[i] = str.string_content[i];
}
string_length = str.string_length;
return *this;
}
// assign 함수 추가한 부분 2
MyString& MyString::assign(const char* str){
int str_length = strlen(str);
if (str_length > string_length){
delete[] string_content;
string_content = new char[str_length];
}
for (int i=0; i!= str_length; i++){
string_content[i] = str[i];
}
string_length = str_length;
return *this;
}
int main()
{
MyString str1("Hello World!");
MyString str2(str1);
str1.println();
str2.println();
}
만약, 긴 문자열 할당
-1
-> 짧은 문자열 할당
-2
-> 긴 문자열 할당
때
2번에서 assign 함수는 문자열에 짧은 문자열을 위한 작은 크기에 공간만이 할당되어 있다고 생각해,
기존 메모리를 해제하고(사실 이미 충분히 많은 메모리를 가지고 있음) 다시 많은 양의 메모리를 할당하는,
매우 비효율적인 작업을 하게 됨.
비효율을 막기 위해서는 얼마나 맣은 공간이 할당되어 있는 지 알 수 있는 정보를 따로 보관하는 것이 좋음.
-> memory_capacity 추가하기
// Example program
#include <iostream>
#include <string.h>
using namespace std;
class MyString{
char * string_content;
int string_length;
int memory_capacity;
public:
MyString(char c);
MyString(const char* str);
MyString(const MyString& str);
~MyString();
void print() const;
void println() const;
MyString& assign(const MyString& str);
MyString& assign(const char* str);
int capacity() const;
void reserve(int size);
int length() const;
char at(int i) const;
};
MyString::MyString(char c){
string_content = new char[1];
string_content[0] = c;
string_length = 1;
}
MyString::MyString(const char* str){
string_length = strlen(str);
string_content = new char[string_length];
for (int i = 0; i != string_length; i++)
string_content[i] = str[i];
}
MyString::MyString(const MyString& str){
string_length = str.string_length;
for (int i = 0; i != string_length; i++){
string_content[i] = str.string_content[i];
}
}
MyString::~MyString(){
delete[] string_content;
}
void MyString::print() const{
for(int i=0; i<string_length; i++){
cout << string_content[i];
}
}
void MyString::println() const {
print();
cout << endl;
}
MyString& MyString::assign(const MyString& str){
if (str.string_length > memory_capacity){
delete[] string_content;
string_content = new char[str.string_length];
memory_capacity = str.string_length;
}
for (int i=0; i!=str.string_length; i++){
string_content[i] = str.string_content[i];
}
string_length = str.string_length;
return *this;
}
MyString& MyString::assign(const char* str){
int str_length = strlen(str);
if (str_length > string_length){
delete[] string_content;
string_content = new char[str_length];
memory_capacity = str_length;
}
for (int i=0; i!= str_length; i++){
string_content[i] = str[i];
}
string_length = str_length;
return *this;
}
void MyString::reserve(int size){
if (size > memory_capacity){
char* prev_string_content = string_content;
string_content = new char[size];
memory_capacity = size;
for(int i=0; i!=string_length; i++){
string_content[i] = prev_string_content[i];
}
delete[] prev_string_content;
}
}
char MyString::at(int i) const{
if (i>= string_length || i<0)
return NULL;
else
return string_content[i];
}
int MyString::length() const{
return string_length;
}
int MyString::capacity() const{
return memory_capacity;
}
int main()
{
MyString str1("very very very long string"); str1.reserve(30);
std::cout << "Capacity : " << str1.capacity() << std::endl;
std::cout << "String length : " << str1.length() << std::endl;
str1.println();
}
i의 위치 바로 앞에 삽입한다고 하자.
아래 코드 참고.
// Example program
#include <iostream>
#include <string.h>
using namespace std;
class MyString{
char * string_content;
int string_length;
int memory_capacity;
public:
// 생략
};
MyString& MyString::insert(int loc, const MyString& str){
if (loc < 0 || loc > string_length) return *this;
if (string_length + str.string_length > memory_capacity){
memory_capacity = string_length + str.string_length;
char * prev_string_content = string_content;
string_content = new char[memory_capacity];
int i;
for (i=0; i<loc; i++){
string_content[i] = prev_string_content[i];
}
for (int j = 0; j<str.string_length+loc; j++){
string_content[j] = str.string_content[j];
}
for (; i<string_length; i++){
string_content[str.string_length + i] = prev_string_content[i];
}
delete[] prev_string_content;
string_length = string_length + str.string_length;
return *this;
}
for (int i = string_length -1; i>=loc; i--){
string_content[i+str.string_length] = string_content[i];
}
for (int i = 0; i< str.string_length; i++){
string_content[i+ loc] = str.string_content[i];
}
string_length = string_length + str.string_length;
return *this;
}
MyString& MyString::insert(int loc, const char* str){
MyString temp(str);
return insert(loc, temp);
}
MyString& MyString::insert(int loc, char c){
MyString temp(c);
return insert(loc, temp);
}
int main()
{
MyString str1("very long string");
MyString str2("<some string inserted between>");
str1.reserve(50);
std::cout << "Capacity : " << str1.capacity() << std::endl;
std::cout << "String length : " << str1.length() << std::endl;
str1.println();
str1.insert(5, str2);
str1.println();
}
C++에서는 동적으로 할당되는 메모리를 처리하는데 매우 빈번하게 사용되는 기법은 다음과 같다.
생략, 비슷하게 진행한다.
// Example program
#include <iostream>
#include <string.h>
using namespace std;
class MyString{
char * string_content;
int string_length;
int memory_capacity;
public:
MyString(char c);
MyString(const char* str);
MyString(const MyString& str);
~MyString();
void print() const;
void println() const;
MyString& assign(const MyString& str);
MyString& assign(const char* str);
int capacity() const;
void reserve(int size);
int length() const;
char at(int i) const;
MyString& insert(int loc, const MyString& str);
MyString& insert(int loc, const char* str);
MyString& insert(int loc, char c);
int find(int find_from, const MyString& str) const;
int find(int find_from, const char* str) const;
int find(int find_from, char c) const;
};
MyString::MyString(char c){
string_content = new char[1];
string_content[0] = c;
string_length = 1;
}
MyString::MyString(const char* str){
string_length = strlen(str);
string_content = new char[string_length];
for (int i = 0; i != string_length; i++)
string_content[i] = str[i];
}
MyString::MyString(const MyString& str){
string_length = str.string_length;
for (int i = 0; i != string_length; i++){
string_content[i] = str.string_content[i];
}
}
MyString::~MyString(){
delete[] string_content;
}
void MyString::print() const{
for(int i=0; i<string_length; i++){
cout << string_content[i];
}
}
void MyString::println() const {
print();
cout << endl;
}
MyString& MyString::assign(const MyString& str){
if (str.string_length > memory_capacity){
delete[] string_content;
string_content = new char[str.string_length];
memory_capacity = str.string_length;
}
for (int i=0; i!=str.string_length; i++){
string_content[i] = str.string_content[i];
}
string_length = str.string_length;
return *this;
}
MyString& MyString::assign(const char* str){
int str_length = strlen(str);
if (str_length > string_length){
delete[] string_content;
string_content = new char[str_length];
memory_capacity = str_length;
}
for (int i=0; i!= str_length; i++){
string_content[i] = str[i];
}
string_length = str_length;
return *this;
}
void MyString::reserve(int size){
if (size > memory_capacity){
char* prev_string_content = string_content;
string_content = new char[size];
memory_capacity = size;
for(int i=0; i!=string_length; i++){
string_content[i] = prev_string_content[i];
}
delete[] prev_string_content;
}
}
char MyString::at(int i) const{
if (i>= string_length || i<0)
return NULL;
else
return string_content[i];
}
int MyString::length() const{
return string_length;
}
int MyString::capacity() const{
return memory_capacity;
}
MyString& MyString::insert(int loc, const MyString& str){
if (loc < 0 || loc > string_length) return *this;
if (string_length + str.string_length > memory_capacity){
memory_capacity = string_length + str.string_length;
char * prev_string_content = string_content;
string_content = new char[memory_capacity];
int i;
for (i=0; i<loc; i++){
string_content[i] = prev_string_content[i];
}
for (int j = 0; j<str.string_length+loc; j++){
string_content[j] = str.string_content[j];
}
for (; i<string_length; i++){
string_content[str.string_length + i] = prev_string_content[i];
}
delete[] prev_string_content;
string_length = string_length + str.string_length;
return *this;
}
for (int i = string_length -1; i>=loc; i--){
string_content[i+str.string_length] = string_content[i];
}
for (int i = 0; i< str.string_length; i++){
string_content[i+ loc] = str.string_content[i];
}
string_length = string_length + str.string_length;
return *this;
}
MyString& MyString::insert(int loc, const char* str){
MyString temp(str);
return insert(loc, temp);
}
MyString& MyString::insert(int loc, char c){
MyString temp(c);
return insert(loc, temp);
}
int MyString::find(int find_from, const MyString& str) const {
int i, j;
if (str.string_length == 0) return -1;
for (i = find_from; i <= string_length - str.string_length; i++) {
for (j = 0; j < str.string_length; j++) {
if (string_content[i + j] != str.string_content[j]) break;
}
if (j == str.string_length) return i;
}
return -1;
// 찾지 못했음
}
int MyString::find(int find_from, const char* str) const {
MyString temp(str);
return find(find_from, temp);
}
int MyString::find(int find_from, char c) const {
MyString temp(c);
return find(find_from, temp);
}
int main()
{
MyString str1("this is a very very long string");
std::cout << "Location of first <very> in the string : " << str1.find(0, "very")<< std::endl;
std::cout << "Location of second <very> in the string : " << str1.find(str1.find(0, "very") + 1, "very") << std::endl;
}
compare 역시 쉽게 구현할 수 있다. 생략.
여러 가지 검색 알고리즘들을 이용해 find 함수를 만들어보자.
어떤 알고리즘의 경우 미리 계산된 테이블이 필요한데, 이러한 정보들 역시 class 변수로 처리해도 된다.