Akıllı pointerlar: unique_ptr

unique_ptr aynı anda tek bir nesnenin sahibidir. Kopyalanamaz1 ama taşınabilir. Taşınması sahipliğin aktarılması demektir.

unique_ptr<int> up = make_unique<int>(10); :+1:

veya

auto up = make_unique<int>(10);  :+1:

veya

unique_ptr<int> up (new int);  :-1:
*up = 10;

şekilerinde tanımlanabilir. Sonuncusu tercih edimemesi gereken bir formdur çünkü smart pointer kullanırken new ve delete fonksiyonlarını kullanmak kötü uygulama (bad practice) olarak kabul edilir.

#include <memory>

#include <iostream>
#include <string>

using std::unique_ptr;
using std::make_unique;

using std::cout;
using std::string;
using std::ostream;
class Book {
       private:
	string m_name{};

       public:
	Book() = default;
	explicit Book(string const& t_name) : m_name(t_name) {}
	void setName(string const& t_name) { m_name = t_name; }
	auto& getName() const { return m_name; }
	~Book() = default;

	friend auto& operator<<(ostream&, Book const&);
	friend auto& operator<<(ostream&, unique_ptr<Book> const&);
};
auto& operator<<(ostream& os, Book const& b) {
	return os << b.getName() << '\n';
}

auto& operator<<(ostream& os, unique_ptr<Book> const& b) {
	return os << b->getName() << '\n';
}
unique_ptr<Book> upB1;  // Bir akıllı unique_pointer tanımladık
			// ama henüz heap'ten yer almadık.
upB1 = make_unique<Book>(); // dinamik bellekten yer ayırdık.
upB1->setName("Something");
cout << upB1; // Output: Something

veya benzer tanımlamayı tek satırda:

auto upB2 = make_unique<Book>("Something Other");
cout << upB2; // Output: Something other
// upB1 = upB2; // unique_ptr'ın kopyalama ctor, silinmiştir
		// unique_ptr(const unique_ptr &) = delete;
		// kopyalanmaz

upB1 = move(upB2); // ama taşınabilir, sahiplik simdi upB1'de
			// upB2 sahipliği serbest bıraktı
cout << upB1; // Output: Something other
// auto printVal = [](unique_ptr<Book> up) { // aynı nedenden fonksiyona
//	cout << up->getName() << '\n';           // kopya ile geçilmez
// };

auto printVal = [](unique_ptr<Book> const& up) {
	cout << up->getName() << '\n';
};
Book* rpB1 = upB1.get(); // unique_ptr'deki adresi raw pointer'a atayabiliriz.
cout << rpB1->getName(); // ve sahiplik devam ediyor
Book* rpB2 = upB1.release(); // unique_ptr'daki adres raw pointer'a atandı ve
				// sahiplik bırakıldı
upB1 = make_unique<Book>("Something");
upB2 = make_unique<Book> ("Something Other"); 
 
upB1.swap(upB2); // Sahiplikler yerdeğiştirdi

cout << upB1;
cout << upB2;

// Output: 
// Something Other
// Something

unique_ptr ile raw pointer hafızada eşit miktarda yer kaplar.

cout << "sizeof(upB1) " << sizeof(upB1) << '\n'
     << "sizeof(rpB1) " << sizeof(rpB1) << '\n';

// Output:
// sizeof(spB2) 8
// sizeof(rpB) 8

[1]: Eğer bir kopyası varsa, tanımı gereği, unique yani benzersiz olmaz😄