Forum: Diğer Konular RSS
Heap Bölgesinde Tutulan Nesneler İçin Operatörleri Aşırı Yükleme Olmamalı Mı?
İbrahim #1
Üye Eki 2015 tarihinden beri · 162 mesaj
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Konu adı: Heap Bölgesinde Tutulan Nesneler İçin Operatörleri Aşırı Yükleme Olmamalı Mı?
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-opera…

Delphi'de sınıflarda operator overloading olmasının şu nedenlerden ötürü zararlı olacağını söylüyorlar:
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.
kerdemdemir #2
Üye Eyl 2013 tarihinden beri · 123 mesaj · Konum: Danimarka
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
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
İbrahim #3
Üye Eki 2015 tarihinden beri · 162 mesaj
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
@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.
acehreli (Moderatör) #4
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4538 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
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
Doğrulama Kodu: VeriCode Lütfen resimde gördüğünüz doğrulama kodunu girin:
İfadeler: :-) ;-) :-D :-p :blush: :cool: :rolleyes: :huh: :-/ <_< :-( :'( :#: :scared: 8-( :nuts: :-O
Özel Karakterler:
Bağlı değilsiniz. · Şifremi unuttum · ÜYELİK
This board is powered by the Unclassified NewsBoard software, 20100516-dev, © 2003-10 by Yves Goergen
Şu an: 2017-12-16, 08:43:29 (UTC -08:00)