C++Builder Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
C++빌더 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
메신저 프로젝트
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
볼랜드포럼 광고 모집

C++빌더 강좌/문서
C++Builder Programming Tutorial&Docments
[187] 객체 생성은 항상 성공할까?
김태선 [cppbuilder] 21140 읽음    2009-03-02 05:09
보통 대부분의 프로그래머들의 코드를 보면
객체 생성시 항상 성공할 것이라는 것을 전제로 하고 있습니다.
물론 생성자에서 성공적인 처리가 안된 경우 실패의 처리 코드를 인자를 통해 되돌려줄 수는 있지만
객체 생성 그 자체가 실패한다는 가정은 안하죠.

class T
{
    char    buf[10];
public:
    T(char *data, int& ret)
    {
        if (strlen(len > 10)) ret = 1;
        strcpy(buf, data);
        ret = 0;
    }
};
식으로 클래스를 만들었다면
{
    int  ret;
    T *t = new T("긴 스트링.....................", ret);
    if (ret)
        ; // 객체 생성자에서 에러 상황 발생 경우
}
식으로 생성자에서의 인자식으로 리턴값을 설정해 에러 상황를 검사할 수 있다는 뜻 입니다.
이렇게 되면 생성자에서 에러 상황이 발생하던지 말던지 간에
객체 변수 *t 는 메모리에 이미 할당된 클래스 T의 인스턴스를 가르키고 있습니다.


C++ 이나 델파이에서는
생성자 자체가 실패해서 객체 생성 자체가 되지 않을 수도 있습니다.
즉 객체 생성 자체가 실패하면
객체 변수 *t 는 값이 전혀 할당되지 않습니다. *t 객체 변수는 그냥 이전에 가지고 있던 그 값이 그대로 유지 됩니다.

그러면 생성자에서 클래스의 인스턴스 생성 실패는 어떻게 구현할까요?
생성자는 그 구조가 함수와 같은 방식의 단일한 리턴 값을 가지지 못하는 구조입니다.
그리고 메모리에 객체의 인스턴스를 생성할지 말지 여부는 컴파일러의 처리에 맡기게 되어 있으므로,
생성를 실패시키는 방법은 강제로 생성자가 끝까지 실행되지 않게 중간에 예외를 일으키는 방법을 쓰면 됩니다.
//---------------------------------------------------------------------------

class T
{
    char    buf[100];
public:
    T()
    {
        throw 1;
    }
};
테스트를 위해 이와 같이 강제로 생성자에서 예외를 일으킵니다.

void __fastcall TForm1::Button3Click(TObject *Sender)
{
    T *t = NULL;
    try
    {
        t = new T;
    }
    __finally
    {
        delete t;
    }
}
//---------------------------------------------------------------------------
그러면 결과적으로
t 는 NULL 값을 그대로 유지하게 됩니다.
그리고 delete t; 는 결국 0 번지를 delete 하게 되므로 delete 동작 자체가 취소되게 됩니다.

위와 같이 throw 나 Exception을 통한 예외 발생 뿐만 아니라,
메모리가 부족해서 객체를 할당할 수 없는 경우도 마찬가지의 경우가 생깁니다.

그러므로, 위와 같이 객체를 다루는 것이 가장 안전한 방법의 구조를 보여주는 것입니다.
상용 프로그램을 만든다면 이렇게 완전하게 객체 생성 실패의 경우까지 대비하는 것이 옳아 보입니다.
하지만 이는 코딩시 귀잖은 요소이고, 현실적으로 무시해도 좋다고 판단됩니다.
물론 경우에 따라서 try 블럭으로 반드시 객체 생성의 실패에 대비해야 하는 경우도 있으나,
대부분의 경우는 객체 생성 실패의 가능성이 지극히 적어 고려하지 않아도 그다지 지장이 없습니다.
특수한 경우에 대한 대비, 가령 대용량의 메모리 할당 등의 실패와 같은 경우는
객체 생성의 실패를  반드시 점검해야 합니다.
이는 new 로 인스턴스의 메모리를 할당 받아 쓰는 모든 경우에 동일한 사항입니다.



이러한 사항은 한번 주지할 필요가 있는 이유는,
생성자에서 만일 어떤 기능을 구현해야 하는데
그 기능 구현을 위해 호출하는 다른 클래스의 메소드나 객체 생성등에서
예외가 나서 자신이 구현한 클래스의 생성자가 완결되지 못하는 경우가 생기기 때문입니다.
자신의 코드만 이상 없으면 되는 일이 아니라는 것이죠.
그러므로
자신이 코딩한 생성자에서 다른 객체를 생성한다던지,
또는 자신이 분명하게 모르는 어떤 객체나 함수의 기능을 쓸때 예외가 일어날 가능성에 대해
try 블럭을 써서 예외를 잡아 내는 식으로 분명하게 대비해야 한다는 것입니다.
그렇지 않으면 자신이 코딩하지 않은 부분의 예외로 인해
자신이 코딩한 클래스의 인스턴스 자체가 메모리에 만들어지지 않는 상황이 발생할 수도 있기 때문입니다.


그닥 대단한 내용은 아니지만 한번 알아둘 필요가 있는 내용이었습니다.
빌더 쓰시는 분들은 오래 코딩하신 분들이 많아 대부분 아실 것군요.

그럼..
초보대왕 [sauron]   2009-03-02 10:19 X
-- C++ 이나 델파이에서는
-- 생성자 자체가 실패해서 객체 생성 자체가 되지 않을 수도 있습니다

...

-- 그러면 생성자에서 클래스의 인스턴스 생성 실패는 어떻게 구현할까요?
-- 생성자는 그 구조가 함수와 같은 방식의 단일한 리턴 값을 가지지 못하는 구조입니다.
-- 그리고 메모리에 객체의 인스턴스를 생성할지 말지 여부는 컴파일러의 처리에 맡기게 되어 있으므로,
-- 생성를 실패시키는 방법은 강제로 생성자가 끝까지 실행되지 않게 중간에 예외를 일으키는 방법을 쓰면 됩니다

C++ 과 델파이를 같이 거론하시는 거보니 new 로 객체를 만드는 것을 얘기하시는 것 같습니다.
C++ 의 경우 new 가 메모리 할당에 실패할 경우에 null 을 반환하는 것이 아니고,
예외를 던지는 것이 C++ 사양입니다. 그렇다면 C++ builder 에서는 new 로 객체를 못만들때,
예외를 던지지 않고 있나요? 문득 궁금해집니다.
김태선 [cppbuilder]   2009-03-02 15:55 X
객체 생성을 못할때는 NULL을 반환하는 것이 아니라고 본문에 표기를 하였는데....
아무래도 예를 보인 것은 미리 *t = NULL; 로 미리 초기화 해 놓았고 그 값이 생성자 예외 에러시 변경되지 않지 않는다고
설명한 것이,
설명이 알아듣기 쉽지 않게 되었나 보군요. ....
C++에서는 비단 new 객체 생성 실패시 뿐만 아니고 정적 생성시에도 생성자에서 예외를 일으킬만한
상황이 되면 객체에 인스턴스 자체가 할당되지 않거나 존재하지 않게 됩니다.
초보대왕 [sauron]   2009-03-02 16:44 X
-- 아무래도 예를 보인 것은 미리 *t = NULL; 로 미리 초기화 해 놓았고
-- 그 값이 생성자 예외 에러시 변경되지 않지 않는다고 설명한 것이,
-- 설명이 알아듣기 쉽지 않게 되었나 보군요. ....

핵심은 이것이었군요. 쪼끔 해맸읍니다.(^^)
저 같은 경우는 예외를 잘 사용하지 않으려고 합니다. 코드의 가독성이 좀 떨어지는 거 같아서요.
그래서 생성자에서 예외를 일으키는 코드는 거의 작성하지 않게 되는데,
builder 를 사용하시는 분들은 가끔씩 사용하시나 보네요.

좋은 팁이었읍니다. ~~
김태선 [cppbuilder]   2009-03-03 01:40 X
여담으로 하나 추가하면
빌더 personal 버전 같은 경우 DB 컴포넌트의 사용을 막고 있습니다.
설치는 되어도 사용은 할수 없게 되어 있습니다.
최종 EXE를 만들어 내도, 이 라이센스에서는 허용되지 않는다고 하는데
이는 대부분의 DB 컴포넌트의 생성자에서 라이센스 체크를 해서
빌더 personal 버전인 경우는 예외를 일으키게 해서 클래스의 인스턴스 자체가 생성되지 않도록 하는 방법을 사용하고 있습니다.
초보대왕 [sauron]   2009-03-03 06:42 X
-- 이는 대부분의 DB 컴포넌트의 생성자에서 라이센스 체크를 해서
-- 빌더 personal 버전인 경우는 예외를 일으키게 해서 클래스의 인스턴스 자체가
-- 생성되지 않도록 하는 방법을 사용하고 있습니다.

아, 라이센스 체크를 생성자에서 하는 방법도 있군요!
연거푸 좋은 팁 보고 갑니다.^^
슬픈사슴 [interest]   2009-03-11 20:52 X
예외는 편할때도 많지만 귀찮을때도 많죠.
C++는 java처럼 예외를 포함하는 Method를 호출할때는 반드시 예외핸들러를 명시해줘야 한다는 제약이 없기 때문에 오히려 더 귀찮아 집니다. 때문에 예를 들면 아마도 ACE같은 경우는 아예 예외를 전부 삼켜버리는 코드를 만들어 놨을겁니다.

개인적으로도 가급적 예외를 발생하는 코드는 발생가능성이 있는 예외는 삼켜 버리는데 가끔 쓰는 경우도 있습니다. 예를 들면 사용자예외를 만들어서 어디서 발생하든 한군데로 모아서 처리할 필요가 있거든요.

예를 들면 서울에서 출발하는 자동차를 운행하는데 이넘이 제주도로 갈지 경상도로 갈지, 울릉도로 갈지 모를 경우 어디서든 자동차에 이상이 생기면 예외를 발생시켜 무조건 서울 남대문 앞으로 떨어뜨려버리고 여기서 처리하죠. C의 longjump 라고나 해야 할까..

+ -

관련 글 리스트
187 객체 생성은 항상 성공할까? 김태선 21140 2009/03/02
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.