top of page

C++ Dilinde Smart Pointers - "std::unique_ptr"

  • Writer: Yusuf Hançar
    Yusuf Hançar
  • Sep 17, 2023
  • 11 min read

Updated: Sep 23, 2023

C++ dilinde değişkenlerin ömürleri üzerine çoğu yazımızda değinmiştik. Otomatik, static ve dinamik ömürlü nesnelerden, nerelerde kullanıldığından ve nasıl sentakslarının olduğundan bahsettik. Bu yazıda da dinamik ömürlü nesneler üzerine modern c++ ile eklenen smart pointer kullanımına değineceğiz.

İlk olarak programın çalışma zamanında boyutunu bilmediğimiz veriler için dinamik ömürlü nesne oluşturarak kontrol edebiliriz demiştik. Bu bellek ihtiyacına esnek olarak yaklaşım sağlayabilmek için ihtiyaç olduğunda yeni nesneler oluşturulup işimiz bitince serbest bırakabiliriz. Bu tanımlamalarla beraber dinamik ömürlü nesneler C++ dilinin sağladığı "new" ve "delete" operatörleri kullanılarak hayata getirilip, istenilen zamanda bellekten serbest bırakılmaktadır dedikten sonra da bazı senaryolarda dezavantajlarının olduğuna değindik. Bellek(memory) ve kaynak(resource) sızıntıları olarak ele alınan bu olumsuz senaryoların önüne geçebilmek adına smart pointerlar dile eklenmiştir. Daha sonra detaylı değineceğimiz "RAII(Resource Acquisition Is Initialization) prensibi" için smart pointers çözüm sunmaktadır.

Şimdi sırayla bu akıllı işaretçilere, özelliklerine, kullanım senaryolarına ve farklılıklarına detaylıca inerek örneklendirelim. Farklı kaynaklarda iki ve üç adet olarak değerlendirilen akıllı işaretçileri biz 2 ana alt başlıkta inceleyerek diğer pointer'ın neden bu şekilde ele alınmadığını da oturtmadan yazıyı tamamlamayacağız.

  1. std::unique_ptr

  2. std::shared_ptr

  • std::weak_ptr


memory başlık dosyasında yer alan bu işaretçiler unique_ptr ve shared_ptr olarak ele alınacaktır. Modern C++(C++11) öncesi bu akıllı işaretçiler yerine auto_ptr kullanılmaktaydı ve C++17 standartları ile dilden tamamen kaldırılmıştır. C++ diline modern standartlar ile eklenen taşıma semantiği, variadic template gibi araçlar yoktu ve dil desteği sınırlı idi.

Özetle dinamik ömürlü nesnelerin yaşamını kontrol eden yapıları akıllı işaretçiler olarak tanımlayabiliriz. Dinamik ömüre sahip bir nesne oluşturulduğunda heap bellek alanından o nesnenin sizeof miktarı kadar byte ayırılır.


class ResourceUser {
public :
    // ...
};

void smart_func()
{
    ResourceUser* ptr = new ResourceUser; // Kaynak edinilir

    try
    {
        // Bazı işlemler yapılıyor
        // İşlemler başarılı bir şekilde tamamlandıysa kaynak serbest bırakılacak
        
        delete ptr;
        ptr = nullptr;
    }
    catch (...) // Hata nesneleri yakalanıyor
    {
        if (nullptr != ptr)
        {
            delete ptr;     // Kaynaklar geri verilir
            ptr = nullptr;
        }

        throw; // Hata nesnesi yeniden gönderilir
    }
}
*****************************
AÇIKLAMA: yukarıdaki kodda bir pointer nesnesi kullanılarak bir kaynak edinilmiş ve hata yakalama yöntemi ile bazı kontroller yapılmıştır. Kaynak işi bittiğinde geri verilmelidir ki kaynak ve memory sızıntısı olmaması için delete işleminin doğru şekilde tamamlanması gerekmektedir. Ancak herhangi bir şekilde bu gerçekleşmez ise bu sızıntı kaçınılmaz olacaktır. Bu problemi çözmek için akıllı işaretçileri kullanacak ve örneklendireceğiz.
*****************************
CEVAP:
*****************************


İlk olarak "std::unique_ptr" akıllı işaretçimizi inceleyelim. Arka planda neler barındırıyor, bizim için neleri kolaylaştırıyor örneklendirerek pekiştirelim. unique_ptr memory başlık dosyasında bulunan bir akıllı işaretçidir. Bir pointer aracılığıyla başka bir nesnenin sahipliğini alarak onu kontrol eden ve scope(kapsam) dışına çıkıldığında o kaynağı serbest bırakan işaretçimizdir.


#include <memory>

template<class T, class Deleter = std::default_delete<T>> 
class unique_ptr;

template <class T, class Deleter> 
class unique_ptr<T[], Deleter>;
*****************************
AÇIKLAMA: Yukarıda da görüldüğü gibi unique_ptr sınıfının iki farklı specialization ile temsili bulunmaktadır. İki temsil arasındaki işledikleri veri türünün farklılığı gözlenmektedir.
*****************************
CEVAP:
*****************************
Specialization konusu template anlatılmaya başlandığında detaylı ele alınacaktır.!

unique_ptr<T, Deleter>;
std::unique_ptr<int> u_ptr(new int(23));
*****************************
AÇIKLAMA: tek bir integer nesne kontrol edilir.
*****************************

1. Buradaki class template T türündeki single nesnelerin yönetilmesi için kullanılmaktadır. unique_ptr tek bir nesne için özelleştirilmiştir diyebiliriz. Bellek tahsisi otomatik olarak kontrol edilir ve özelleştirerek(specialization) Deleter türü ile nesnenin serbest bırakılmasına olanak tanır.

Genellikle "std::default_delete<T" kullanılır ancak özelleştirmeye de olanak tanındığı belirtilir.

unique_ptr<T[], Deleter>;
std::unique_ptr<int[]> u_ptr(new int[10]);
*****************************
AÇIKLAMA: bir integer dizisi kontrol edilir.
*****************************

2. Buradaki class template T türündeki dizilerin yönetilmesi için kullanılmaktadır. unique_ptr birden fazla T türündeki nesneyi kontrol etmek için özelleştirilmiştir diyebiliriz. Bellek tahsisi otomatik olarak kontrol edilir ve özelleştirerek(specialization) Deleter türü ile dizinin serbest bırakılmasına olanak tanır.


"Buradaki Deleter farklı tür işlev veya işlev nesnesi olabilmektedir. "

1. Function Object

#include <memory>

class SmartDeleter {
public :
    void operator()(int* sptr) const 
    {
        delete sptr;
    }
};

std::unique_ptr<int, SmartDeleter> u_ptr(new int(23));
*****************************
AÇIKLAMA: 
*****************************
CEVAP:
*****************************

2. Fonksiyon Nesnesine L Value Reference

#include <memory>

class SmartDeleter {
public :
    void operator()(int* sptr) const 
    {
        delete sptr;
    }
};

SmartDeleter sc_deleter;
std::unique_ptr<int, SmartDeleter&> u_ptr(new int(23), sc_deleter);
*****************************
AÇIKLAMA: 
*****************************
CEVAP:
*****************************

3. Fonksiyona L Value Reference

#include <memory>

void smart_deleter(int* sptr)
{
    delete sptr;
}

std::unique_ptr<int, void(*)(int*)> u_ptr(new int(23), smart_deleter);
*****************************
AÇIKLAMA: void(*)(int*) türü smart_deleter fonksiyonuna l value reference olarak geçer.
*****************************
CEVAP:
*****************************

Örnek bir sınıf yazarak özelliklerini anlatmaya devam edelim..

#include <iostream>
#include <memory>

class SmartCode {
public:
    SmartCode(int val1, int val2) : a(val1), b(val2) {}

    void show_values() 
    {
        cout << "a : " << a << ", b : " << b << endl;
    }

private:
    int a, b;
};

void unique_ex(std::unique_ptr<SmartCode> u_ptr) 
{
    cout << "unique_ex function has a : ";
    u_ptr->show_values();
}

int main() 
{
    std::unique_ptr<SmartCode> u_ptr = std::make_unique<SmartCode>(5, 8);
    
    cout << "main function has a : ";
    u_ptr->show_values();
    
    unique_ex(std::move(u_ptr));
    
    // u_ptr artık geçerli değil, çünkü taşındı.
    // u_ptr->show_values(); // Bu satır hata verir.
    
    return 0;
}
*****************************
AÇIKLAMA: 
*****************************
CEVAP:
*****************************

Yukarıdaki fonksiyonda unique_ex fonksiyonunda parametre olarak sınıf türünden unique_ptr nesnesi alındığı ve bu parametreyi alırken geçici nesne std::move kullanarak taşınır. Dolayısıyla, fonksiyon içerisindeki u_ptr artık geçerli değildir. Fonksiyon içinde std::make_unique kullanılarak oluşturulup std::move ile taşınan nesneler "show_values" fonksiyonu ile tekrar yazdırılmaktadır.

Bu açıklamayı yapmamızın nedenlerinden biri std::unique_ptr sınıfının kopyalamaya kapalı taşımaya açık olmasıdır.

'unique_ex((u_ptr)' şeklinde yapsaydık çağrıyı delete edilen fonksiyona çağrı yapıldığı için hata alacaktık.


#include <iostream>
#include <memory>

class SmartCode {
public:
    SmartCode(int val1, int val2) : a(val1), b(val2) {}

    void show_values() 
    {
        cout << "a : " << a << ", b : " << b << endl;
    }

private:
    int a, b;
};

int main() 
{
    unique_ptr<SmartCode> u_ptr1{ new SmartCode{ 1, 3 } }; 
    unique_ptr<SmartCode> u_ptr2{ new SmartCode{ 5, 7 } };

    auto uptr_3 = u_ptr1;   // 1
    auto u_ptr3(u_ptr1);    // 2
    auto u_ptr3{ u_ptr1 };  // 3
    u_ptr1 = u_ptr2;        // 4
    
    return 0;
}
*****************************
AÇIKLAMA: yukarıdaki 4 çağrıda da kopyalayan kurucu işlevler delete edildiği için ve delete edilen fonksiyona çağrı olduğu için hepsi hatalıdır.
*****************************
CEVAP: syntax error
*****************************
std::make_unique

T türünde template içinde bir nesne oluşturarak std::unique_ptr içinde sarmalayan(wrapper) bir fonksiyon şablonudur(function template). std::make_unique ile bellek tahsisi ve yönetilmesi otomatik olarak yapılır. Bu sayede memory leak riski azalır. Çünkü herhangi bir exception throw edildiğinde veya fonksiyon sonlandığında bellek otomatik olarak serbest bırakılır. Ayrıca kodun daha okunabilir ve temiz yazılmasında önemli rol oynar.


std::unique_ptr ve sink ilişkisi

sink terimi modern C++ ile konuşulmaya başlanan ve taşıma semantiği ile ilişkili olan bir terimdir. Oluşturulan nesnenin sahipliğinin başka bir nesneye taşınmasını ifade eder. Dolayısıyla std::move kullanılarak taşınan nesnelerle ilişkilidir.

std::unique_ptr<int> source = std::make_unique<int>(23);

// source'un sahipliği target'a taşınıyor (sink işlemi)
std::unique_ptr<int> target = std::move(source);

// Artık source geçersizdir, bellek kaynağı target'ta.
std::unique_ptr ve "reset" fonksiyonu ilişkisi

reset fonksiyonu ile akıllı işaretçinin sahip olduğu kaynağı geri verir ve yeni kaynağa işaret edebilir. Yani nesnenin sahipliğini değiştirebilir.


  • Parametre verilmeden kullanılırsa hiçbir kaynağı işaret etmiyor anlamında kullanılır yani nullptr atamak ile aynı anlamdadır.

std::unique_ptr<int> u_ptr = std::make_unique<int>(23);
u_ptr.reset(); 
*****************************
AÇIKLAMA: u_ptr artık nullptr ile işaret edilir ve hiçbir kaynağı tutmaz.
*****************************
  • Parametre verilerek yeni bir kaynağa işaret etmesini sağlamak için kullanılabilir.

std::unique_ptr<int> u_ptr = std::make_unique<int>(23);
u_ptr.reset(new int(24); 
*****************************
AÇIKLAMA: u_ptr artık yeni int nesnesine işaret edecektir.
*****************************
  • Yukarıda da ifade ettiğimiz özel deleter oluşturmak için kullanılabilir.

#include <memory>

class SmartDeleter {
public :
    void operator()(int* sptr) const 
    {
        delete sptr;
    }
};

std::unique_ptr<int, SmartDeleter> u_ptr(new int(23));
u_ptr.reset(new int(24)); 
*****************************
AÇIKLAMA: u_ptr artık yeni int nesnesine işaret edecektir ve özel deleter kullanılır.
*****************************

std::unique_ptr ve "release" fonksiyonu ilişkisi

release fonksiyonu unique_ptr nesnesinin sahipliğini bırakır ancak delete etme işlemini yapmaz. Bu işlev doğrudan unique_ptr nesnesinin kontrol ettiği dinamik nesnenin adresini döndürmektedir.


#include <iostream>
#include <memory>

using namespace std;

class SmartCode {
public:
    SmartCode(int val1, int val2) : a(val1), b(val2) {}

    ~SmartCode() 
   {
       cout << "SmartCode dtor" << endl;
   }
    
    void show_values() 
    {
        cout << "a : " << a << ", b : " << b << endl;
    }

private:
    int a, b;
};

int main() 
{
    unique_ptr<SmartCode> u_ptr{ new SmartCode{ 1, 3 } }; 

    auto* ptr = u_ptr.release();  
    
    return 0;
}
*****************************
AÇIKLAMA: burada u_ptr nesnesinin sahipliğini release eder ancak destructor çağırılmaz.  Kaynak serbest bırakıldı yani hiçbir kaynağı göstermez. Genellikle unique_ptr nesnesinin sahip olduğu kaynağı başka bir akıllı işaretçiye devretmek ya da bu kaynağın programcı tarafından yönetilmesi istendiğinde release işlevi kullanılır.
*****************************
CEVAP: 
*****************************
int main() 
{
    unique_ptr<SmartCode> u_ptr{ new SmartCode{ 1, 3 } }; 

    auto* ptr = u_ptr.release();  

    delete ptr;
    
    return 0;
}
*****************************
AÇIKLAMA: bu şekilde ptr bizim tarafımızdan geri verildi ve dtor çağrısı gerçekleştirildi.
*****************************
CEVAP: 
*****************************
double deletion
  • Adından da anlaşılacağı üzere unique_ptr nesnesinin tek sahibi(owner) olması gerekir. Bunu yaparken ya devredilecek ya da kaynak geri verilecektir. Aynı kaynağa birden fazla unique_ptr nesnesi ile sahip olunması tanımsız davranışa neden olacaktır.

int main() 
{ 
    std::string* s_ptr = new std::string("smartcode"); 
    std::unique_ptr<std::string> up1(s_ptr); 
    std::unique_ptr<std::string> up2(s_ptr); 
}
*****************************
AÇIKLAMA: up1 ve up2 aynı aynı s_ptr nesnesini sahipleniyor. Güvenlik önlemi nedeniyle tasarlanan unique_ptr ilkesi gereği aynı kaynak iki defa delete edilecek ve bu durumda tanımsız davranışa neden olacaktır. (double deletion or double free)
*****************************
CEVAP: 
*****************************
class A { 
public: 
    A(int); 
};

class B { 
private: 
    A* ptr1;
    A* ptr2; 

public: 
    B(int val1, int val2) : ptr1(new A(val1)), ptr2(new A(val2)) {}

    B(const B& val) : ptr1(new A(*val.ptr1)), ptr2(new A(*val.ptr2)) {}

    ~B() 
    { 
        delete ptr1; 
	    delete ptr2; 
    } 
};

int main() 
{ 
    return 0;
}
*****************************
AÇIKLAMA: buradaki kurucu işlevlerde ikinci new exception throw ederse kaynak sızıntısı olacaktır.
*****************************
CEVAP: 
*****************************
class A { 
public: 
    A(int); 
};

class B { 
private: 
    std::unique_ptr<A> ptr1;
    std::unique_ptr<A> ptr2; 

public: 
    B(int val1, int val2) : ptr1(new A(val1)), ptr2(new A(val2)) {}
    B(const B& val) : ptr1(new A(*val.ptr1)), ptr2(new A(*val.ptr2)) {}
};

int main() 
{ 
    return 0;
}
*****************************
AÇIKLAMA: kaynak sızıntısı olmayacak ve otomatik olarak kaynaklar geri verilecektir.
*****************************
CEVAP: 
*****************************

std::unique_ptr ve "get" fonksiyonu ilişkisi
std::unique_ptr<T,Deleter>::get
  • unique_ptr nesnesinin sahipliğini aldığı dinamik bellek alanının başlangıç adresini(T*) döndüren bir işlevdir. Aynı zamanda bu kaynağa erişmek için de kullanılabilmektedir. Bu erişim de kaynak serbest bırakılmaz.!!!

#include <iostream>
#include <memory>

using namespace std;

class SmartCode {
public:
    SmartCode(int val1, int val2) : a(val1), b(val2) {}

    ~SmartCode() 
   {
       cout << "SmartCode dtor" << endl;
   }
    
    void show_values() 
    {
        cout << "a : " << a << ", b : " << b << endl;
    }
    
   friend ostream& operator<<(ostream& os, const SmartCode& val);

private:
    int a, b;
};

ostream& operator<<(ostream& os, const SmartCode& val)
{    
    return os << val.a << " " << val.b << endl;
}

int main ()  
{  
    auto u_ptr = make_unique<SmartCode> (10, 20);  
    cout << "u_ptr : " << (u_ptr ? "full" : "empty") << endl;  
    cout << *u_ptr << endl;  
    auto* ptr = u_ptr.get();
    
    u_ptr.reset();
    
    return 0;
}
*****************************
AÇIKLAMA: kaynak sızıntısı olmayacak ve otomatik olarak kaynaklar geri verilecektir.
*****************************
CEVAP: 
u_ptr : full
10 20
SmartCode dtor
*****************************
#include <iostream>
#include <memory>

using namespace std;

class SmartCode {
public:
    SmartCode(int val1, int val2) : a(val1), b(val2) {}

   ~SmartCode() 
   {
       cout << "SmartCode dtor" << endl;
   }
    
    void show_values() 
    {
        cout << "a : " << a << ", b : " << b << endl;
    }

private:
    int a, b;
};

int main () 
{ 
    auto up = make_unique<SmartCode> (10, 20); 

    SmartCode* ptr = up.get();

    delete ptr; 
}
*****************************
AÇIKLAMA: double deletion - undefined behaviour
*****************************
CEVAP: 
SmartCode dtor
SmartCode dtor
free(): double free detected in tcache 2
tcache : thread-local cache hatasıdır. thread spesifik alandır.
*****************************

get() işlevi aynı zamanda nesnenin nullptr değerinde olup olmadığını kontrol etmek içinde kullanılmaktadır.
#include <iostream>
#include <memory>
#include <string>

using namespace std;

template <typename T>
void check_uptr(const std::unique_ptr<T>& uptr) 
{
    if (nullptr != uptr.get()) 
    {
        std::cout << "uptr is not nullptr." << std::endl;
    } 
    else 
    {
        std::cout << "uptr is nullptr." << std::endl;
    }
}

int main() 
{
    std::unique_ptr<string> u_ptr;

    check_uptr(u_ptr);
    u_ptr = std::make_unique<string>("smartcode");
    check_uptr(u_ptr);

    return 0;
}
*****************************
AÇIKLAMA: 
*****************************
CEVAP: 
uptr is nullptr
uptr is not nullptr
*****************************

Yukarıda değindiğimiz üzere tek bir nesne ya da bir dizi nesnesinin kontrol edilebildiğini söylemiştik. Burada tasarımı yaparken dizi olduğunda [ ] operatörünü türde belirtmemiz gerekir. Aksi durumda tanımsız davranışa neden olacaktır.
#include <iostream>
#include <memory>

using namespace std;

class SmartCode {
public:
    SmartCode() : a(0), b(0) 
    {
        cout << "SmartCode ctor" << endl;
    } 
    
    SmartCode(int val1, int val2) : a(val1), b(val2) {}
    
    ~SmartCode() 
    {
        cout << "SmartCode dtor" << endl;
    }

    void show_values() 
    {
        cout << "a : " << a << ", b : " << b << endl;
    }

private:
    int a, b;
};

int main() 
{
    unique_ptr<SmartCode> uptr{ new SmartCode[3] };

    return 0;
}
*****************************
AÇIKLAMA: bu koddaki dinamik dizi nesnesi oluşturulurken akıllı işaretçinin türü tek bir nesneyi göstermektedir ve bellek kullanımında sorunlara neden olur. Yani bir tür tanımsız davranış olacak ve büyük problemlere yol açabilecektir.
*****************************
CEVAP: 
SmartCode ctor
SmartCode ctor
SmartCode ctor
SmartCode dtor
munmap_chunk():invalid pointer
*****************************
ÇÖZÜM 1 :
int main() 
{
    unique_ptr<SmartCode[]> uptr{ new SmartCode[3] };

    return 0;
}
*****************************
AÇIKLAMA: artık tek bir nesneyi değil dizi nesnenini kontrol ettik ve ilgili tüm bellek alanları serbest bırakıldı.
*****************************
CEVAP: 
SmartCode ctor
SmartCode ctor
SmartCode ctor
SmartCode dtor
SmartCode dtor
SmartCode dtor
*****************************
ÇÖZÜM 2 :
int main() 
{
    auto uptr = std::make_unique<SmartCode>(3);

    return 0;
}
*****************************
AÇIKLAMA: make_unique kullanarak çözüm
*****************************
CEVAP: 
SmartCode ctor
SmartCode ctor
SmartCode ctor
SmartCode dtor
SmartCode dtor
SmartCode dtor
*****************************

Boş(nullptr) bir unique_ptr nesnesini dereference(çözümleme) edersek tanımsız davranışa neden oluruz!
#include <iostream>
#include <memory>

using namespace std;

class SmartCode {
public:
    SmartCode() : a(0), b(0) 
    {
        cout << "SmartCode ctor" << endl;
    } 
    
    SmartCode(int val1, int val2) : a(val1), b(val2) {}
    
    ~SmartCode() 
    {
        cout << "SmartCode dtor" << endl;
    }

private:
    int a, b;
};


int main () 
{ 
    try  
    { 
        unique_ptr<SmartCode> u_ptr; 
        auto eptr = *u_ptr;                            
    } 
    catch (exception &ex) 
    { 
        cout << "exception caught : " << ex.what() << endl; 
    } 
}
*****************************
AÇIKLAMA: auto = *u_ptr işleminde smart pointer boş ve dereference etmeye çalıştığımız için tanımsız davranışa neden olacaktır.
*****************************
CEVAP: 
*****************************

std::unique_ptr ve "swap" fonksiyonu ilişkisi
  • swap fonksiyonu iki unique_ptr akıllı işaretçisinin sahip oldukları kaynakları yani bellek bloğunu değiştirmek için kullanılır.

  • Bu takas işlemi gerçekleştirilirken verilerin kopyalanmasının önüne geçer. Büyük boyutlu verilerde bu olukça maliyetli bir işlem olacaktır. Kaynak kopyalamak yerine kaynaklarının sahipliği transfer edilerek kontrol edilmektedir.

template <typename T> 
void Swap_Temp(T& a, T& b) 
{ 
    T temp = std::move(a); 
    a = std::move(b); 
    b = std::move(temp); 
}
#include <iostream>
#include <memory>

int main() 
{
    std::unique_ptr<int> uptr1 = std::make_unique<int>(23);
    auto uptr2 = std::make_unique<int>(24);

    std::cout << "uptr1: " << *uptr1 << ", uptr2: " << *uptr2 << std::endl;

    uptr1.swap(uptr2);

    std::cout << "uptr1: " << *uptr1 << ", uptr2: " << *uptr2 << std::endl;

    return 0;
}
*****************************
AÇIKLAMA: 
*****************************
CEVAP: 
*****************************

std::unique_ptr ve maliyeti
  • Temel olarak ifade etmek gerekirse düşük maliyet ve yüksek performans sağlayan bir akıllı işaretçidir. Performans açısından etkili olmasının gerekçelerini yukarıda özetledik. Tek bir sahiplik izni verdiği için diğer akıllı işaretçilere göre daha az maliyetlidir diyebiliriz.

#include <iostream>
#include <memory>

using namespace std;

class SmartCode {
public:
    SmartCode() : a(0), b(0) 
    {
        cout << "SmartCode ctor" << endl;
    } 
    
    SmartCode(int val1, int val2) : a(val1), b(val2) {}
    
    ~SmartCode() 
    {
        cout << "SmartCode dtor" << endl;
    }

private:
    int a, b;
};

int main () 
{ 
    cout << "sizeof (SmartCode)           : " << sizeof (SmartCode)            << endl; 
    cout << "sizeof (SmartCode*)          : " << sizeof (SmartCode *)          << endl; 
    cout << "sizeof(unique_ptr<SmartCode>): " << sizeof(unique_ptr<SmartCode>) << endl; 
}
*****************************
AÇIKLAMA: buradaki maliyet çalışılan platforma bağlı değişiklik gösterebilir. Ancak genel olarak pointer ile akıllı işaretçi benzer boyutlarda sonuç verecektir.
*****************************
CEVAP: 
*****************************

std::unique_ptr ve PIMPL idiom
  • PIMPL idiom, sınıfların uygulama ayrıntılarını gizli tutmak ve sürdürülebilir bir arayüz oluşturmak için kullanılan bir tasarım modelidir. Sınıfın kendi yapısını gizleyip programcıya bir pointer sunar. Sınıfların header dosyalarında programcıya daha sade bir arayüz sunar. Bu sayede yapılan değişikliklerin etkisi en az seviyede etki oluşturmuş olur.

#ifndef BOOK_H
#define BOOK_H

#include <memory>
#include <string>

class Book {
public:
    Book(const std::string& title, const std::string& author, int publicationYear);
    ~Book();

    void show_book_info() const;

private:
    class BookImpl; 
    std::unique_ptr<BookImpl> pimpl;
};

#endif

/*************************************************************************************/

#include "book.h"
#include <iostream>

class Book::BookImpl {
public:
    std::string m_title;
    std::string m_author;
    int m_public_year;

    BookImpl(const std::string& t, const std::string& a, int year)
        : m_title(t), m_author(a), m_public_year(year) {}
};

Book::Book(const std::string& title, const std::string& author, int year)
    : pimpl(std::make_unique<BookImpl>(title, author, year)) {}

Book::~Book() = default;

void Book::show_book_info() const 
{
    std::cout << "Title           : " << pimpl->m_title << std::endl;
    std::cout << "Author          : " << pimpl->m_author << std::endl;
    std::cout << "Publication Year: " << pimpl->m_public_year << std::endl;
}

/*************************************************************************************/

#include "book.h"

int main() 
{
    Book book("Suç ve Ceza", "Fyodor Dostoevsky", 1866);
    book.show_book_info();

    return 0;
}
*****************************
AÇIKLAMA: burada sadece bu sınıfa erişimi olanlar ilgili kitap sınıfının gizlenen öğelerine erişebilecek. 
*****************************
CEVAP: 
*****************************

run time polymorphism ve unique_ptr örnek
#include <iostream>
#include <vector>
#include <memory>
#include <random>

using namespace std;

class Car {                                                
public :  
    virtual void start() = 0;  
    virtual void run()   = 0;  
    virtual void stop()  = 0;  
    
    virtual ~Car() = default;  
}; 

class Mercedes : public Car {  
public :  
    void start() final   
    {  
        cout << "Mercedes engine has just started\n";  
    } 

    void run() final    
    {    
        cout << "Mercedes is running\n";    
    }  

    void start() final   
    {    
        cout << "Mercedes engine has just stopped\n";    
    }  
}; 

class Mercedes_S500 : public Mercedes {                   // multiple inheritence  
public :   
    void start() final   
    {   
        cout << "Mercedes engine has just started\n";   
    } 
    
    void run() final   
    {     
        cout << "Mercedes is running\n";     
    }  

    void stop() final   
    {     
        cout << "Mercedes engine has just stopped\n";     
    }   
};

unique_ptr<Car> get_random_car()   
{   
    static mt19937 engine{ random_device {} () };   
    static uniform_int_distribution<> dist{ 1, 3 };  
   
    switch (dist(engine))   
    {   
        case 1:   
        {   
            cout << "*****Mercedes case*****\n";   
            return make_unique<Mercedes>(); 
        }   
        break;   
       
        case 2:   
        {   
            cout << "*****Mercedes_S500 case*****\n";   
            return make_unique<Mercedes_S500>(); 
        }   
        break;   
       
        default:   
        {   
            return nullptr;   
        }   
    }  
}

int main ()  
{  
     while (1) 
     { 
        auto up = get_random_car(); 
        up->start(); 
        up->run(); 
        up->stop(); 
     }   
}
*****************************
AÇIKLAMA: 
*****************************
CEVAP: 
*****************************
ree

 
 
 

Recent Posts

See All
C++ Dilinde [[nodiscard]] attribute

C++’ta [[attribute]] (öznitelik) mekanizması, kod hakkında ek bilgi sağlayarak derleyicinin uyarılar vermesine, belirli optimizasyonlar...

 
 
 
C++ Dilinde constinit

C++20'de tanıtılan önemli bir özellik `constinit`'dir; bu özellik, statik depolama süresine sahip değişkenlerin derleme zamanında...

 
 
 

Comments


bottom of page