※ 스마트 포인터 

// int *ptr=new int;    delete ptr;
std::unique_ptr<int> ptr = make_unique<int>();

//int *ptr=new int[5];
auto ptr=make_unique<int[]>(5);

//void (*ptr)()
std::function<void(void)>

C++ 프로그램에서 new 를 사용해 동적할당한 메모리는, 반드시 delete로  해제해야 한다.

 

C++에서는 메모리 누수(memory leak)로부터 프로그램의 안전성을 보장하기 위해 스마트 포인터를 제공 (memory 헤더).

 

스마트 포인터란 포인터처럼 동작하는 클래스 템플릿으로, 사용이 끝난 메모리를 자동으로 해제한다. (delete 자동 수행)

 

이는 new로 기본포인터(raw pointer)가 실제 메모리를 가리키도록 한 후

스마트 포인터 (smart pointer)에 기본포인터를 대입하여 사용한다.

이렇게 정의된 스마트 포인터의 수명이 다하면, 소멸자는 delete 키워드를 사용하여 할당된 메모리를 자동으로 해제한다.

 

 

※ 스마트 포인터의 종류

<C++11 이전>

auto_ptr (삭제됨)

 

<C++ 이후>

 

§ 참조 카운트 (reference count): 해당 메모리를 참조하는 포인터가 몇 개인지 나타내는 값

 

1. unique_ptr: 하나의 스마트포인터만이 객체를 가리킬 수 있도록 함

                        (reference cnt1을 넘길 수 없음)

2. shared_ptr: 하나의 특정 객체를 참조하는 스마트포인터의 개수를 참조

                        (reference cnt1씩 증가or감소, 참조횟수가 0이되면 delete되어 자동 해제)

3. weak_ptr: 하나 이상의 shared_ptr가 가리키는 객체를 참조 

                         (reference cnt늘리지 않음으로 shared_ptr 객체사이 순환참조를 제거하기 위함)

 

unique_ptr  (활용도: ★★★★★) 

사람이 실수할 수 있기에, unique와 move를 이용해 최대한 shared사용을 자제하는 것이 좋다.

unique_ptr 객체는 move() 멤버함수로 소유권을 이전할 수 있다. (단, 복사는 불가능!)

소유권이 이전되면 이전 unique_ptr 객체는 더이상 해당 객체를 소유하지 않게 재설정 된다.

 

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

int main() {
    unique_ptr<int> ptr1(new int(10));
    //unique Pointer 초기화와 함께 int형 값 10으로 동적할당

    auto ptr2 = move(ptr1);
    //auto 키워드로 ptr2는 ptr1의 타입을 추론(unique_ptr<int> 타입)
    //move 키워드는 ptr1에서 ptr2로 메모리의 소유권을 이전하기 위해 사용된다.

    unique_ptr<int> ptr3 = ptr1; // Err
    //애초에 ptr1이 소멸되어 접근이 불가하다.
    //대입 연산자를 이용한 복사는 오류를 발생시킨다.

    if (ptr1 == nullptr) {
        cout << "I'm Dead. Call ptr2 instead." << endl;
        cout << ptr1 << endl;   // *ptr1이라 써주지 않아서 Err
    }
    cout << *ptr2 << endl;

    ptr2.reset();
    ptr1.reset();
    //reset 함수로 메모리 영역을 삭제할 수 있다.
}

 

 

<make_unique()함수를 이용>

- 전달받은 인수를 사용해 지정된 타입의 객체를 생성

- 생성된 객체를 가리키는 unique_ptr을 반환하여 예외발생에 대해 안전히 대처가능

#include <iostream>
#include <memory>
#include <string>

using namespace std;

class Animal {
public:
    string name = "";
    Animal() { cout << "construct" << endl; };
    Animal(string name) {
        this->name = name;
        cout << "construct" << endl;
    }
    ~Animal() { cout << "destruct" << endl; };

    void setName() {
        cout << name << " : Grrr!" << endl;
    }
};

int main() {
    // Animal 객체 생성
    unique_ptr<Animal> a = make_unique<Animal>("Tiger");
    a->setName();
}

//------------출력------------//
//construct
//Tiger : Grrr!
//destruct

 

 

shared_ptr

shared_ptr은 하나의 특정 객체를 참조하는 스마트 포인터가 총 몇 개인지 를 참조하는 스마트 포인터

reference count는 특정 객체에 새로운 shared_ptr이 추가될 때마다 1씩 증가하고, 수명이 다하면 1씩 감소하며

추가되었던 shared_ptr이 해제되어 참조 카운트가 0이 되면 delete 가 자동으로 진행, 메모리를 자동해제한다.

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

int main() {
    shared_ptr<double> ptr1(new double(123.456));
    cout << ptr1.use_count() << endl;

    auto ptr2(ptr1);
    cout << ptr2.use_count() << endl;

    auto ptr3(ptr2);
    cout << ptr3.use_count() << endl;
}

//------------출력------------//
// 1
// 2
// 3

 

 

<make_shared()함수를 이용>

- shared_ptr의 객체를 안전하게 만들 수 있다.

- 전달받은 인수를 사용해 지정된 타입의 객체를 생성, 생성된 객체를 가리키는 shared_ptr을 반환.

- 이 함수도 예외 발생에 대해 안전하다.

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

class Monster {
public:
    Monster() { cout << "construct" << endl; }
    ~Monster() { cout << "destruct" << endl; }
};

int main()
{
    shared_ptr<Monster> mst_ptr1 = make_shared<Monster>();
    cout << mst_ptr1.use_count() << endl;

    auto mst_ptr2 = mst_ptr1;
    cout << mst_ptr1.use_count() << endl;

    mst_ptr2.reset();
    cout << mst_ptr1.use_count() << endl;
}

//------------출력------------//
// construct
// 1
// 2
// 1
// destruct

 

 

 

 

weak_ptr

shared_ptr은 참조 카운트를 기반으로 동작하는 스마트 포인터

 

하나 이상의 shared_ptr 객체가 소유하는 객체에 대한 접근을 제공하지만, 참조 카운트에 포함되지 않는다.

 

만약에 서로가 상대를 가르키는 shared_ptr을 가지고 있다면, 참조 횟수는 절대 1 이하로 내려가지 않기 때문에,

0이 되어야 자동으로 해제되는 스마트 포인터에 가장 크리티컬한 문제가 된다.

이렇게 서로가 상대를 참조하는 상황을 순환 참조(Circular Reference)라고 한다.

 

weak_ptr은 바로 이러한 shared_ptr 인스턴스 사이의 순환 참조를 제거하기 위해서 사용한다.

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

class Monster {
public:
    weak_ptr<Monster> otherMonster; 
    // shared_ptr로 선언할 경우 순환 참조 발생하기에 weak_ptr로 선언하여 순환 참조를 예방
    Monster() { cout << "생성" << endl; }
    ~Monster() { cout << "소멸" << endl; }
};

int main() {
    //철수와 민수에 대한 shared_ptr을 선언
    shared_ptr<Monster> chul_su = make_shared<Monster>();
    shared_ptr<Monster> min_su = make_shared<Monster>();

    // reference count : 참조 카운트
    cout << "철수 reference count : " << chul_su.use_count() << endl;
    cout << "철수 reference count : " << min_su.use_count() << endl;

    chul_su->otherMonster = min_su;
    min_su->otherMonster = chul_su;

    cout << "철수 reference count : " << chul_su.use_count() << endl;
    cout << "민수 reference count : " << min_su.use_count() << endl;
}

//------------출력------------//
// 생성
// 생성
// 철수 reference count : 1
// 철수 reference count : 1
// 철수 reference count : 1
// 민수 reference count : 1
// 소멸
// 소멸

 

 

 

 

 

 

 

※ 스마트 포인터 형변환(typecasting)

- static_pointer_cast

- dynamic_pointer_cast

- const_pointer_cast

 

포인터를 사용할 때, 다른 포인터 타입으로의 캐스팅은 용이했다.

하.지.만! shared_ptr 등의 스마트 포인터는 그렇지가 않다.

이를 위해서 ((AnotherClass*)(ptr.get())) 와 같이 강제로 포인터를 얻어 캐스팅을 해줄 수 있지만 전혀 C++ 답지 못하다.

 

따라서 static_pointer_cast  /  dynamic_pointer_cast  / const_pointer_cast 가 추가되었다.

이를 통해 안전하고도 편한 스마트 포인터 캐스팅이 가능해진 것이다.

vector<shared_ptr<MediaAsset>> assets;

assets.push_back(shared_ptr<Song>(new Song(L"Himesh Reshammiya", L"Tera Surroor")));
assets.push_back(shared_ptr<Song>(new Song(L"Penaz Masani", L"Tu Dil De De")));
assets.push_back(shared_ptr<Photo>(new Photo(L"2011-04-06", L"Redmond, WA", L"Soccer field at Microsoft.")));

vector<shared_ptr<MediaAsset>> photos;

copy_if(assets.begin(), assets.end(), back_inserter(photos), [] (shared_ptr<MediaAsset> p) -> bool {
    // Use dynamic_pointer_cast to test whether
    // element is a shared_ptr<Photo>.
    shared_ptr<Photo> temp = dynamic_pointer_cast<Photo>(p);
    return temp.get() != nullptr;
});

for (const auto&  p : photos) {
    // We know that the photos vector contains only
    // shared_ptr<Photo> objects, so use static_cast.
    wcout << "Photo location: " << (static_pointer_cast<Photo>(p))->location_ << endl;
}

 

 

 

 

코드출처) 

https://bbagwang.com/programming/cpp/%EC%8A%A4%EB%A7%88%ED%8A%B8-%ED%8F%AC%EC%9D%B8%ED%84%B0-smart-pointer/

 

https://ence2.github.io/2020/11/c-%EC%BA%90%EC%8A%A4%ED%8C%85-%EC%B4%9D%EC%A0%95%EB%A6%AC%EC%8A%A4%EB%A7%88%ED%8A%B8%ED%8F%AC%EC%9D%B8%ED%84%B0-%EC%BA%90%EC%8A%A4%ED%8C%85-%ED%8F%AC%ED%95%A8/

+ Recent posts