EDA Playground'da Dene

this ve static

Gün 2: Nesne Yönelimli Programlama (OOP) | this anahtar kelimesi, statik değişkenler ve metodlar

Bu derste iki önemli OOP kavramını öğreneceğiz: nesnenin kendisini ifade eden this anahtar kelimesi ve tüm nesneler arasında ortak olan static değişken ile metodlar. Ayrıca this'i döndürerek metodları zincirleme (method chaining) bağlamayı göreceğiz.

this Anahtar Kelimesi

this, bir metodun içinde o an üzerinde çalışılan nesneyi ifade eder. En sık iki amaçla kullanılır:

  • İsim çakışmasını çözmek: Bir parametre ile özellik aynı ada sahipse, this.id = id ifadesinde this.id nesnenin özelliğini, id ise parametreyi belirtir.
  • Metod zincirleme: Bir metod this'i döndürürse, çağrıları nesne.metot1().metot2() şeklinde art arda bağlayabilirsiniz.

static Değişkenler ve Metodlar

Normalde her nesnenin özelliklerinin kendi kopyası vardır. static olarak işaretlenen bir değişken ise sınıfa aittir; o sınıftan üretilen tüm nesneler arasında tek bir ortak kopya paylaşılır.

  • static değişkenler: Nesne sayısı saymak, toplam istatistik tutmak veya benzersiz kimlik üretmek gibi sınıf düzeyinde durum tutmak için idealdir.
  • static metodlar: Bir nesneye ihtiyaç duymadan, sınıf adı ve :: operatörüyle (Transaction::get_count()) çağrılabilir. Bir static metod yalnızca static üyelere erişebilir, nesneye özgü üyelere erişemez.

Kaynak Kod

// =============================================================================
// GUN 2 - Konu 3: this Anahtar Kelimesi, Statik Degiskenler ve Metodlar
// =============================================================================

class Transaction;
  // Statik degisken: Tum nesneler arasinda paylasilir
  static int count = 0;
  static int total_data = 0;

  // Nesne degiskenleri
  int id;
  int data;
  string name;

  // Constructor: 'this' ile parametre/ozellik ayrimi
  function new(int id, int data, string name = "txn");
    this.id   = id;       // this.id = nesne ozelligi, id = parametre
    this.data = data;
    this.name = name;
    count++;              // Her yeni nesne sayaci artirir
    total_data += data;
  endfunction

  function void display();
    $display("  [%s] id=%0d, data=%0d", this.name, this.id, this.data);
  endfunction

  // Statik metod: Nesne olmadan cagrilabilir
  static function int get_count();
    return count;
  endfunction

  static function int get_total_data();
    return total_data;
  endfunction

  static function void reset_stats();
    count = 0;
    total_data = 0;
  endfunction

  // Zincirleme metod (method chaining benzeri): this dondur
  function Transaction set_data(int data);
    total_data -= this.data;  // Eski degeri toplamdan dus...
    total_data += data;       // ...yeni degeri ekle (toplam tutarli kalir)
    this.data = data;
    return this;
  endfunction

  function Transaction set_name(string name);
    this.name = name;
    return this;
  endfunction

endclass

module this_ve_static;
  initial begin
    Transaction t1, t2, t3;

    $display("=== this ve static Kullanimi ===\n");

    // --- Statik: Nesne olusmadan erisim ---
    $display("--- static: Baslangic ---");
    $display("  Nesne sayisi = %0d", Transaction::get_count());

    // --- Nesne olustur ---
    $display("\n--- Nesne Olusturma (this ile) ---");
    t1 = new(1, 100, "paket_A");
    t2 = new(2, 200, "paket_B");
    t3 = new(3, 300, "paket_C");

    t1.display();
    t2.display();
    t3.display();

    // --- set_data ve set_name kullanimi (tek tek) ---
    $display("\n--- set_data ve set_name (tek tek cagri) ---");
    t1.set_data(150);
    t1.set_name("paket_A_guncel");
    t1.display();

    // --- set_data ve set_name kullanimi (zincirleme) ---
    $display("\n--- set_data ve set_name (zincirleme) ---");
    t2.set_name("paket_B_plus").set_data(250);
    t2.display();

    // --- Statik degiskenlere erisim ---
    $display("\n--- static Degiskenler ---");
    $display("  Toplam nesne = %0d", Transaction::get_count());
    $display("  Toplam veri  = %0d", Transaction::get_total_data());
    // Nesne uzerinden de erisilebilir (ayni deger)
    $display("  t1.count     = %0d (statik, tum nesneler ayni)", t1.count);

    // --- Statik reset ---
    $display("\n--- static Reset ---");
    Transaction::reset_stats();
    $display("  Reset sonrasi sayac = %0d", Transaction::get_count());

    // Yeni nesneler
    t1 = new(10, 500);

    // Yeni nesne uzerinde zincirleme setter ornegi
    t1.set_name("reset_sonrasi_t1").set_data(550);
    t1.display();

    t2 = new(20, 600);
    $display("  2 yeni nesne sonrasi sayac = %0d", Transaction::get_count());
    $display("  Toplam veri = %0d", Transaction::get_total_data());

    $display("\n=== this ve static Sonu ===");
    $finish;
  end
endmodule

Kodun Açıklaması

  • Statik değişkenler: static int count ve static int total_data, Transaction sınıfının tüm nesneleri arasında paylaşılır. Her new() çağrısında count++ ile nesne sayısı artar ve total_data += data ile toplam veri birikir.
  • this ile başlatma: Constructor'da this.id = id, this.data = data, this.name = name ifadeleri parametre adlarıyla çakışan özellikleri ayrıştırır.
  • Statik metodlar: get_count(), get_total_data() ve reset_stats() static olduğundan nesne olmadan Transaction::get_count() biçiminde çağrılabilir. Nitekim ilk çağrı, henüz hiç nesne yokken sayacı 0 olarak okur.
  • Method chaining: set_data() ve set_name() metodları, ilgili özelliği güncelledikten sonra return this ile nesnenin kendisini döndürür. Bu sayede t2.set_name("paket_B_plus").set_data(250) gibi zincirleme çağrı mümkün olur.
  • Statik üyeye nesne üzerinden erişim: t1.count ifadesi de aynı paylaşılan değeri verir; statik değişken nesneye özgü olmadığı için tüm nesnelerde aynıdır.
  • reset_stats() etkisi: Statik sayaçlar sıfırlanır; ardından oluşturulan yeni nesneler sayacı tekrar artırmaya baştan başlar.
  • Dikkat - set_data ve total_data: set_data() yalnızca total_data += data yapsaydı, aynı nesnenin verisini değiştirmek toplamı çift sayardı (eski 100 + yeni 150 = 250, oysa nesnede yalnızca 150 var). Bu yüzden önce eski değer toplamdan düşülür, sonra yeni değer eklenir. Statik istatistik tutan her sınıfta bu "önce çıkar, sonra ekle" kalıbını alışkanlık yapın.

Önemli Noktalar

  • this, yalnızca bir nesne metodu (non-static) içinde anlamlıdır; static bir metodun içinde this kullanılamaz çünkü ortada belirli bir nesne yoktur.
  • static değişkenler sınıf yüklendiğinde bir kez başlatılır ve nesneler arasında paylaşılır; benzersiz kimlik üretimi veya sayaç gibi global durum için idealdir.
  • static metodlar yalnızca static üyelere erişebilir; nesneye özgü (id, data, name) alanlara erişemezler.
  • Statik üyelere erişmek için sınıf adı ve :: operatörünü kullanmak (Transaction::get_count()), nesne üzerinden erişmekten (t1.count) niyet olarak daha açıktır.
  • Method chaining için her setter'ın return this yapması gerekir; bu kalıp, akıcı ve okunaklı yapılandırma kodu yazmayı sağlar.
  • t1.set_data(150); gibi bir çağrıda dönüş değeri (handle) kullanılmaz; bazı simülatörler bunun için uyarı verir. Zincirleme yapmadığınız yerlerde void'(t1.set_data(150)); yazarak uyarıyı susturabilirsiniz.
  • static değişkenlerin bildirimde başlatılması (static int count = 0;) simülasyon başında bir kez gerçekleşir. Aynı sınıfın birden fazla package'e kopyalanması veya parametreli sınıflarda her özelleşmenin (Foo#(8), Foo#(16)) kendi statik kopyasına sahip olması sürpriz yaratabilir.

Statik vs Nesne Üyeleri

Nesne üyesi (int id) Statik üye (static int count)
Kopya sayısı Her nesnede bir tane Sınıf başına tek
Erişim t1.id Transaction::count veya t1.count
this kullanılabilir mi? Evet Statik metot içinde hayır
Ömür Nesne yaşadığı sürece Tüm simülasyon boyunca
Tipik kullanım Transaction alanları ID sayacı, toplam istatistik, singleton

Benzetme: Bir sınıfı okul olarak düşünün: her öğrencinin (nesne) kendi numarası ve notu vardır (nesne üyeleri). Okulun tek bir ziline (statik üye) herkes aynı anda uyar; zili "3-A sınıfının zili" diye ayrı ayrı düşünmezsiniz. Transaction::get_count() zile dışarıdan bakmaktır; t1.count da aynı zile bir öğrencinin penceresinden bakmaktır.

UVM'de Karşılığı

UVM'de static kavramı her yerdedir: her bileşen tipi için factory kaydı statik bir nesnede tutulur (type_id), uvm_root::get() ve uvm_config_db statik metodlarla erişilen tekil (singleton) yapılardır. Bu derste Transaction::get_count() ile yaptığınız şey, ileride uvm_config_db#(int)::get(...) yazarken aynen karşınıza çıkacaktır.

Sık Yapılan Hatalar

  • Statik metottan nesne alanına erişmek: static function int get_id(); return id; endfunction derleme hatasıdır; ortada bir nesne yoktur.
  • Statik sayacı reset etmeyi unutmak: Birden fazla test aynı simülasyonda art arda koşarsa count sıfırlanmaz ve ikinci testin ID'leri 0'dan başlamaz. Test başında reset_stats() çağırın.
  • this ile parametreyi karıştırmak: Constructor'da id = id; yazarsanız parametre kendine atanır, nesne alanı değişmez. Ya this.id = id yazın ya da parametre adını farklı seçin (id_i).
  • Static ile const'u karıştırmak: static paylaşılan değişkendir ve değiştirilebilir; const ise sabittir. İkisi birlikte (static const int MAX = 10;) sınıf sabiti tanımlamanın doğru yoludur.

Kendinizi Deneyin

  1. set_data içindeki total_data -= this.data; satırını silin ve Toplam veri çıktısının nasıl bozulduğunu gözlemleyin.
  2. Transaction sınıfına static function Transaction create_default(); fabrika metodu ekleyip içinde new(0, 0, "default") döndürün. Statik metot nesne yaratabilir mi?
  3. static const int MAX_DATA = 1000; ekleyip set_data içinde data > MAX_DATA ise uyarı basın.
  4. t1, t2, t3 için t1.count, t2.count, t3.count değerlerini yazdırın; neden hepsi aynı?
Hızlı Kontrol: Statik bir metot içinde this kullanılabilir mi?

Hayır. Statik metot belirli bir nesneye bağlı değildir; Transaction::get_count() çağrıldığında ortada "bu nesne" yoktur. Bu yüzden statik metotlar yalnızca statik üyelere ve argümanlarına erişebilir.