Giriş
Şu satırı dahil ederiz.#include <memory>
Lock YöntemiReference counting ile birden fazla göstergecin aynı anda tek bir nesneyi kullanması sağlanır.
Reference counting'in lock ile yapılıp yapılmadığı anlaşılabilir. Visual Studio 2012'de her zaman false döner.
#include <atomic>
#include <cstdio>
#include <memory>
int main() {
auto ptr = std::make_shared<int>(0);
bool is_lockless = std::atomic_is_lock_free(&ptr);
printf("shared_ptr is lockless: %d\n", is_lockless);
}
Eğer tek thread'li bir ortamda çalışıyorsak lock policy single olacağı için true döner.
template<typename T>
using shared_ptr_unsynchronized = std::__shared_ptr<T, __gnu_cxx::_S_single>;
Kalıtım
Bu sınıf Coercion by Member Template yöntemi ile kalıtım kullanan tiplerin birbirlerine atanabilmesini sağlar.
void Tip
Şu kod derlenir.
MetodlarBu sınıf Coercion by Member Template yöntemi ile kalıtım kullanan tiplerin birbirlerine atanabilmesini sağlar.
void Tip
Şu kod derlenir.
struct Thing{
~Thing(){
cout<<"Destroyed\n";
}
int data;
};
int main(){
{
shared_ptr<void> voidPtr;
{
shared_ptr<Thing> thingPtr = make_shared<Thing>();
voidPtr = thingPtr;
}
cout<<"thingPtr is dead\n";
}
cout<<"voidPtr is dead\n";
return 0;
}
Çıktı olarak şunu alırızthingPtr is dead
Destroyed
voidPtr is dead
Bu sınıfın tam 13 tane constructor metodu var.
Constructor metodu - raw pointer
Şöyle yaparız.
Constructor metodu - raw pointer
Şöyle yaparız.
shared_ptr<Type> var(new Type());
1. Constructor explicit olduğu için şu kod derlenmez. Yani raw pointer, shared_tipine çevrilemez.shared_ptr<Type> var = new Type();
Eğer bu işleme müsaade edilseydi şu tür kodlar hataya sebep olurdu.void foo(std::shared_ptr<int> bar) { /*do something, doesn't matter what*/ }
int main()
{
int * bar = new int(10);
foo(bar);
std::cout << *bar;
}
Bu da hataya sebep olurdu.void f(std::shared_ptr<int> arg);
int a;
f(&a); // bug
2. Aynı nesneye iki farklı pointer kullanmak yanlıştır.int *ip = new int;
shared_ptr<int> sp1(ip);
shared_ptr<int> sp2(ip);
Constructor - delete
shared_ptr, contructor içine verilen nesnenin tipini delete işlemi için kullanır. Örnekte template parametresi olarak void verilse bile kod doğru çalışır.
C++11
Sınıfı nesneleri delete ile siler. Eğer array kullanıyorsak delete[] şeklinde silmek isteriz bunun için std::default_delete metodunu kullanırız. std::default_delete içine tip + [] işareti alır
Örnek - int []
Şöyle yaparız.
shared_ptr<void> exampleVoidCons(new MyClass);
Constructor - default_delete - arrray içindirC++11
Sınıfı nesneleri delete ile siler. Eğer array kullanıyorsak delete[] şeklinde silmek isteriz bunun için std::default_delete metodunu kullanırız. std::default_delete içine tip + [] işareti alır
Örnek - int []
Şöyle yaparız.
std::shared_ptr<int> ptr(new int[5]{1,2,3,4}, std::default_delete<int[]>());
Örnek - char []
Şöyle yaparız
Şöyle yaparız.
Sınıfı array'leri de destekler. Şöyle yaparız.
Şöyle yaparız
std::shared_ptr<char> ptr(new char[size], std::default_delete<char[]>());
Örnek - unsigned char []Şöyle yaparız.
std::shared_ptr<unsigned char> ptr(
new unsigned char[10],
std::default_delete<unsigned char[]>()));
C++17Sınıfı array'leri de destekler. Şöyle yaparız.
shared_ptr<unsigned char[]> ptr (new unsigned char[N])
Constructor - custom deleter
Metodun imzası şöyle
Şöyle yaparız. Dosya functor ile otomatik kapatılır. Functor yerine fclose() metodu da geçilebilir. Sadece örnek olması için functor kullanıldı.
template< class Y, class Deleter, class Alloc >
std::shared_ptr( Y* ptr, Deleter d, Alloc alloc );
ÖrnekŞöyle yaparız. Dosya functor ile otomatik kapatılır. Functor yerine fclose() metodu da geçilebilir. Sadece örnek olması için functor kullanıldı.
struct FILE_deleter {
void operator() (FILE* fp) const {
fclose(fp);
}
};
void f()
{
std::shared_ptr<FILE> sp(fopen("some-file.txt"), FILE_deleter{});
// Now do whatever you want with sp: make copies of it, pass it around, whatever.
}
Örnek
Custom deleter içinde shared_ptr artık silindi sayılsa daha iyi. Şöyle yapmak doğru olmayabilir.
Metodun imzası şöyle
Örnek - aynı template parametresi
Şöyle yaparız. Eğer gerekiyorsa unique_ptr std::move() ile r-value haline getirilir.
Aliasing Constructor yazısına taşıdım.Custom deleter içinde shared_ptr artık silindi sayılsa daha iyi. Şöyle yapmak doğru olmayabilir.
struct Foo : public std::enable_shared_from_this<Foo>
{};
void deleter(Foo *f)
{
{
std::shared_ptr<Foo> tmp = f->shared_from_this(); // Line A
}
delete f;
}
int main()
{
std::shared_ptr<Foo> foo(new Foo, &deleter);
}
Constructor - unique_ptrMetodun imzası şöyle
template< class Y, class Deleter >
shared_ptr( std::unique_ptr<Y,Deleter>&& r );
unique_ptr'nin shared_ptr'ye dönüşebilmesini sağlar.Örnek - aynı template parametresi
Şöyle yaparız. Eğer gerekiyorsa unique_ptr std::move() ile r-value haline getirilir.
shared_ptr<int> getInt2() {
return make_unique<int>(2);
}
Örnek - farklı template parametresi
Açıklaması şöyle
Açıklaması şöyle
Şöyle yaparız.This constructor shall not participate in overload resolution unless unique_ptr<Y, D>::pointer is convertible to T*.
auto up = std::unique_ptr<int[]>(new int[5]{});
auto sp = std::shared_ptr<int>(std::move(up));
Ya da şöyle yaparız.auto sp = std::shared_ptr<int>(up.release(), up.get_deleter());
Contructor - aliasing
shared_ptr sınıfı sakladığı raw pointer'a get() metodu ile erişilebilmeyi sağlar. Örnek:
Eğer shared_ptr bir diziye işaret ediyorsa, dizideki belirtilen elemana erişmek için kullanılır.
operator-> ve operator*
Her iki operator de shared_ptr'nin sakladığı raw pointer null olsa bile exception atmazlar.
reset metodu
İmzası şöyle.
Şöyle yaparız.
İmzası şöyle.
Metodun imzası şöyle. İlginç bir şekilde long tipi dönüyor.
std::vector<int*> vector2;
vector2.push_back(std::make_shared<int>(4).get());
operator []Eğer shared_ptr bir diziye işaret ediyorsa, dizideki belirtilen elemana erişmek için kullanılır.
operator-> ve operator*
Her iki operator de shared_ptr'nin sakladığı raw pointer null olsa bile exception atmazlar.
reset metodu
İmzası şöyle.
void shared_ptr::reset() noexcept;
ÖrnekŞöyle yaparız.
shared_ptr<Type> var;
var.reset(new Type());
reset metodu - Yİmzası şöyle.
template <typename Y>
void shared_ptr::reset(Y* ptr);
use_count metoduMetodun imzası şöyle. İlginç bir şekilde long tipi dönüyor.
long use_count() const noexcept;
Şöyle yaparız.std::shared_ptr<int> s1 = std::make_shared<int>(0);
std::cout<<s1.use_count()<<std::endl;
Cast Metodları
std::static_pointer_cast
Aynı nesneye işaret eden iki shared_ptr oluşturur. static_cast ile aynıdır. Aşağıdaki örnekte MyClass static_cast ile void'e cast edilse bile sorun olmaz.
std::dynamic_pointer_cast
Aynı nesneye işaret eden iki shared_ptr oluşturur. dynamic_cast ile aynıdır. Yani cast işlemi başarısız ile null içeren bir shared_ptr döner. Base sınıfın en az 1 tane virtual metodunun olması gerekir.
Ben çoklu kalıtım varsa kullanılması taraftarıyım. Yoksa static_pointer_cast yeterli.
ObjectPool sınıfını şöyle yaparız.
Bu sınıf ilk başlarken havuzu doldurmuyor. İstek geldikçe havuz boş ise yeni bir tane nesne yaratıyor. Belki yeni bir constructor ile havuzun kaç nesne ile başlaması gerektiği belirtilebilir.
İlâveten bu sınıf thread safe değil. Belki başka bir sınıf ile sarmalanarak, thread safe yapılabilir. Bu yeni sınıfı ObjectPoolMT olarak adlandırabiliriz.
Aynı nesneye işaret eden iki shared_ptr oluşturur. static_cast ile aynıdır. Aşağıdaki örnekte MyClass static_cast ile void'e cast edilse bile sorun olmaz.
shared_ptr<MyClass> example(new MyClass);
shared_ptr<void> castToVoid = static_pointer_cast<void>(example);
std::dynamic_pointer_cast
Aynı nesneye işaret eden iki shared_ptr oluşturur. dynamic_cast ile aynıdır. Yani cast işlemi başarısız ile null içeren bir shared_ptr döner. Base sınıfın en az 1 tane virtual metodunun olması gerekir.
Ben çoklu kalıtım varsa kullanılması taraftarıyım. Yoksa static_pointer_cast yeterli.
std::shared_ptr<Base> base (new Derived());
std::shared_ptr<Derived> derived = std::dynamic_pointer_cast<Derived> (base);
Object PoolObjectPool sınıfını şöyle yaparız.
Bu sınıf ilk başlarken havuzu doldurmuyor. İstek geldikçe havuz boş ise yeni bir tane nesne yaratıyor. Belki yeni bir constructor ile havuzun kaç nesne ile başlaması gerektiği belirtilebilir.
İlâveten bu sınıf thread safe değil. Belki başka bir sınıf ile sarmalanarak, thread safe yapılabilir. Bu yeni sınıfı ObjectPoolMT olarak adlandırabiliriz.
class ObjPool<T>
{
private:
std::vector<T*> freeObjPool;
public:
void destroy(T *o)
{
freeObjPool.push_back(o);
}
std::shared_ptr<T> get()
{
T* o;
if (freeObjPool.empty())
o = new T();
else
{
o = freeObjPool.back();
freeObjPool.pop_back();
}
return std::shared_ptr<T>(o,
std::bind(&ObjRecycler::destroy, this, std::placeholders::_1)); }
}
Hiç yorum yok:
Yorum Gönder