Thread overview
Heap Bölgesinde Tutulan Nesneler İçin Operatörleri Aşırı Yükleme Olmamalı Mı?
Sep 30, 2017
İbrahim
Sep 30, 2017
kerdemdemir
Sep 30, 2017
İbrahim
September 30, 2017

Selamün Aleyküm;

C++'da sınıflar ve yapılar stack bölgesinde tutulurlar fakat Object Pascal'da sınıflar heap bölgesinde, kayıtlar (record) ise stack bölgesinde tutulurlar. Bu yüzden de Object Pascal'da bir sınıftan bir nesne oluşturduğumuzda bellek yönetimini kendimiz yapmalıyız:

type
 TExample = class
 ...
 end;

var
 example: TExample;
begin
 example := TExample.Create; // -> Heap bölgesinde tutuluyor.
end.

Eğer bu nesneyi bellekten silmezsek bellekte hala yer işgal ettiğini görebiliriz:
Not: C++ için GCC, Object Pascal için de Free Pascal kullanıyorum.

Heap dump by heaptrc unit
1 memory blocks allocated : 8/8
0 memory blocks freed     : 0/0
1 unfreed memory blocks : 8
True heap size : 32768
True free heap : 32608
Should be : 32632
Call trace for block $00007F471EF730C0 size 8

(Derleme komutu: fpc -gh test.pas)

Şimdi asıl konumuza gelecek olursak, C++'da sınıflarda operatörleri aşırı yükleme olayını kullanabiliyoruz. Lakin Object Pascal ve Delphi'de sınıflarda operatörleri aşırı yükleme yapamıyoruz, sadece kayıtlarda (record) yapabiliyoruz. Neden sınıflar için operatör aşırı yükleme olmadığına dair şurada açıklama yapmışlar: https://stackoverflow.com/questions/2090472/why-isnt-operator-overloading-available-for-classes-in-delphi

Delphi'de sınıflarda operator overloading olmasının şu nedenlerden ötürü zararlı olacağını söylüyorlar:
Alıntı:

>

Sınıflarda operator overloading olursa sürekli olarak hafıza sızıntılarına sebep olur. Örnek olarak:

> type
>   TExample = class
>   private
>     memberData: Integer;
>   public
>     class operator Add(obj: TExample; n: Integer): TExample;
>   end;
>
>   class operator TExample.Add(obj: TExample; n: Integer): TExample;
>   begin
>     Result := TExample.Create; // Yeni alan oluşturuyor.
>     Result.memberData := obj.memberData + n;
>   end;
> ```

>
> Burada example0 := example1 + 5; gibi kullandığımızda + operatörünün yaptığı işlemlerde sürekli yeni alan oluşturuluyor ve bu alanlar silinmiyor. Yani kısaca bellek taşması oluşuyor.
>
diyorlar. Ama bu mantıksız değil mi? C++'da da şöyle yapabiliriz:

Example* example = new Example;
..
delete example; // -> Belleği geri verdik.


Burada da C++ sınıfını heap bölgesinde tutuyor ve elimizle belleği iade etmemiz lazım. Örnek kod:

class Example
{
private:
int _count;
public:
Example();
Example(int count);
int getCount() const;

Example operator+(int c);
};

Example::Example() : _count(0)
{
}

Example::Example(int count) : _count(count)
{
}

int Example::getCount() const
{
return _count;
}

// + Operatörünü Aşırı Yüklüyoruz:
Example Example::operator+(int c)
{
return Example(_count + c);
}


Kullanalım:

// Stack'de tutuluyor:
Example example(75);
example = example + 7;
cout << example.getCount() << endl;

// Heap'de tutuluyor:
Example* ex = new Example(75);
*ex = *ex + 7;
cout << ex->getCount() << endl;



Bu örneği Object Pascal'da record ile şöyle yapabiliyoruz:

type
TExample = record
strict private
_count: Integer;
public
// record'da kurucu fonksiyonlar olmadığı için Getter ve Setter kullanıyoruz:
function getCount: Integer;
procedure setCount(count: Integer);

class operator Add(obj: TExample; n: Integer): TExample;
end;

function TExample.getCount: Integer;
begin
Result := _count;
end;

procedure TExample.setCount(count: Integer);
begin
_count := count;
end;

class operator TExample.Add(obj: TExample; n: Integer): TExample;
begin
Result._count := obj._count + n;
end;


Kullanımı:

var
example: TExample;
begin
example.setCount(75);
example := example + 7;
WriteLn(example.getCount);
end.


Bunda sorun yok ama sınıflarda operator overloading'in bellek taşması sorunu oluşturacağını söylüyorlar ama bir Object Pascal sınıfına Java'da olduğu gibi toplama yapan bir metot da yazabiliriz:

function TExample.add(n: Integer): TExample;
begin
Result._count := _count + n;
end;


E yukarıda record'daki Add operatörü ile bu sınıfta yazdığımız add fonksiyonu aynı kodları kullanıyor. Neden + operatörü varken add diye bir metot yazayım ki? Sizce bu operatör aşırı yükleme konusu Delphi gibi sınıfları heap'de tutulan dillerde olmaması mı yoksa olması mı daha iyi olur? Yani ben bellek tahsisi için bir problem göremiyorum.

Teşekküler.

-- 
[ Bu gönderi, <http://ddili.org/forum>'dan dönüştürülmüştür. ]
September 30, 2017

Selam İbrahim,

Bu operatörleri aşırı yükleme bazen sıkıntı yaratabiliyor gerçekten.

Örneğin + operatörünün bir çok defa eleman ekleme için kullandığını gördüm. Yani bir vektörümüz var diyelimki ErdemVec ben + operatörünü push_back gibi düşünüyorum. Fakat başkası bunu beklemiyor ve kodlama hatalarına sebep acabiliyor.

101 Rules Guidelines and Best Practices kitabının 25-30 arasındaki maddelerine bakmanı tavsiye ederim.

Ben C++'da allocate edilen memory eğer destructor'da silinirse leak sorunu olmamazı gerektiğini düşünüyorum(tabi gereksiz yaratılmış objeler ve performans hariç).

Saygılar
Erdemdem

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

September 30, 2017

@kerdemdemir cevabınız için teşekkür ederim.
Object Pascal operatör aşırı yüklemeyi kayıtlarda sonuna kadar destekliyor. Fakat Object Pascal’da sınıflar heap tarafında olduğu için Object Pascal geliştiricileri sınıflarda operatör aşırı yüklemenin olmasının bellek taşmalarına sebep olacağını söylüyorlar. Ben bunu basit bir şekilde şöyle anlatayım:
Yine TExample adında bir sınıfımız olsun ve bu sınıfın static olan swap adlı bir üye fonksiyonu olsun:

class TExample
{
private:
 Int _num;
public:
 static void swap(TExample obj0, TExample obj1) const
 {
TExample tmp = obj0;
obj0 = obj1;
obj1 = tmp;
 }
};

Bu üye fonksiyonda (swap) herhangi bir bellek taşması olmaz. Çünkü tüm veriler stack’da olduğu için oluşturulan tmp bu üye fonksiyon işini yapınca otomatik olarak silinecek.
Ama Object Pascal’da swap şöyle kodlanması gerekiyor:

class procedure TExample.swap(obj0, obj1: TExample);
Var
 tmp: TExample;
Begin
 tmp := TExample.Create; // -> Bunun C++ karşılığı TExample* tmp = new TExample;
 tmp := obj0;
 obj0 := obj1;
 obj1 := tmp;
End;

Bu swap metodunda ise bellekten yer alındığı için bu metot işi bitince bile tmp nesnesini otomatik silmeyecek. İşte bu yüzden dolayı sınıflarda operatör aşırı yükleme sakıncalıdır denildiği için koyulmamış. Lakin bana bu tuhaf geliyor. Farkedersiniz ki swap aslında sadece bir üye fonksiyon, yani üye fonksiyon içinde de bellek taşması oldu ve bu swap fonksiyonunu herhangi bir operatörü aşırı yükleyerek de yapabiliriz. O zaman da bellek taşması olacak. Peki o zaman bir operatör fonksiyonu ile normal bir üye fonksiyon arasında kod olarak hiçbir fark yok. Bellek yünetimi varsa bu da bizim elimize bırakılmış. Programcı istediği gib hükmeder. Anlatmak istediğim operatörlerin aşırı yüklenmesinin hatalara yol açabileceği değil de Object Pascal’da record’larda bunu kullanabiliyorken sınıflarda bellek taşmasına sebep olabiliyor diye bunu kullanmamıza izin verilmemesi.

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]

October 01, 2017

Ne C++'ta ne de D'de sınıflarda işleç yüklemeye karşı bir uyarı duydum. C++'ın RAII yöntemi ve D'nin çöp toplayıcısı gereken temizliği halleder.

Gösterdiğin bağlantıdaki örnek şu: 'myResult := myObject1 + myObject2 + myObject3' ifadesi sırasında en az bir geçici nesne oluşturulması gerekiyormuş ve programcının o nesneyi geri verme şansı yokmuş. Deneyip görmek gerek... :)

Ali

--
[ Bu gönderi, http://ddili.org/forum'dan dönüştürülmüştür. ]