new의 실패 #

new 키워드의 메모리 할당은 operator new 함수에 의해 처리가 됩니다.

이 operator new 함수의 실패는 ISO C++표준 이전에는 NULL을 리턴하게 되었으나 ISO C++표준에서는 std::bad_alloc 객체를 예외로 발생 시키는 것으로 결정 되었습니다.

#include <stdexcept>
#include <iostream>
using namespace std;

int main()
{
        try {
                // 실패한다면 std::bad_alloc 예외 발생 
                int *p = new int[10000000];

                // 구식 컴파일러라면 이 조건에 .. 
                if (!p) {
                        cout << "bad alloc error : old version " << endl;
                        return -1;
                }
        }
       catch (std::bad_alloc &e) {
                cout << "bad alloc error " << endl;
        }
        catch (...) {
                cout << "unknown error " << endl;
        }
}
표준은 준수하는 컴파일러라면 exception(catch (std::bad_alloc &e))이 발생 할것이고 아니라면 if (!p) 조건에 해당되어 처리가 될것입니다.




예외없는 new #

위에서 언급했듯이 표준 이전의 스펙에서만 실패했을때 예외를 발생 시키지 않고 NULL을 리턴 하게 됩니다.

그러나 상황에 따라서는 실패시 NULL을 리턴하는 동작이 필요 할수도 있습니다.
혹은 예외의 비용이 부담되서, 혹은 예외가 지원되지 않아서 등등..

그래서 ISO C++ 표준에서는 예외를 발생시키지 않는 operator new 함수를 추가 했습니다.
void* operator new(std::size_t size, const std::nothrow_t&) throw();
void* operator new[](std::size_t size, const std::nothrow_t&) throw();
일반 operator new 함수에 2번째 인자 const std::nothrow_t& 가 추가가 된 형태 입니다.

#include <new>
#include <stdexcept>
#include <iostream>
using namespace std;

int main()
{
        try {
                int *p = new(nothrow) int[10000000];
                if (!p) {
                        cout << "bad alloc error : old version " << endl;
                        return -1;
                }
        }
        catch (std::bad_alloc &e) {
                cout << "bad alloc error " << endl;
        }
        catch (...) {
                cout << "unknown error " << endl;
        }
}
위의 코드에서는 예외를 발생 시키지 않고 실패시 NULL을 리턴하게 될것입니다.

'Dev > C++' 카테고리의 다른 글

상속되지 않는것  (0) 2008.05.01
STL  (0) 2008.05.01
new 1  (0) 2008.05.01
initialization - array structure  (0) 2008.05.01
operator overloading 2  (0) 2008.05.01

new #

new 키워드는 C++에서 힙영역에 메모리를 할당하기 위해 사용됩니다.

힙영역이란 프로그램(프로세스)이 메모리에 로딩이 될때 사용되는
메모리영역(정적영역, 스택, 코드영역) 을 제외하고 OS 차원에서
허용해주는 메모리 영역입니다.

그래서 힙영역은 프로그램차원에서 다룰수 있는 영역이 아니므로
OS에게 요청을해서 메모리를 받아와야 됩니다.

C언어의 malloc 함수와 C++의 new는 OS에게 그런 요청을 해서
메모리는 얻어오는 함수및 키워드 입니다.


new는 클래스 객체의 생성자를 호출해준다는 점에서
malloc 함수와는 차별화가 됩니다.

#include <iostream>
using namespace std;

class Item
{
public:
        Item() { cout << "ctor" << endl; }
        virtual ~Item() { cout << "dtor" << endl; }
};

int main()
{
        // Item 클래스의 메모리를 확보한후 
        // 생성자를 호출해 줍니다. 
        Item *p = new Item;
        Item *arr = new Item[10];

        // Item 클래스의 소멸자를 호출한후 
        // 메모리를 해제 합니다. 
        delete p;
        delete []arr; // new의 배열은 delete[] 로 해제합니다. 


        // Item 클래스의 메모리만 확보합니다. 
        // 생성자는 호출되지 않습니다. 
        Item *p2 = (Item *p)malloc(sizeof(Item));

        // Item 클래스의 메모리만 해제합니다. 
        // 소멸자는 호출되지 않습니다. 
        // 그것이 new로 생성된 객체일지라고 ... 
        free(p2);
}



operator new #


new의 기능은 크게 두가지로 나눕니다.

첫번째는 메모리를 항당하는 기능이고
두번째는 생성자를 호출하는 기능입니다.

이중 첫번째부분은 사용자에 의해 제어가 가능한 부분이지만
두번째 생성자를 호출하는 부분은 소멸자와 틀리게
컴파일러의 영역일뿐 사용자에 의해서 제어가 불가능한 부분입니다.

그럼 new의 메모리 할당은 어떻게 제어할까?
답은 operator new() 함수를 오버로딩 하는것입니다.


new의 메모리 할당은 내부적으로 컴파일러에 의해 수행 되는것이 아니고
메모리를 할당하는 operator new() 함수를 호출하여 메모리를 할당합니다.
그리고 그것에 대한 결과(할당된 메모리)에 해당 객체의 코드영역을 위치 시킨후
생성자를 호출합니다.

한마디로 컴파일러는 new의 동작에서 메모리 할당부분은
operator new 함수에게 위임 하게 되는것입니다.

그러므로 operator new를 오버로딩 한다면 메모리를 할당받는 과정을
사용자에 의해 변경 할수 있는 것입니다.


#include <iostream>
#include <string>
using namespace std;

class Custom
{
public:
        // 사용자정의 operator new 함수 
        void* operator new (size_t n)
        {
                return malloc(n);
        }

        // operator new를 정의 했다면 
        // 반드시 그에 상응하는 operator delete를 
        // 만들어 재앙을 막읍시다 
        void operator delete(void *p)
        {
                free(p);
        }

};

int main()
{
        Custom *p = new Custom();
        delete p;

}

위의 Custom 객체에 대한 new와 delete는
Custom::operator new() 와 Custom::operator delete()를
사용하게 됩니다.

placement new #


위에서 말했듯이 new의 생성자 호출은 사용자에 의해
제어가 불가능 합니다.
class Creator
{
public:
        Creator() {}
};

int main()
{
        Creator c;

        // 이걸 명시적으로 하고 싶은데 불행히도 
        // 이런것은 하지 못합니다. 
        // 에러 !! 
        c.Creator();        // 생성자호출 
}

그러나 또다른 new의 버전 placement new를 이용하면
비슷한 효과를 낼수도 있습니다.


placement new의 기본적인 내용은 메모리 할당은 직접 안하고
할당된 메모리에 객체를 생성 시키는 일만 합니다.

#include <iostream>
#include <string>
using namespace std;


int main()
{
        // 메모리 할당만 합니다. 
        // 생성자 호출은 안합니다. 
        string *pHeap = (string*)malloc(sizeof(string));
        // C++식 단순 메모리 할당 입니다. 
        // malloc과 같은 내용입니다.   
        // string *pHeap = (string*)operator new (sizeof(string)); 


        // 정적으로 메모리를 잡습니다. 
        // 당연히 스택에 메모리가 잡히겠죠   
        char pStack[sizeof(string)];

        // 메모리 생성은 안하고 
        // 생성된 메모리 pBuff에 string을 코드를 
        // 올려 놓고 생성자를 호출합니다. 
        new (pHeap) string("할당된메모리에 생성");

        // 스택에 string 객체를 생성합니다. 
        new (pStack) string("정적메모리에 생성");


        // 소멸자를 명시적으로 호출해 줍니다. 
        // 미리 할당된(정적이든 동적이든) 메모리에 
        // 객체만 생성하는것이기 때문에 
        // pHeap이 메모리가 해제 된다고 해도 string의 
        // 소멸자가 자동으로 호출되지 않습니다. 
        // 그러므로 소멸자를 호출 안하거나 
        // 두번이상 소멸자를 호출하는 재앙은 
        // 사용자의 책임입니다.   
        pHeap->~string();

        // 소멸자 호출   
        // pStack은 char타입의 배열이므로 
        // string* 형으로 명시적인 형변화 필요   
        reinterpret_cast<string *>(pStack)->~string();

        // pHeap의 메모리를 해제 합니다. 
        // operator new로 할당 했다면 
        // operator delete(pHeap) 
        free(pHeap);

        // pStack은 stack에 할당 되었으므로 
        // 해제가 필요없습니다. 
        // 이 함수가 끝나면 자동으로 스택에서 
        // 제거 됩니다.   

}

placement new는 메모리 할당의 제약이 있는 임베디드시스템이나
혹은 다른 프로그램 기법상 필요한
(예를들어 기본생성자가 없는 객체의 배열생성이나 메모리 풀링 등등)
일을 할때에 유용하게 사용할수 있을 것입니다.


참고: More Effective C++

'Dev > C++' 카테고리의 다른 글

STL  (0) 2008.05.01
new 2  (0) 2008.05.01
initialization - array structure  (0) 2008.05.01
operator overloading 2  (0) 2008.05.01
operator overloading 1  (0) 2008.05.01

+ Recent posts