본문 바로가기

IT책/C++14 STL 철저 입문

1.6 스마트 포인터

스마트 포인터는 원시(일반)포인터처럼 주소를 담고있지만 두가지 차이점이있다 첫 번째는 스마트 포인터는 자유 공간에 할당된 메모리의 주소만 지정할 수 있고 원시 포인터에는 하던 증가,감소 같은 산술연산은 불가능하다.

 

자유 공간에 생성된 스마트 포인터는 객체에 할당된 메모리가 자동으로 해제되니 delete를 사용할지 말지 고민하지않아도 된다 다시말해 메모리 누수걱정이없다는것이다. 그리고 객체가 아닌 포인터를 저장하면 다형성을 유지할수있다.

 

  • unique_ptr은 말 그대로 유니크하다 하나만 존재할수있어며 복제또한 불가능하다 A라는 unique_ptr에서 B라는 unique_ptr의 이동은 가능하다 unique_ptr보다 유니크 make_unique로 생성하는것이 더욱 좋다
//unique_ptr
int _tmain() 
{
	unique_ptr<string>str(new string{ "나는 첫 번째 unique_ptr!" });

	cout << *str << endl;
}
//make_unique
int _tmain() 
{
	auto str = make_unique<string>("나는 make_unique로 만들어졌따!");
	cout << *str << endl;
}

 

int _tmain() 
{
	auto str = make_unique<string>(7,'*');
	cout << *str << endl;
}//출력값은 *******이다

원시 포인터와 마찬가지로 역참조가 가능하다.

그리고 배열을 가르키는 객체도 생성이 가능하다

//unique_ptr
int _tmain() 
{
	unique_ptr<int[]>arr(new int[3]);
}
//make_unique
int _tmain() 
{
	auto arr = make_unique<int[]>(3);
}

멤버함수 이용 예시

int _tmain()
{
	auto str = make_unique<string>("test");
	
	//기존에 가르키던 원본 문자열을 메모리할당을 해제하고 새 string객체 first_test를 새 주소에 할당 
	str.reset(new string("first_test"));
	
	str.reset();
	//string 객체 메모리 해제
}

그리고 멤버함수 reset에 저어어얼대로 자유 공간 객체의 주소를 넣지말아야한다. 

 

 

스마트 포인터에 release를 호출하면 unique_ptr<T>객체가 가르키는 객체를 해제할수있다 그리고 메모리를 해제하지않아도 nullptr로 설정할수있다

 

int _tmain()
{
	auto str = make_unique<string>("나 사라지는거야?");
    	str.release();//나 사라진다
}

그리고 원본 객체 메모리를 해제하지 않아도 소유권을 A에서 B로 양도가 가능하다

int _tmain()
{
	auto str = make_unique<string>("난 이동한다구~");
    unique_ptr<string>backup_str(str.release());
}

swap멤버함수를 사용하면 A와 B의 소유권을 서로 바꿀 수 있다

int _tmain()
{
	auto str1 = make_unique<string>("바꾼다!");
	auto str2 = make_unique<string>("너와 나를!");
	str1.swap(str2);
	cout << *str1 <<endl<< *str2 << endl;
}

nullptr인지 확인하고 reset()이나 release()사용하도록하자

 

#include<iostream>
#include<iterator>
#include<memory>
#include<tchar.h>
using namespace std;


int _tmain() 
{
	auto sub_iter = make_unique<string>("my my my");
	unique_ptr<string>iter(sub_iter.release());

	if (iter) 
	{
		cout << "NULL이 아니야!" << endl;
	}
	else 
	{
		cout << "NULL이야!" << endl;
	}

}

nullptr인지 확인하고 reset()이나 release()사용하도록하자

 

  • shared_ptr은 하나의 객체가 공유가 된다. A라는 객체에 여러개의 shared_ptr이 공유가능하며 공유갯수에 따라 count가 올라가고 count가 0이 되면 소멸한다.
int _tmain() 
{
	shared_ptr<string>str(new string{ "나는 첫 번째 shared_ptr" });
	cout << *str << endl;
}

auto로 지정한 타입이 initializer_list로 추론될 수 있으므로 타입을 auto로 지정할때 초기화 목록을 쓰면 안 된다

기억하자

shared_ptr<double>pdata1(new double(555.1));
	
	auto pdata2 = make_shared<double>();

	pdata2 = pdata1;
	cout << *pdata2 << endl;

이렇게 복사가 가능하며 복사하면 레퍼런스 카운트가 증가한다. 두 포인터가 모두 초기화되거나 소멸되어야 double변수에 할당된 메모리가 해제된다.

자유공간에 생성된 배열의 주소는 저장하지 못하지만 array<T>나 vector<T>컨테이너의 주소를 저장하는건 가능하다

 

나머지 예제는 위의 unique_ptr과 다 똑같다.

 

int _tmain()
{
	auto str = make_shared<string>("test string");
	cout << "나를 지금 가르키는 자들은 몇개인가 : " <<str.use_count()<< endl;
	auto sub_str1 = str;
	cout << "지금은 ? : " << sub_str1.use_count() << endl;

	str.reset();//str을 쳐리한다!

	if (sub_str1.unique())
		cout << "난 하나밖에없는 존재도다!" << endl;
	else
		cout << "나 말고 또있었어?" << endl;
}

 

  • weak_ptr은 shared_ptr 객체에서 생성되어 같은 주소를 가르키며 shared_ptr의 레퍼런스 카운터에는 아무런 영향을 끼치지않는다 그리고 shared_ptr이 소멸하여도 그대로 남아있는다. 이 스마트 참조를 쓰는 가장 큰 이유는 순환참조를 피하기 위해서이다. 그리고 shared_ptr객체가 소멸되면 그 객체와 관련된 weak_ptr은 유효한 객체를 가리키는것이 아니게 된다

 

int _tmain()
{
	auto shared= make_shared<string>("오예 나는냐 weak_ptr에 들어간다~~~");
	
	weak_ptr<string> pwdata(shared);
	weak_ptr<string> pwdata2(pwdata);

	shared_ptr<string>pnew(pwdata.lock());
	
}

expired멤버함수는 객체가 존재하는지 안하는지 확인하며 없으면 true를 반환한다

lock함수는 객체를 잠그고 shared_ptr객체를 반환한다

 

int _tmain()
{
	auto shared= make_shared<string>("우효~~");
	
	weak_ptr<string> pwdata(shared);
	weak_ptr<string> pwdata2(pwdata);

	shared_ptr<string>pnew(pwdata.lock());
	

	if (pwdata.expired())
		cout << "객체가 없다!" << endl;
	else
		cout << "있다!" << endl;

}

lock멤버함수는 잘 모르겠다.............(수정예정)

'IT책 > C++14 STL 철저 입문' 카테고리의 다른 글

1.8 함수를 인수로 전달하기  (0) 2019.06.27
1.7 알고리즘  (0) 2019.06.26
1.4~5 반복자  (0) 2019.06.25
1.3 컨테이너  (0) 2019.06.24