12. 클래스와 동적 메모리 대입
1. 클래스 멤버를 위한 동적 메모리 대입
클래스 멤버에 동적 메모리를 사용하는 경우, 필요에 따라 메모리를 동적으로 할당할 수 있으며, 이를 위해 new 연산자를 사용한다. 생성자에서 메모리를 동적으로 할당하고, 소멸자에서 반드시 delete를 사용해 메모리를 해제해야 한다. 메모리를 관리하지 않으면 메모리 누수 문제를 일으킬 수 있다. 이를 통해 객체가 생성될 때마다 메모리 사용량을 최적화할 수 있다 .
예:
class StringBad {
private:
char *str;
int len;
static int num_strings;
public:
StringBad(const char *s) {
len = std::strlen(s);
str = new char[len + 1];
std::strcpy(str, s);
num_strings++;
}
~StringBad() {
delete[] str;
num_strings--;
}
};
2. 암시적/명시적 오버로딩 대입 연산자
암시적 대입 연산자는 컴파일러가 자동으로 제공하며, 기본적으로 멤버별 대입을 수행한다. 하지만 포인터나 동적 메모리를 사용한 클래스의 경우 얕은 복사로 인한 메모리 충돌 문제가 발생할 수 있다. 이를 방지하기 위해 명시적으로 대입 연산자를 오버로딩하여 깊은 복사를 구현해야 한다 .
예:
StringBad& StringBad::operator=(const StringBad &st) {
if (this == &st) return *this; // 자기 자신 대입 방지
delete[] str;
len = st.len;
str = new char[len + 1];
std::strcpy(str, st.str);
return *this;
}
3. Static 클래스 멤버
Static 멤버는 모든 객체에서 공유되며, 프로그램이 종료될 때까지 메모리에 남아있다. 이를 통해 클래스의 모든 객체가 공유하는 데이터를 관리할 수 있다. Static 멤버 함수는 객체 없이도 호출할 수 있다.
예:
class StringBad {
private:
static int num_strings;
public:
static int HowMany() {
return num_strings;
}
};
4. 객체를 지시하는 포인터
동적 메모리를 사용하는 객체를 포인터로 관리할 때, 객체의 메서드에 접근하거나 메모리 해제 등의 작업을 수행할 수 있다. 객체를 지시하는 포인터를 통해 객체 간의 관계를 유연하게 관리할 수 있다. 메모리 해제 시 반드시 delete를 사용해야 한다 .
예:
StringBad *pStr = new StringBad("Hello");
std::cout << pStr->HowMany();
delete pStr;
5. 암시적/명시적 복사 생성자
복사 생성자는 객체가 다른 객체로 복사될 때 호출된다. 암시적 복사 생성자는 컴파일러가 자동으로 제공하며 얕은 복사를 수행하지만, 이는 포인터가 있는 경우 문제가 될 수 있다. 명시적 복사 생성자를 통해 깊은 복사를 구현하여 데이터를 안전하게 복사한다 .
예:
StringBad::StringBad(const StringBad &st) {
len = st.len;
str = new char[len + 1];
std::strcpy(str, st.str);
}
6. 생성자에 new 사용하기
생성자에서 new를 사용하여 동적 메모리를 할당하는 방법이다. 동적으로 할당된 메모리는 소멸자에서 반드시 delete를 사용하여 해제해야 한다 .
7. 객체에 위치 지정 new 사용하기
위치 지정 new는 기존 메모리 공간에 객체를 생성하는 방법으로, 주로 메모리 제약이 있을 때 유용하다. 하지만 객체를 해제할 때 파괴자를 명시적으로 호출해야 하며, 메모리 관리에 주의를 기울여야 한다 .
예:
char buffer[512];
JustTesting *p1 = new (buffer) JustTesting;
JustTesting *p2 = new JustTesting("Heap Object", 20);
p1->~JustTesting(); // 명시적으로 파괴자 호출
delete p2; // Heap에 할당된 객체는 delete로 해제
8. 큐 ADT 구현
큐는 선입선출(FIFO) 구조를 가지는 자료구조로, 큐의 앞부분에서 데이터를 삭제하고 뒷부분에서 추가하는 방식이다. 큐를 구현할 때 배열 또는 연결 리스트를 사용할 수 있으며, 큐가 비었는지 또는 가득 찼는지 검사하는 기능이 필요하다 .
예:
class Queue {
private:
int front, rear, size;
unsigned capacity;
int* array;
public:
Queue(unsigned capacity) {
this->capacity = capacity;
front = size = 0;
rear = capacity - 1;
array = new int[capacity];
}
~Queue() {
delete[] array;
}
void enqueue(int item) {
rear = (rear + 1) % capacity;
array[rear] = item;
size++;
}
int dequeue() {
int item = array[front];
front = (front + 1) % capacity;
size--;
return item;
}
};
'스터디 > C++ 스터디' 카테고리의 다른 글
C++ 챕터 13장 기초정리 (0) | 2024.09.28 |
---|---|
C++ 챕터 11장 기초정리 (0) | 2024.09.26 |
C++ 챕터 10장 기초정리 (0) | 2024.09.25 |
C++ 챕터 1~ 9장 기초 정리 (0) | 2024.09.24 |