이단계 생성 소프트웨어 개발에서 객체 생성 과정은 두 단계로 나뉘며
- 시스템 자원과 무관한 초기화 작업 예외 상황 발생 불가능한 연산 - 자원 할당 및 접근이 필요한 작업 메모리 분할, 파일 시스템 접근 등 예외 가능성 있는 연산
이단계 생성 예시 1 class ResourceSafeConstructor { private: ResourceSafeConstructor() // 첫 번째 생성 단계, C++ 기본 생성자
} bool initialize() // 두 번째 초기화 단계, 일반 멤버 함수 ....... return true; } public: static ResourceSafeConstructor* create(); // 객체 생성 메서드
}
참고: 첫 번째 생성자는 private 영역에 위치함으로써 외부에서 직접 인스턴스화 불가능하게 설계됨
이단계 생성 예시 2 ResourceSafeConstructor* ResourceSafeConstructor::create() { ResourceSafeConstructor* result = new ResourceSafeConstructor(); // 힙 공간에서 객체 생성, 첫 번째 생성자 호출
// 두 번째 초기화 실패 시 null 반환 if( !(result && result->initialize()) ){ delete result; result = nullptr; }
return result; }
이단계 생성 원리:
#include <stdio.h>
class ResourceSafeConstructor
{
private:
ResourceSafeConstructor() // 첫 번째 생성 단계
{
}
bool initialize() // 두 번째 초기화 단계
{
return true;
}
public:
static ResourceSafeConstructor* create(); // 객체 생성 메서드
};
ResourceSafeConstructor* ResourceSafeConstructor::create()
{
ResourceSafeConstructor* result = new ResourceSafeConstructor();
// 두 번째 초기화 실패 시 null 반환
if( !(result && result->initialize()) )
{
delete result;
result = nullptr;
}
return result;
}
int main()
{
//ResourceSafeConstructor obj; // 컴파일 오류, private 생성자로 인해 외부 호출 불가능 <br></br> ResourceSafeConstructor* obj = ResourceSafeConstructor::create();
printf("obj = %p\n", obj);
delete obj;
return 0;
}
이단계 생성의 핵심은 정상적인 객체 생성 성공 시 사용 가능하며, 실패 시 null을 반환하는 구조를 갖는다. 이 패턴은 불완전한 객체 생성을 방지한다.
동적 배열 클래스 재구현:
DynamicArray.h
#ifndef _DYNAMICARRAY_H_
#define _DYNAMICARRAY_H_
class DynamicArray
{
private:
size_t m_capacity;
int* m_data;
DynamicArray(size_t size);
bool allocate();
public:
static DynamicArray* create(size_t capacity);
size_t capacity();
bool read(size_t index, int& value);
bool write(size_t index ,int value);
~DynamicArray();
};
#endif
DynamicArray.cpp
#include "DynamicArray.h"
DynamicArray::DynamicArray(size_t size)
{
m_capacity = size;
}
bool DynamicArray::allocate()
{
bool success = true;
m_data = new int[m_capacity];
if( m_data )
{
for(size_t i=0; i<m_capacity; i++)
{
m_data[i] = 0;
}
}
else
{
success = false;
}
return success;
}
DynamicArray* DynamicArray::create(size_t capacity)
{
DynamicArray* result = new DynamicArray(capacity);
if( !(result && result->allocate()) )
{
delete result;
result = nullptr;
}
return result;
}
size_t DynamicArray::capacity()
{
return m_capacity;
}
bool DynamicArray::read(size_t index, int& value)
{
bool valid = (0 <= index) && (index < capacity());
if( valid )
{
value = m_data[index];
}
return valid;
}
bool DynamicArray::write(size_t index, int value)
{
bool valid = (0 <= index) && (index < capacity());
if( valid )
{
m_data[index] = value;
}
return valid;
}
DynamicArray::~DynamicArray()
{
delete[]m_data;
}
main.cpp
#include <stdio.h>
#include "DynamicArray.h"
int main()
{
DynamicArray* arr = DynamicArray::create(5);
printf("arr.capacity = %zu\n", arr->capacity());
arr->write(0, 1);
for(size_t i=0; i<arr->capacity(); i++)
{
int val = 0;
arr->read(i, val);
printf("arr[%zu] = %d\n", i, val);
}
delete arr;
return 0;
}
요약: 생성자는 객체의 초기 상태 결정만 담당 생성자 내부 초기화 실패도 객체 생성 여부와 무관 불완전한 객체 생성은 버그의 주요 원인 이단계 생성 패턴은 초기화 과정을 명확히 분리 이방법은 완전한 초기화된 객체만 생성 보장