31 Mayıs 2019 Cuma

bool tipi

Bool tipi
C ile kullanılır. Açıklaması şöyle.
C did not originally have a Boolean type, it was added in the 1999 version of the language (C99). At that point, C++ was already standardized (in 1998) to use the type bool, with keywords false and true. To keep the C Boolean type separate from the one in C++, as well as preventing the new name from breaking old C code, it was named _Bool.
bool nerede tanımlı ?
bool eskiden #define kullanılarak tanımlanırdı. Şöyle komik tanımlamalar bile vardı.
#define TRUE  '/'/'/'
#define FALSE '-'-'-'
Yeni derleyiciler bool tipini built-in bir tip olarak işliyorlar.

bool integral bir tiptir
Açıklaması şöyle.
Types bool, char, char16_t, char32_t, wchar_t, and the signed and unsigned integer types are collectively called integral types.
Her integral tip için karşılaştırma işlemi tanımlıdır
Dolayısıyla bool için de aşağıdaki işlemler tanımlıdır.
false < false = false
false < true = true
true < false = false
true < true = false
bool tipinin büyüklüğü belli değildir
Tamamen gerçekleştirime bağlı.
sizeof(char)sizeof(signed char) and sizeof(unsigned char) are 1; the result of sizeof applied to any other fundamental type is implementation-defined. [Note: in particular, sizeof(bool) and sizeof(wchar_t) are implementation-defined.69)]
Ayrıca bool 1 byte olmak zorunda da değil.
sizeof(bool) is not required to be 1.
Örnek
Şöyle yaparız. Çıktı olarak 1 alırız.
std::cout<<sizeof(!0);
bool ile bit işlemleri yapılmaz
Açıklaması şöyle.
In C++ the bit representation (and even the size) of a bool is implementation defined; generally it's implemented as a char-sized type taking 1 or 0 as possible values.

If you set its value to anything different from the allowed ones (in this specific case by aliasing a bool through a char and modifying its bit representation), you are breaking the rules of the language, so anything can happen. In particular, it's explicitly specified in the standard that a "broken"  bool may behave as both true and false (or neither true nor false) at the same time:
bool int'e çevrilebilir
Açıklaması şöyle. bool zaten integral bir tip olduğu için int'e de rahatlıkla çevrilebilir.
A prvalue of type bool can be converted to a prvalue of type int, with false becoming zero and true becoming one.
Örnek
bool int'e çevrilebildiği için şöyle yaparız.
bool x = 1;
if (x==1)
    Do something..
Örnek
bool int'e çevrilebildiği için şöyle yaparız.
bool y = 0;
if (y>0.5)
    Do something..
bool'a değer atamak
bool sadece true veya false değerleri veya buna karşılık gelen primitif bir değer yani 1 veya 0 atanabilir. Açıklaması şöyle.
6.2.6 Representations of types
6.2.6.1 General
5 Certain object representations need not represent a value of the object type. If the stored value of an object has such a representation and is read by an lvalue expression that does not have character type, the behavior is undefined. [...]
Çünkü bool derleyici tarafından aşağıdakine benzer bir halde kullanılıyor.
a = 00000000 (false)
!a = 11111111 (true)
Örnek
primitif değer ataması yani 1 veya 0 yapılabilir. Şöyle yaparız
bool x = 1; // x will be true
bool y = 0; // y will be false
bool z = 1; // z will be true
Örnek
a değişkenine 1 veya 0 değeri atanmadığı için if (a) veya if (!a) da olsa hep true çıktısı verir
#include <cstring>
#include <iostream>

bool a;
memset(&a, 0x03, sizeof(bool));
if (a) {
  std::cout << "a is true!" << std::endl;
}
if (!a) {
  std::cout << "!a is true!" << std::endl;
}
Sebebi ise yukarıdaki kodun muhtemelen
a = 00000011 (true)
!a = 11111100 (also true)
şeklinde çalışması

Derleyici
Şu açıklamada bool kullanırken derleyicinin 1 veya 0 olduğunu kontol eden kod ürettiği için girdilerde boolean kullanılmasının çok verimli olmayan kodlara sebep olduğu söyleniyor. Ancak sanırım artık bu açıklama güncel değil.
Boolean variables are stored as 8-bit integers with the value 0 for false and 1 for true. Boolean variables are overdetermined in the sense that all operators that have Boolean variables as input check if the inputs have any other value than 0 or 1, but operators that have Booleans as output can produce no other value than 0 or 1. This makes operations with Boolean variables as input less efficient than necessary.




30 Mayıs 2019 Perşembe

operator new - Raw Bellek Alanı

Giriş
operator new global ve sınıf için olmak üzere ikiye ayrılır. Global operator new metodunu çağırmak için şöyle yaparız
::new T();
Sınıf için olanı (eğer varsa) çağırmak için şöyle yaparız
new T();
Global Operator New Nasıl Çalışır
Effective C++ 55'e göre operator new şöyle çalışır.
When operator new is unable to fulfill a memory request, it calls the new-handler function repeatedly until it can find enough memory.

A well-designed newhandler function must do one of the following:
  • Make more memory available.
  • Install a different new-handler.
  • Deinstall the new-handler
  • Throw an exception
  • Not return
pseudo kod olarak metod şuna benzer.
while (true) {
  //attempt to allocate size bytes
  if (the allocation was successful) 
     return (a pointer to the memory);
  // allocation was unsuccessful; find out what the
  // current new-handling function is 
  new_handler globalHandler = set_new_handler(0);
  set_new_handler(globalHandler);
  if (globalHandler) (*globalHandler)();
  else throw std::bad_alloc();
}
new_handler bir function pointer olarak tanımlıdır. Gerçek kod ise  şöyle. Tüm algoritmanın nasıl çalıştığı şöyle anlatılıyor. Açıklaması şöyle
In case of failure, the standard library implementation calls the function pointer returned by std::get_new_handler and repeats allocation attempts until new handler does not return or becomes a null pointer, at which time it throws std::bad_alloc.
Thread Safe
Global operator new thread safe. Açıklaması şöyle.
For purposes of determining the existence of data races, the library versions of operator new, user replacement versions of global operator new, the C standard library functions aligned_­alloc, calloc, and malloc, the library versions of operator delete, user replacement versions of operator delete, the C standard library function free, and the C standard library function realloc shall not introduce a data race ([res.on.data.races]). Calls to these functions that allocate or deallocate a particular unit of storage shall occur in a single total order, and each such deallocation call shall happen before the next allocation (if any) in this order.
Operator New ve void *
void * C++'ta pek kullanılmaz. operator new bu kullanım yerlerinden bir tanesi.

Operator New'e Geçilen size_t Büyüklüğü
Kurallar şöyle.

1. Derleyici sınıf için sizeof(T) geçer. Bellek ayıran kod parçası yani operator new belleği yönetebilmek için talep edilenden biraz daha büyük bir alan ayırır.

2. Derleyici sınıf dizisi için N * sizeof(T)'den biraz daha büyük bir alan geçer. Buradaki amaç dizi içinde kaç tane nesne olduğunu takip etmek ve dolayısıyla kaç defa destructor çağrılacağını bulmaktır. Bellek ayıran kod parçası yani operator new belleği yönetebilmek için yine talep edilenden biraz daha büyük bir alan ayırır.
When a new-expression calls an allocation function and that allocation has not been extended, the new-expression passes the amount of space requested to the allocation function as the first argument of type std::size_t. That argument shall be no less than the size of the object being created; it may be greater than the size of the object being created only if the object is an array.
Elimizde şöyle bir sınıf olsun ve array için operator new metodunu override etmiş olalım. Sınıfın destructor metodu olduğuna dikkat!
struct foo {
  static void* operator new[](std::size_t sz) {
    return malloc(sz); //0x...16FO döndürür
  }
  static void operator delete[](void* ptr) {
    free(ptr);
  }
  virtual ~foo() {}
};
Foo sınıfını şöyle çağıralım.
new foo[10]; //0x...16F8 alırım
Bu çağrı sonucunda sz parametresinin 10 tanelik bir dizi için gereken 80 byte'tan daha büyük bir değer ile çağrıldığını görebiliriz. Yani derleyici kendisi için kullanmak üzere 80 byte'tan daha büyük bir alan talep etmiştir.

Sınıf için Operator New
operator new - Sınıf İçin (Raw Bellek Alanı) yazısına taşıdım.

Sınıf İçin Operator Delete
Şöyle yaparız.
class A
{
public:
  void* operator new(std::size_t sz);
  void operator delete(void* ptr);
};
Bir sınıf için operator delete metodunun boş olması bu sınıfın belleğinin halen kullanılabileceği anlamına gelmez. Elimizde şöyle bir sınıf olsun
struct Foo {
  int a;
  static void operator delete(void* ptr) {}
  Foo(): a(5) {}
  ~Foo() { std::cout << "Destructor called\n"; }
  void doSomething() { std::cout << __PRETTY_FUNCTION__ << "a = " << a << " called\n"; }
};
Bu sınıfı şöyle kullanalım
Foo* foo = new Foo();
delete foo;
foo->doSomething(); // safe?
Çıktı olarak şunu alırız. Çunkü sınıfın destructor'ı bir kere çağrılırsa tüm alanları için de destructor çağrılır. Bu durumda alanlara erişmek undefined behavior'a (UB) sebep olur.
Destructor called
void Foo::doSomething() a= 566406056 called
Sınıf için Operator New Array
Şöyle yaparız
struct foo {
  void* operator new[](std::size_t sz) {
    ...
  }
  static void operator delete[](void* ptr) {
    ...
  }
};
Global Operator New Overload
Bir kere bile bu işi yapmam gerekmedi. Metod imzaları şöyle. Hem operator new hem de karşılık gelen delete'leri verdim.
void* operator new (std::size_t size) throw (std::bad_alloc);
void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) throw();
void* operator new (std::size_t size, void* ptr) throw();
void* operator new[] (std::size_t size) throw (std::bad_alloc);
void* operator new[] (std::size_t size, const std::nothrow_t& nothrow_value) throw();
void* operator new[] (std::size_t size, void* ptr) throw();

void operator delete (void* ptr) throw();
void operator delete (void* ptr, const std::nothrow_t& nothrow_constant) throw();
void operator delete (void* ptr, void* voidptr2) throw();
void operator delete[] (void* ptr) throw();
void operator delete[] (void* ptr, const std::nothrow_t& nothrow_constant) throw();
void operator delete[] (void* ptr, void* voidptr2) throw();
Global Operator New Overload
global operator new ise şöyle yazılırak override edilir. blah blah yazan yere istenen tip konulur.
void* operator new (std::size_t size, optional blah blah here) blah blah here
Bu işi bir kere yaptım. Kendi yönettiğimiz bellek alanına erişmek için yeni bir operator new yazdık. blah yerine de yine dummy bir sınıf kullandık. 


Array İçin Global Operator New Overload
Bir kere bile bu işi yapmam gerekmedi. Eğer array için new override edilirse, delete de override edilmeli. Metod imzaları şöyle olabilir.
void* operator new[] (std::size_t size) throw (std::bad_alloc);
void* operator new[] (std::size_t size, const std::nothrow_t&nothrow_value)throw()
void* operator new[] (std::size_t size, void* ptr) throw();

void operator delete[] (void* ptr) throw();
void operator delete[] (void* ptr, const std::nothrow_t& nothrow_constant)throw();
void operator delete[] (void* ptr, void* voidptr2) throw();
Ya da şöyle olabilir..
void* operator new[](std::size_t size);
void operator delete[](void* ptr) noexcept;
void operator delete[](void* ptr, std::size_t size) noexcept;
Derleyicisine göre denemek gerekiyor.