반응형

http://cmlife.blogspot.com/2007_02_01_archive.html


auto_ptr

책: C++ Standard Library 튜토리얼.레퍼런스
(니콜라이 M.조슈티스 저 / 문정환.김희준 공역 )
68p
auto_ptr 클래스

일반적으로 사용자는 포인터를 사용할 경우, 명시적으로 리소스를 관리해 주어야 한다.
void foo()
{
classA * ptr = new ClassA; // 생성..
... 작업수행..
delete ptr; // 파괴
}
이 함수에는 문제가 발생할 여지가 있다.
객체를 파괴하는 작업을 잊을수도 있고..
예외가 발생할 경우에는 delete문장이 호출되지 않고 곧바로 함수가 종료되어..메모리 릭이 발생할 수 있다.
그래서 예외처리를 위해서 try {... } catch() {....}로 처리해야 한다
만약 여러개의 객체일 경우 더욱 복잡해져 버리게 된다.
스마트 포인터는 이런 경우에 도움이 될수 있다. 스마트 포인터는 포인터 자체가 파괴될때 가리키고 있던 리소스를 항상 해제할 수 있다. 게다가 정상적이던 예외로이던 함수가 종료될때 포인터는 자동으로 파괴된다.

70p
auto_ptr은 자신이 가리키는 객체에 대한 소유자로서 행동하는 포인터이다. 결과적으로 auto_ptr이 파괴되면 소유하던 객체도 자동으로 파괴되는 것이다. auto_ptr의 요구사항은, 객체는 단 하나의 소유자만을 가질수 있다는 것이다.

#include // auto_ptr을 사용하기 위한 헤더
using namespace std;

void foo()
{
auto_ptr ptr( new ClassA );
......작업수행..
}

delete와 예외처리를 위한 문장이 더이상 필요하지 않다.
auto_ptr은 통상적인 포인터와 같은 인터페이스를 가지고 있다.
'*'연산자와, '->"연산자를 사용할수 있으며 클래스나 구조체일 경우 멤버에 대한 억세스를 제공한다.
그러나 ++/--같은 포인터 증감 연산은 사용불가하다..(Undefined)

auto_ptr<>클래스는 대입연산자로(=)로 초기화할 수 없다.
반드시 생성자를 사용하여 초기화를 해야 한다.

auto_ptr ptr1( new ClassA ); // OK!!
auto_ptr ptr2 = new ClassA; // ERROR !!!!!!!!!!!!!!

또는
auto_ptr ptr3; // auto_ptr이 생성된다.
ptr3 = auto_ptr(new ClassA); // 생성자를 이용해 명시적으로 생성한다.
ptr3이 가진 소유권이 파괴된후.. 새로 생성한 ( ...= auto_ptr(new ClassA) ) 것으로 소유권을 이전 받는다.

71p
auto_ptr에 의한 소유권 이전

auto_ptr ptr1( new ClassA );
auto_ptr ptr2( ptr1 );


이 경우에.. ptr1이 가지고 있던 소유권이 ptr2로 이전되고..
ptr1은 NULL 포인터를 가지게 된다...
그래서 ptr1은 이제 더이상 사용되서는 안된다.(이것은 프로그래머의 책임.)

auto_ptr ptr1( new ClassA );
auto_ptr ptr2;
ptr2 = ptr1;
이 경우에도 ptr1의 소유권이 ptr2로 넘어간다.


auto_ptr ptr1( new ClassA );
auto_ptr ptr2( new ClassA );
ptr2 = ptr1;

ptr1, ptr2모두 각각 다른 객체에 대한 소유권을 가지고 있을때는 어떻게 처리될까..
이때는 ptr2 = ptr1시에... ptr2가 가진 객체의 소유권이 파괴가 된다..
그런후 ptr1의 소유권을 이전받는 것이다.

위와 같은 소유권의 이전은 함수에서도 똑같이 적용된다.
auto_ptr foo()
{
auto_ptr ptr
....
return ptr;
}

void g()
{
auto_ptr p;

p = foo(); // 소유권을 이전 받는다.
}


auto_ptr을 인자로 사용할 경우 문제가 생긴다.

auto_ptr p( new int );

*p = 42; // *p에 값을 대입할수 있다. 정상.
bad_print( p ); // 소유권 이전이 일어난다.
// p가 가진 소유권이 bad_print()함수로 넘어가고 p는 NULL이 되어버렸다.
*p = 34; // 런타임 에러가 발생한다.

그럼 레퍼런스를 사용하면 되지 않을까 생각할수 있지만..
레퍼런스로 전달하면 함수는 소유권을 이전받았을 수도 있고 아닐 수도 있다.
auto_ptr을 레퍼런스타입으로 전달하는 것은 반드시 피해야 한다.

auto_ptr을 좀더 안전하게 만드는 방법으로 결정된 것은..const를 붙이는 것이다.
const auto_ptr p( new int );
*p = 42;
bad_print( p ); // 컴파일 에러.
*p = 34;
return p; // 컴파일 에러

const auto_ptr을 사용하면 의도하지 않은 소유권의 이전을 막을수 있다.
const는 auto_ptr객체의 값을 변경할수 없다는 뜻이 아니다...,
소유권을 이전할수 없다는 것을 의미한다.


(76p)
멤버 변수로의 auto_ptr
클래스 안에서 auto_ptr을 사용하게 되면 리소스 릭을 피할수 있다. 또한 파괴자가 필요없게 된다.
객체의 생성시 예외가 발생해도 리소스 릭을 피할수 있는 것이다.
한가지 주목해야 할 점은 파괴자는 생략하더라도, 복사 생성자와 대입연산자는 반드시 작성해야 한다.


(78p)
잘못된 사용례.

auto_ptr은 배열을 참조하는 것을 허락하지 않는다. 이것은 auto_ptr이 자신이 소유한 객체를 파괴하기 위해서 delete[]가 아닌 delete를 호출하기 때문이다. C++표준 라이브러리에서는 배열타입을 위한 auto_ptr은 없다. 대신 라이브러리는 데이터의 집합을 관리하기 위해서 컨테이너 클래스들을 제공한다.

auto_ptr은 표준 컨테이너 클래스와 함께 사용될 수 없다.
auto_ptr은 값의 복사가 아닌 소유권의 이전이 이루어지므로 그렇다.
다행히 라이브러리는 이러한 실수에 대해 컴파일에러를 제공한다. :)

(79p)
const auto_ptr 사용예..

#include #include 
template ostream & operator<<( ostream &os, const auto_ptr & p ){ if(p.get()==NULL) { os << "NULL"; } else { os << *p; } return os; } templatevoid Func( const auto_ptr & p ){ *p += 1;
cout << "Func(+1시킴) : " <<> p (new int(42)); auto_ptr q;

cout << "after initialization:" << endl; cout << "p : " << p << endl; cout << "q : " << q << endl;
Func(p);
q = p; cout << "\nafter assign:" << endl; cout << "p : " << p << endl; cout << "q : " << q << endl;
*q += 13; p = q; cout << "\nafter change:" << endl; cout << "p : " << p << endl; cout << "q : " << q << endl;
cin.get();
}

반응형

+ Recent posts