반응형

[C++11] shared_ptr 정리 및 간단한 사용 예제

 

shared_ptr은  C++11부터 사용 가능한 smart pointer입니다.

 

std::shared_ptr 란

포인터를 통해 객체의 공유 소유권을 유지하는 스마트 포인터입니다. 힙 메모리에 객체를 위한 메모리와 참조 카운터를 위한 메모리가 할당됩니다. 여러 shared_ptr 객체가 동일한 객체를 소유할 수 있습니다. 다음 중 하나가 발생하면 개체가 소멸되고 메모리 할당이 해제됩니다.
- 객체를 소유하고 있는 마지막 남은 shared_ptr이 파괴됩니다.
- 객체를 소유하고 있는 마지막 남은 shared_ptr에는 operator= 또는 reset()을 통해 다른 포인터가 할당됩니다.
- 객체는 생성 중에 shared_ptr에 제공되는 delete-expression 또는 사용자 지정 삭제자를 사용하여 소멸됩니다.

 

std::shared_ptr 사용법

1. object를 가르키는 shared_ptr 생성자 호출할 수 있습니다. => std::shared_ptr<T> ptr(T *);

2.  새로운 shard_ptr 객체를 생성하기 위해 std::make_shard를 사용할 수 있습니다.
=> std::shared_ptr<T> ptr = std::make_shared<T>(); 

3. 동일한 포인터를 공유할 수 있습니다. 공유하는 변수가 늘어날수록 참조 카운터가 증가하며, use_count() 함수를 사용하여 참조 카운터를 확인할 수 있습니다. reset() 함수는 nullptr처럼 포인터를 해제하고 참조 카운터를 감소시킵니다.

=> std::shared_ptr<T> ptr1 = std::make_shared<T>(); 

=> std::shared_ptr<T> ptr2 = ptr2;

 

#include <iostream>
#include <memory>

class Student
{
public:
    Student() { std::cout << __FUNCTION__ << std::endl; }
    ~Student() { std::cout << __FUNCTION__ << std::endl; }

    std::shared_ptr<Student> getPtr()
    {
        return std::shared_ptr<Student>(this);
    }
};

int main()
{
    Student* p = new Student;

    // 1. p object를 가르키는 shared_ptr 생성자 호출합니다.
    std::shared_ptr<Student> ptr1(p); 
    std::cout << "ptr1.use_count(): " << ptr1.use_count() << std::endl;

    // 2. 새로운 shard_ptr 객체를 생성하기 위해 std::make_shard를 사용합니다.
    //  std::make_shard는 객체와 참조 카운터를 위한 메모리를 할당합니다.
    std::shared_ptr<Student> ptr2 = std::make_shared<Student>(); 
    std::cout << "ptr2.use_count(): " << ptr2.use_count() << std::endl;

    // 3. 동일한 포인터를 공유합니다. (ptr3과 ptr4)

    std::shared_ptr<int> ptr3(new int(5));
    std::cout << "ptr3.use_count(): " << ptr3.use_count() << std::endl;
    std::cout << "ptr3 value : " << *ptr3 << std::endl;

    std::shared_ptr<int> ptr4 = ptr3;
    std::cout << "ptr4.use_count(): " << ptr4.use_count() << std::endl;
    std::cout << "ptr4 value : " << *ptr4 << std::endl;

    std::cout << "ptr4 reset! " <<  std::endl;
    ptr4.reset();
    std::cout << "ptr3.use_count(): " << ptr3.use_count() << std::endl;

    return 0;
}

std::shared_ptr를 사용할 때 주의할 점

개체의 소유권은 다른 shared_ptr에 값을 할당하거나 복사 구성을 통해서만 다른 shared_ptr과 공유할 수 있습니다. 다른 shared_ptr이 소유한 원시 기본 포인터를 사용하여 새 shared_ptr을 구성하면 정의되지 않은 동작이 발생합니다.

 

아래 예제는 ptr2에 ptr1에서 객체의 주소를 return 받아 할당하도록 하였는데, 실행파일이 죽게됩니다.

#include <iostream>
#include <memory>

class Student
{
public:
    Student() { std::cout << __FUNCTION__ << std::endl; }
    ~Student() { std::cout << __FUNCTION__ << std::endl; }

    std::shared_ptr<Student> getPtr()
    {
        return std::shared_ptr<Student>(this);
    }
};

int main()
{
    Student* p = new Student;

    //p object를 가르키는 shared_ptr 생성자 호출합니다.
    std::shared_ptr<Student> ptr1(p); 
    std::cout << "ptr1.use_count(): " << ptr1.use_count() << std::endl;

    // ptr2에 ptr1에서 객체의 주소를 return 받아 할당하도록 해봅니다.
    std::shared_ptr<Student> ptr2 = ptr1->getPtr(); // 
    std::cout << "ptr2.use_count(): " << ptr2.use_count() << std::endl;

    return 0;
}

Student라는 클래스를 하나 동적선언하였고, 이 객체를 두 개의 shared_ptr ptr1, ptr2에 할당하였습니다.  ptr2를 생성하는 시점에 ptr1에 의해서 이미 Student 객체의 파괴자는 호출되어 객체가 소멸됩니다. 객체가 없는 상태에서 ptr2에서 다시 파괴자를 호출하는 구조이기 때문에 실행파일이 죽게됩니다. 

#include <iostream>
#include <memory>

class Student
{
public:
    Student() { std::cout << __FUNCTION__ << std::endl; }
    ~Student() { std::cout << __FUNCTION__ << std::endl; }

    std::shared_ptr<Student> getPtr()
    {
        return std::shared_ptr<Student>(this);
    }
};

int main()
{
    Student* p = new Student;

    //p object를 가르키는 shared_ptr 생성자 호출합니다.
    std::shared_ptr<Student> ptr1(p); 
    std::cout << "ptr1.use_count(): " << ptr1.use_count() << std::endl;

    //p object를 가르키는 shared_ptr 생성자 호출합니다.
    std::shared_ptr<Student> ptr2(p); 
    std::cout << "ptr2.use_count(): " << ptr2.use_count() << std::endl;

    return 0;
}

참조

https://en.cppreference.com/w/cpp/memory/shared_ptr

 

std::shared_ptr - cppreference.com

template< class T > class shared_ptr; (since C++11) std::shared_ptr is a smart pointer that retains shared ownership of an object through a pointer. Several shared_ptr objects may own the same object. The object is destroyed and its memory deallocated when

en.cppreference.com

https://en.cppreference.com/w/cpp/memory/unique_ptr

 

std::unique_ptr - cppreference.com

(1) (since C++11) template <     class T,     class Deleter > class unique_ptr ; (2) (since C++11) std::unique_ptr is a smart pointer that owns and manages another object through a pointer and disposes of that object when the unique_ptr goes out of sco

en.cppreference.com

 

반응형
반응형

[C++11] std::bind, std::placeholders 사용법

 

std::bind란?

함수 템플릿 바인드는 함수에 대한 전달 호출 wrapper를 생성합니다. 전달 인자를 설정할 수 있는 함수 포인터라고 보시면 됩니다. 
 

std::bind 사용 방법

template< class F, class... Args >
/*unspecified*/ std::bind( F&& f, Args&&... args );
 
f - 일부 인수에 바인딩되는 호출 가능한 객체입니다.(함수 객체, 함수에 대한 포인터, 함수에 대한 참조, 멤버 함수에 대한 포인터 또는 데이터 멤버에 대한 포인터)

args - 바인드할 전달인자 목록입니다. 바인딩되지 않은 인수는 namespace std::placeholders의 자리 표시자 _1, _2, _3...으로 대체될 수 있습니다. 
 

std::placeholder 이란?

보통 std::bind와 같이 많이 쓰이며, 함수의 인자를 받을 수 있도록 해줍니다. 
std::placeholders::_1, std::placeholders::_2, ..., std::placeholders::_N 형식으로 사용됩니다. 
 

std::bind, std::placeholder의 header 파일

#include <functional>
 

std::bind, std::placeholder의 사용 예제

#include <iostream>
#include <functional>


void add(int n1, int n2) {
    std::cout << "n1 = " << n1 << ", n2 = " <<  n2 << std::endl;
    std::cout << "add = " << n1 + n2 << std::endl;
}

void sub(int n1, int n2) {
    std::cout << "n1 = " << n1 << ", n2 = " << n2 << std::endl;
    std::cout << "sub = " << n1 - n2 << std::endl;
}

int main() {
    auto f1 = std::bind(add, 100, std::placeholders::_1);
    f1(5); // add(100, 5) 형식으로 호출됩니다.

    auto f2 = std::bind(sub, std::placeholders::_1, std::placeholders::_2);
    f2(10, 7); // sub(10, 7) 형식으로 호출됩니다.

    return 0;
}

 

 
 
 
 

참조

https://en.cppreference.com/w/cpp/utility/functional/bind

std::bind - cppreference.com

(1) template< class F, class... Args > /*unspecified*/ bind( F&& f, Args&&... args ); (since C++11) (until C++20) template< class F, class... Args > constexpr /*unspecified*/ bind( F&& f, Args&&... args ); (since C++20) (2) template< class R, class F, clas

en.cppreference.com

https://en.cppreference.com/w/cpp/utility/functional/placeholders

std::placeholders::_1, std::placeholders::_2, ..., std::placeholders::_N - cppreference.com

/*see below*/ _1; /*see below*/ _2; . . /*see below*/ _N; The std::placeholders namespace contains the placeholder objects [_1, ..., _N] where N is an implementation defined maximum number. When used as an argument in a std::bind expression, the placeholde

en.cppreference.com

 

반응형
반응형

[C++11] std::thread 사용법 (함수, 클래스)

 

std::thread란 

클래스 스레드는 단일 실행 스레드를 나타냅니다. 스레드를 사용하면 여러 기능을 동시에 실행할 수 있습니다. 

std::thread는 C++11에서 표준으로 되었습니다. 

 

 

std::thread Header 파일

#include <thread>

 

 

thread 생성 방법

thread 생성은 보통 아래 방법이 있습니다. 

lambda를 이용한 방법도 있는데, 나중에 한번 알아보도록 하겠습니다. 

1. 함수를 이용한 생성 => std::thread 변수(함수명, 전달인자)

2. Class 멤버 함수를 이용한 생성 => std::thread  변수(class명::thread 수행할 함수, class 생성자, 전달 인자);

3. Class Static 함수를 이용한 생성 => std::thread  변수(class명::thread 수행할 함수, 전달 인자);

 

 

 

thread 생성 예제

thread가 작업 중인데, main 함수에서 return 0; 으로 종료하려고 할 때, 비정상적으로 종료될 수 있습니다.

이를 방지하고자, thread의 join 함수를 사용하여, main thread는 자식 thread의 작업을 기다리고, thread의 작업이 끝나면, main thread는 실행을 재개합니다. 

thread를 실행시킬 때, 함수의 전달인자를 넣을 수도 있고, 넣지 않아도 됩니다.

 

1. 함수를 이용한 생성

1부터 1000까지 더하는 작업을 하는 thread를 만들어 보겠습니다. 

#include <iostream>
#include <string>
#include <thread>


void sum_thread()
{
    int sum = 0;
    for (int i = 1; i <= 1000; i++)
        sum += i;
    std::cout << "sum_thread() : " << sum << std::endl;
}

int main()
{
    std::thread sum(sum_thread);

    std::cout << "main() : start" << std::endl;
    sum.join();
    std::cout << "main() : finish" << std::endl;
}

위 코드를 실행해보면 아래와 같이 출력합니다. 

thread의 작업이 끝날때까지 기다리기위해 join 함수를 사용하였습니다. 

join 함수가 끝나고 main thread에서 실행을 재개합니다.

 

2. Class 멤버 함수를 이용한 생성 & 3. Class Static 함수를 이용한 생성

 

#include <iostream>
#include <string>
#include <thread>


class SUM {
public :
    static void sum_static(int num) {
        int sum = 0;
        for (int i = num; i <= 100; i++)
            sum += i;
        std::cout << "sum_static() : " << sum << std::endl;
    }
    void sum_func(int num) {
        int sum = 0;
        for (int i = num; i <= 1000; i++)
            sum += i;
        std::cout << "sum_func() : " << sum << std::endl;
    }
};

int main()
{

    // 2. Class의 멤버 함수를 사용한 Thread 생성
    std::thread thread1(&SUM::sum_func, SUM(), 10);


    // 3. Class의 Static 함수를 사용한 Thread 생성
    std::thread thread2(SUM::sum_static, 10);




    std::cout << "main() : start" << std::endl;
    thread1.join();
    thread2.join();
    std::cout << "main() : finish" << std::endl;
}

thread가 병렬로 작업을 수행해서 출력이 섞여서 출력이 됩니다. 

나중에는 이러한 것들을 순차적으로 수행할 수 있도록 해보도록 하겠습니다.

반응형

+ Recent posts