커스텀 String 클래스 구현
#include <iostream>
#include <string>
#include <cstring> // strlen, memset, memcpy를 위해 필요
namespace ya
{
class string
{
public:
// 🏗️ 생성자: C 스타일 문자열로부터 초기화
string(const char* str)
{
mSize = strlen(str); // 문자열 길이 계산
mCapacity = (mSize * 2) + (mSize / 2); // 여유 공간 2.5배 할당
mStr = new char[mCapacity]; // 동적 메모리 할당
memset(mStr, 0, mCapacity); // 메모리 0으로 초기화
memcpy(mStr, str, mSize + 1); // 문자열 복사 (\0 포함)
}
// 🗑️ 소멸자: 메모리 해제
~string()
{
delete[] mStr;
mStr = nullptr;
}
// ➕ += 연산자: 문자열 추가
void operator+= (const char* str)
{
int len = strlen(str); // 추가할 문자열 길이
int newSize = mSize + len; // 새로운 전체 크기
// 🚨 용량 부족 시 재할당
if (newSize >= mCapacity)
{
mCapacity = (newSize * 2) + (newSize / 2);
char* newStr = new char[mCapacity];
memset(newStr, 0, mCapacity);
memcpy(newStr, mStr, mSize); // 기존 데이터 복사
delete[] mStr; // 기존 메모리 해제
mStr = nullptr;
mStr = newStr; // 새 메모리로 교체
}
// 📝 문자열 추가 (기존 끝부분부터)
memcpy(mStr + mSize, str, len + 1); // \0까지 포함해서 복사
mSize = newSize; // 크기 업데이트
}
// 🎯 [] 연산자: 인덱스로 문자 접근
char& operator[] (int index)
{
return mStr[index]; // 참조 반환으로 읽기/쓰기 가능
}
// 📏 크기 반환 함수
int Size() const
{
return mSize;
}
// 🖨️ 출력을 위한 C 스타일 문자열 반환
const char* c_str() const
{
return mStr;
}
private:
char* mStr; // 실제 문자열 데이터 포인터
int mSize; // 현재 문자열 길이
int mCapacity; // 할당된 메모리 용량
};
}
int main()
{
// 🌟 std::string 사용 예시
std::string strA("Hello");
int len = strA.length();
strA += " World";
std::cout << "std::string: " << strA << std::endl;
std::cout << "4번째 문자: " << strA[4] << std::endl;
// 🎯 우리가 만든 ya::string 사용 예시
ya::string strB("Hello");
std::cout << "\nya::string 초기값: " << strB.c_str() << std::endl;
std::cout << "크기: " << strB.Size() << std::endl;
strB += " World~~~~~~~~~";
std::cout << "문자열 추가 후: " << strB.c_str() << std::endl;
std::cout << "크기: " << strB.Size() << std::endl;
strB[4] = 'a'; // 'o'를 'a'로 변경
std::cout << "4번째 문자 변경 후: " << strB.c_str() << std::endl;
std::cout << "4번째 문자: " << strB[4] << std::endl;
return 0;
}
C++
복사
학습 목표
지금까지 배운 객체지향 지식과 클래스를 활용하여 std::string 클래스를 비슷하게 설계해보기
•
실제 구현: 매우 복잡하지만 핵심 기능만 간단히 구현
•
주요 구현 요소: 생성자, += 연산자, [] 연산자
ya::string 클래스 구조
핵심 멤버 변수
private:
char* mStr; // 📝 실제 문자열 데이터를 저장하는 포인터
int mSize; // 📏 현재 문자열의 길이
int mCapacity; // 📦 할당된 메모리 용량 (여유 공간 포함)
C++
복사
mCapacity: [H][e][l][l][o][\0][ ][ ][ ][ ][ ][ ] (12칸 할당)
mSize: 5 ↑─────────────────↑ (실제 사용 5칸)
mStr: 포인터가 첫 번째 위치를 가리킴
Plain Text
복사
생성자 구현
string(const char* str)
{
mSize = strlen(str); // 🔢 문자열 길이 계산
mCapacity = (mSize * 2) + mSize; // 🏗️ 여유 공간 확보 (3배 할당)
mStr = new char[mCapacity]; // 🆕 동적 메모리 할당
memset(mStr, 0, mCapacity); // 🧹 메모리 초기화 (0으로)
memcpy(mStr, str, mSize + 1); // 📋 문자열 복사 (\0 포함)
}
C++
복사
ya::string strB("Hello"); // 호출 시
1. strlen("Hello") = 5 → mSize = 5
2. (5 * 2) + 5 = 15 → mCapacity = 15
3. new char[15] → 15칸 메모리 할당
4. memset으로 모두 0 → [0][0][0]...[0]
5. memcpy로 "Hello" 복사 → [H][e][l][l][o][\0][0]...[0]
C++
복사
+= 연산자 오버로딩
void operator+= (const char* str)
{
int len = strlen(str); // 🔢 추가할 문자열 길이
int newSize = mSize + len; // 📏 새로운 전체 크기
if (newSize >= mCapacity) // 🚨 용량 초과 체크
{
// 📦 용량 재할당
mCapacity = (newSize * 2) + newSize;
char* newStr = new char[mCapacity];
memset(newStr, 0, mCapacity);
memcpy(newStr, mStr, mSize); // 🔄 기존 데이터 복사
// 🗑️ 기존 메모리 해제
delete[] mStr;
mStr = nullptr;
mStr = newStr; // 🔗 새 메모리로 교체
}
// ➕ 문자열 추가 (기존 끝부분부터)
memcpy(mStr + mSize, str, len + 1);
mSize = newSize; // 📏 크기 업데이트
}
C++
복사
ya::string strB("Hello"); // mSize=5, mCapacity=15
strB += " World~~~~~~~~~"; // 13글자 추가 → 총 18글자
1. newSize = 5 + 13 = 18
2. 18 >= 15 → 용량 부족!
3. 새 용량 = (18 * 2) + 18 = 54
4. 새 메모리 할당 후 "Hello" 복사
5. 기존 메모리 해제
6. " World~~~~~~~~~" 추가
C++
복사
[] 연산자 오버로딩
char& operator[] (int index)
{
return mStr[index]; // 📍 참조 반환으로 읽기/쓰기 가능
}
C++
복사
strB[4] = 'a'; // 🖊️ 쓰기 가능
cout << strB[4]; // 📖 읽기 가능
C++
복사
Size() 함수
int Size() const // 🔒 const 함수 (값 변경 안함)
{
return mSize;
}
C++
복사
전체 구현 코드##
핵심 개념 정리
동적 메모리 관리
•
할당: new char[capacity]로 필요한 크기만큼 할당
•
해제: delete[] mStr로 메모리 누수 방지 (소멸자에서)
•
재할당: 용량 부족 시 더 큰 공간으로 확장
메모리 효율성
•
Capacity 전략: 현재 크기의 3배 할당으로 빈번한 재할당 방지
•
여유 공간: 문자열 추가 시 매번 재할당하지 않도록 버퍼 확보
연산자 오버로딩 활용
•
+= 연산자: 직관적인 문자열 추가 인터페이스
•
[] 연산자: 배열처럼 인덱스로 접근 가능
•
참조 반환: 읽기와 쓰기 모두 지원
숙제 안내
기본 숙제
// 3번 디버깅하며 따라서 치기 (마지막 1번은 안보고 구현)
// 그 다음 코드 보지 않고 main만 참고하여 직접 구현
C++
복사
표준 라이브러리 활용
•
동적할당 링크드리스트 문제 → std::list 사용
•
문자열 처리 문제들 → std::string 사용
고급 숙제 - std::vector 구현
#include <iostream>
#include <vector>
int main()
{
std::vector<int> vec;
vec.push_back(1); // 요소 추가
vec.push_back(2);
vec.push_back(3);
vec.push_back(4);
vec.push_back(5);
// 🔄 반복문으로 출력
for (size_t i = 0; i < vec.size(); i++)
{
std::cout << vec[i];
}
vec.clear(); // 모든 요소 제거
vec.resize(10); // 크기를 10으로 조정
// 📝 값 할당
for (size_t i = 0; i < vec.size(); i++)
{
vec[i] = i;
}
return 0;
}
C++
복사
•
템플릿: template<typename T> 사용
•
동적 배열: 크기 변경 가능한 배열 구조
•
주요 함수들:
◦
push_back(): 요소 추가
◦
size(): 현재 크기 반환
◦
clear(): 모든 요소 삭제
◦
resize(): 크기 변경
◦
operator[]: 인덱스 접근
학습 포인트
•
메모리 관리: 동적 할당과 해제의 실제 활용
•
연산자 오버로딩: 직관적인 클래스 인터페이스 설계
•
STL 이해: 표준 라이브러리가 어떻게 구현되는지 체험
•
객체지향 설계: 캡슐화와 정보 은닉의 실제 적용