new operator etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
new operator etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

10 Temmuz 2018 Salı

new operator kullanımı - new expression

Giriş
"new operator" ve "operator new" farklı şeylerdir. Ve bence çok kötü bir isimlendirme yapılmış.
  • "operator new" aynı C'deki malloc gibi raw bellek alanı döndürür. 
  • "new operator" ise C++ dilindeki bir anahtar kelimedir. Bu anahtar kelimeyi gören derleyici kod üretir.
operator new aynı zamanda "new expression" olarak ta bilinir.

New Operator
"new operator" ' ünün 3 şekli bulunur. Bunlar sırasıyla şöyledir:

1. Normal new
2. Null Döndüren new
3. Placement new
3. Kendi new metodum
"new operator" için derleyici arka planda kod üretir. "new operator için üretilen kod" yazısında detayları açıklamaya çalıştım.

New ve Free'nin Karıştırılması
new ile ayrılan bellek alanı asla free ile silinmemelidir.

New İle Ayrılan Belleğin C Metodunda Kullanılması
Bu konuda bir problem bulunmuyor. new ile ayrılan bellek alanı C metodlarında kullanılabilir. Tek koşul olarak C kütüphanesinin belleği free etmemesi gerekir diyebilirim.
int *p = new int(42);
lib_func(p);
delete p;
New Komutu Optimize Out Edilebilir
C++14 standardına göre kullanılmadığı belirlenen new komutu dikkate alınmayarak kod üretilmeyebilir.
An implementation is allowed to omit a call to a replaceable global allocation function (18.6.1.1, 18.6.1.2). When it does so, the storage is instead provided by the implementation or provided by extending the allocation of another new-expression.
1. Normal New Kullanımı
Bu kullanım şeklinde verilen nesne heap'te yaratılır. Eğer yer kalmamışsa std::bad_alloc atılır. Bu durumda genelde de aşağıdaki mesaj ile uygulama sonlanır.
terminate called after throwing an instance of std::bad_alloc
what(): std::bad_alloc
Aborted(core dumped)
1.1 Kendi Constructor'ımızın Exception Fırlatması
Constructor İçinde Exception Fırlatmak yazısına taşıdım.

2. Null Döndüren New Kullanımı
Bu kullanım şeklinde nesne heap'te yaratılır. Eğer yer kalmamışsa çağrı null döner. Çok eskiden yani Turbo C++ zamanlarında C++'ın default çalışma modeli null döndürmek şeklindeydi. Sonradan default çalışma modeli exception atmaya döndü. Null döndüren new kullanmak için null çağrısının içine std::nothrow parametresini geçmek gerekir. Şöyle yaparız.
SomeType *p = new(std::nothrow) SomeType;
Integral tipleri kullanan bir başka örnek şöyle
int* mem = new (std::nothrow) int[100];
std::nothrow <new> dosyasında tanımlı boş bir sınıf. Açıklaması şöyle
std::nothrow is a constant of type std::nothrow_t used to disambiguate the overloads of throwing and non-throwing allocation functions.
std::nothrow ve std::noexcept farklı tipler. Bazen karıştırılabiliyorlar.

Null döndüren new kullanmanın masaüstü uygulamalarında ne avantajı var bilmiyorum. Her tarafa 
"if " koşulu koyulmasına sebep oluyor.
T *p = new (std::nothrow) T(args);

if ( p == nullptr ) //must check for nullity
{
     std::cout << "memory allocation failed" << std::endl;
}

3. Placement New Kullanımı
Placement New yazısına taşıdım.

4. Kendi new metodum
Açıklaması şöyle.
18.6.2 Storage allocation and deallocation [new.delete]
2 Replaceable: A C ++ program may define functions with either of these function signatures, and thereby displace the default versions defined by the C ++ standard library.
Bu yöntemi bir kere gömülü bir projede pool denilen bir yapıdan bellek alıp vermek için kullandım.



14 Temmuz 2017 Cuma

new operator için üretilen kod

Giriş
"new operator" ve "operator new" farklı şeylerdir. Ve bence çok kötü bir isimlendirme yapılmış.

"operator new" aynı C'deki malloc gibi raw bellek alanı döndürür. Şöyle yaparız
char *x = static_cast<char *>(operator new(100));//malloc ile aynıdır
"new operator" ise C++ dilindeki bir anahtar kelimedir. Bu anahtar kelimeyi gören derleyici kod üretir. Şöyle yaparız
my_class *x = new my_class(0);
Bu yüzden "new operator" için 3 tane overload vardır.

new operator için üretilen kod
Not : Bu yazıda nesnemiz için user defined new operator olmadığını etmediğini farz ettim.

Kodlarken genellikle new T() şeklinde bir satır yazıp geçeriz. Arka planda neler döndüğüne pek dikkat etmeyiz. Aslında new T() kodunun çalışması için arka planda derleyici bizim için kod üretmektedir. Bu kod iki ana esasa dayanır.


1. Global operator new
Yaratmak istediğimiz nesne için heap'ten alan almak gerekir. Bunun için derleyici önce
T *p = ::operator new(sizeof(T)); // allocate memory for a T
çağrısı ile aynı malloc'taki gibi bellek alanı ayıran kod parçası ayrılır.

2. Contructor Çağrısı
Daha sonra da T nesnesinin constructor metodunu çağıran kod parçasını üretiyor.
Bu kod ise şuna benziyor.
Not : Aslında aşağıdaki kod placement new kodudur fakat kavramı göstermesi için almakta mahzur görmedim.
new (p) T; // construct a T into the allocated memory

3. Cast
Ayrılan bellek alanı istediğimiz nesne tipine cast edilerek bize verilir. Bu iş için kod üretilmiyor ancak mantıksa olarak işlemin bir parçası olduğu için eklemek istedim.

Sonuç
new T() çağrısı, global operator new - yani ::new()'den  -  farklıdır !

Global operator new ile derleyicinin ürettiği kodun farkını gösteren örnek aşağıda.
// Allocates space for a T, and calls T's constructor,
// or calls a user-defined overload of new.
//
T* v = new T;

// Allocates space for N instances of T, and calls T's 
// constructor on each, or calls a user-defined overload
// of new[]
//
T* v = new T[N];

// Simply returns a raw byte array of `sizeof(T)` bytes.
// No constructor is invoked.
//
void* v = ::operator new(sizeof(T));
new operator İçin Üretilen Kod
Şuna benzer.
template <typename T, typename... Ts>
T* library_new(Ts&&... xs)
{
  auto* ptr = /* request enough memory for `T` from OS */;
  new (ptr) T(std::forward<Ts>(xs)...);
  return ptr;
}

template <typename T>
void library_delete(T* ptr)
{
  ptr->~T();
    /* reclaim memory for `T` from OS */
} 
Nesnenin Heap'te Yaratılmasını Önlemek
Bunun için sadece new operator'ünü saklamak yeterli.
private:
    void* operator new(size_t);          // standard new
    void* operator new(size_t, void*);   // placement new
    void* operator new[](size_t);        // array new
    void* operator new[](size_t, void*); // placement array new
Aynı zamanda delete operatürünü de engellemek gerekebilir. Ama yazmadım.