Forum: Ders Arası RSS
Nesne kavramı
canalpay (Moderatör) #1
Kullanıcı başlığı: Can Alpay Çiftçi
Üye Tem 2009 tarihinden beri · 1133 mesaj · Konum: İzmir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Konu adı: Nesne kavramı
Nesneler bir çok dilin önemli konularından biri. Ama nesne ile çok tanışmamış biri için nesne'nin nasıl kullanıldığını bir ders biçiminde anlatabilir misiniz ?
D'deki nesneler ayrıca Java programlama dilindekine(Doğal olarak php'yede) benziyor mu ?
acehreli (Moderatör) #2
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Ben bu seferlik kısaca anlatacağım...

Sınıf: Bir kaç (aslında sıfır veya daha fazla) değişkenin bir araya getirilerek bir tür oluşturulmasına, ve o tür üzerindeki işlemlerin de aynı program yapısı içinde tanımlanmasına sınıf denir. Bundan bir adet tanımlanır. Örnek: Dikdörtgen...

Nesne: Nasıl int türünden oluşturulan hava_sıcaklığı gibi bir program yapısına değişken deniyorsa, bir sınıf türünden oluşturulan program yapısına da nesne denir.

Örneğin bir oyun kağıdı destesini 52 tane Dikdörtgen nesnesinden oluşturabiliriz. (Çok kötü bir örnek oldu... :D)

class Dikdörtgen
{
private:
 
    uint uzunluk_;
    uint genişlik_;
 
public:
 
    this(uint uzunluk, uint genişlik)
    {
        uzunluk_ = uzunluk;
        genişlik_ = genişlik;
    }
 
    uint alan() const
    {
        return uzunluk_ * genişlik_;
    }
}
 
import std.stdio;
 
void main()
{
    Dikdörtgen[52] deste = new Dikdörtgen(10, 7);
 
    writeln("Yirmincisinin alanı: ", deste[19].alan(), " santimetre kare");
 
    // Tabii tek başına da oluşturabiliriz:
    auto pencere = new Dikdörtgen(120, 80);
    writeln("Penceremin alanı: ", pencere.alan(), "cm\²");
}

Ali
Avatar
mert #3
Üye Ara 2010 tarihinden beri · 194 mesaj
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Konu adı: Bellekte nerede tutulurlar?
Sınıf ve yapı örneklemeleri yapabilmek için bir kaç zamandır ağ üzerinde dolaşıyorum. Bir c# makalesinde sınıflar ve yapılar arasındaki önemli farklara değinilirken şunlar ifade edilmiş:
Yapılar, sınıflar ile büyük benzerleklik gösterirler. Sınıf gibi tanımlanırlar. Hatta sınıflar gibi, özellikler,metodlar,veriler, yapıcılar vb... içerebilirler. Buna karşın sınıflar ile yapılar arasında çok önemli farklılıklar vardır.

Herşeyden önce en önemli fark, yapıların değer türü olması ve sınıfların referans türü olmasıdır. Sınıflar referans türünden oldukları için, bellekte tutuluş biçimleri değer türlerine göre daha farklıdır. Referans tiplerinin sahip olduğu veriler belleğin öbek(heap) adı verilen tarafında tutulurken, referansın adı stack(yığın) da tutulur ve öbekteki verilerin bulunduğu adresi işaret eder. Ancak değer türleri belleğin stack denilen kısmında tutulurlar. Aşağıdaki şekil ile konuyu daha net canlandırabiliriz.

Ne zaman yapı ne zaman sınıf kullanmalıyız? Özellikle metodlara veriler aktarırken bu verileri sınıf içerisinde tanımladığımızda, tüm veriler metoda aktarılacağını sadece bu verilerin öbekteki başlangıç adresi aktarılır ve ilgili parametrenin de bu adresteki verilere işaret etmesi sağlanmış olur. Böylece büyük boyutlu verileri stack'ta kopyalayarak gereksiz miktarda bellek harcanmasının önüne geçilmiş olunur. Ancak küçük boyutlarda veriler ile çalışırken bu verileri sınıflar içerisinde kullandığımızda bu kezde gereksiz yere bellek kullanıldığı öbek şişer ve performans düşer. Bu konudaki uzman görüş 16 byte'tan küçük veriler için yapıların kullanılması, 16 byte'tan büyük veriler için ise sınıfların kullanılmasıdır.

Bu durum D içinde geçerli midir acaba ve eğer geçerliyse bizler kodlarımızı tasarlarken nelere dikkat etmeliyiz?

mert
mert
canalpay (Moderatör) #4
Kullanıcı başlığı: Can Alpay Çiftçi
Üye Tem 2009 tarihinden beri · 1133 mesaj · Konum: İzmir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Genel olarak doğru.

Ben sınıfları daha çok iş yapmak içim kullanıyorum. Yapıları ise tür olarak kullanıyorum. Yani benim için yapı daha karmaşık bir string gibi tür iken, sınıf ise daha karmaşık işlev topluluğu.

Yapılar, sınıflar ile büyük benzerleklik gösterirler. Sınıf gibi tanımlanırlar. Hatta sınıflar gibi, özellikler,metodlar,veriler, yapıcılar vb... içerebilirler. Buna karşın sınıflar ile yapılar arasında çok önemli farklılıklar vardır.

Herşeyden önce en önemli fark, yapıların değer türü olması ve sınıfların referans türü olmasıdır.

Bu D içinde olduğu gibi doğru. Geri kalanıda ben bilmiyorum ancak doğrudur. Çoğu zaman ikiside ihtiyacı yeterince karşılıyor. Ancak ben kendime tür tasarlayacak isem struct(yapı), işlevler topluluğu tanımlayacak isem sınıf kullanırım.
erdem (Moderatör) #5
Üye Tem 2009 tarihinden beri · 978 mesaj · Konum: Eskişehir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Sınıf nesneleri Andrei'nin ifadesiyle kişiliği olan elemanlardır :)

class objects are entities, meaning that they have "personality"

Sınıf nesneleri her zaman çöp toplama mekanizması tarafından yönetilen heap (yığın) üzerinde oluşur.
class A 
{
    int x = 42;
}
 
unittest 
{
    auto a1 = new A;
    assert(a1.x == 42);
    auto a2 = a1;       // yeni bir A nesnesi oluşturulmaz sadece A nesnesi
                        // a2 tarafından da gösterilmeye başlanır
    assert(a1 is a2);   
    assert(a1 == a2);
    
    a2.x = 100;
    assert(a1.x == 100);
}

Ayrıca daha önce de konuştuğumuz gibi buradaki a1 ve a2 aslında birer referanstır. a2'yi a1'e eşitlediğimizde aslında yeni bir nesne oluşturulmaz sadece aynı nesne iki referans tarafından gösterilmeye başlanır. Çöp toplama mekanizması ancak bir nesneye bağlanmış referans kalmadığından emin olduğunda o nesne için ayrılan bellek alanını geri verir.

Herhangi bir nesneye bağlanmamış bir referans null'dur. Bu yüzden sınıfın varsayılan kurucusu çalıştığında  sınıfa ait referanslar da null değer alır.
unittest 
{
    A a;
 
    assert(a is null);
    a = new A;
    assert(a !is null);
    a = null;
    assert(a is null);
    a = A.init;
    assert(a is null)}
Bunu yukardaki birim testinde de görebiliriz.
Bu mesaj 2 defa değişti; son değiştiren: erdem; zaman: 2011-05-13, 13:27.
Değişiklik nedeni: birden fazla nesneyi gösteren referanslarla ilgili kısmı çıkarttım çünkü Ali bey duruma göre değişir demişti :)
Avatar
mert #6
Üye Ara 2010 tarihinden beri · 194 mesaj
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Yeniden merhaba;
Sınıf nesneleri her zaman çöp toplama mekanizması tarafından yönetilen heap (yığın) üzerinde oluşur.
Demişsiniz. Ancak Erdem; http://ddili.org/makale/bellek.html adresindeki makalede ise şöyle bir başlık var "Sınıf Nesnelerini Yığıtta Oluşturmak" Oradan alıntılıyorum;
Sınıf nesneleri normalde çöp toplamalı bellekte yer alırlar. Bazı durumlarda ise program yığıtında oluşturulurlar:

    * fonksiyonlarda yerel tanımlandıklarında
    * new ile oluşturulduklarında
    * new argümansız kullanıldığında (yine de kurucu için argüman kullanılabilir)
    * scope depolama türü ile tanımlandıklarında

Anladığım kadarıyla sınıflar her zaman yığın içerisinde oluşmuyorlar. Genellikle daha doğru bir terim galiba ne dersiniz? Benim yakınlaşmaya çalıştığım asıl mesele şu aslında; çözümlemelerimde sınıfı  ve yapıları en hızlı ve en az maliyetle nasıl tasarlar ve kullanabilirim. Yani İlk alıntıladığım "Bellekte nerede tutulurlar" bölümündeki şu tür ifadeler;
Bu konudaki uzman görüş 16 byte'tan küçük veriler için yapıların kullanılması, 16 byte'tan büyük veriler için ise sınıfların kullanılmasıdır.
Bizim için ne kadar doğrudur? Diyelim ki benim bir .txt dosyam var. O dosyamın içinde ise 10 bin adet int tipinde değişkenim var. Bu dosyadan verilerimi çeken bir yapı tasarlasam bu yapının bana sağladığı dizilerle işlem yapmamın hızı ve bellek kullanımı hakkında neleri öngörebilmem gerekir?
mert
Bu mesaj mert tarafından değiştirildi; zaman: 2011-05-13, 14:17.
erdem (Moderatör) #7
Üye Tem 2009 tarihinden beri · 978 mesaj · Konum: Eskişehir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
mert:
Sınıf nesneleri her zaman çöp toplama mekanizması tarafından yönetilen heap (yığın) üzerinde oluşur.
http://ddili.org/makale/bellek.html adresindeki makalede ise şöyle bir başlık var

Emin değilim ama o makale D1 zamanından kalmış olmalı.

Sınıf nesneleri normalde çöp toplamalı bellekte yer alırlar. Bazı durumlarda ise program yığıtında oluşturulurlar:
    * new ile oluşturulduklarında
    * new argümansız kullanıldığında (yine de kurucu için argüman kullanılabilir)

scope konusunu tam bilmiyorum ama new ile oluşturulduklarında yığın(heap) üzerinde bellek ayrılır. Üstteki birim testinde görüldüğü gibi A a şeklinde kullanırsanız sadece null bir referans oluşturulur.
erdem (Moderatör) #8
Üye Tem 2009 tarihinden beri · 978 mesaj · Konum: Eskişehir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
erdem:
Emin değilim ama o makale D1 zamanından kalmış olmalı.

Aslında kalmamış :)

scope konusunu tam bilmiyorum ama new ile oluşturulduklarında yığın(heap) üzerinde bellek ayrılır. Üstteki birim testinde görüldüğü gibi A a şeklinde kullanırsanız sadece null bir referans oluşturulur.

Bu ifade doğru.

Sınıf nesneleri normalde çöp toplamalı bellekte yer alırlar. Bazı durumlarda ise program yığıtında oluşturulurlar:
    * new ile oluşturulduklarında

Burada yanlız bir yazım yanlışı olmuş sanırım. Sanırım doğrusu new ile bellekten yer ayrıldığında olacak. Yani şunun gibi bir şey:
import std.c.stdlib;
import std.outofmemory;
import std.gc;
 
class Foo
{
    new(size_t sz)
    {
    void* p;
 
    p = std.c.stdlib.malloc(sz);
    if (!p)
        throw new OutOfMemoryException();
    std.gc.addRange(p, p + sz);
    return p;
    }
 
    delete(void* p)
    {
    if (p)
    {   std.gc.removeRange(p);
        std.c.stdlib.free(p);
    }
    }
}
Sınıf nesneleri her zaman çöp toplama mekanizması tarafından yönetilen heap (yığın) üzerinde oluşur.

TDPL'de aynen böyle bir ifade var:

All class-based objects are dynamically allocated-unlike in C++, there is no way to allocate a class object on the stack.

Sanırım bu ifadenin doğrusu.

There is no built-in way to allocate a class object on the stack

D'nin kendi sunduğu olanaklarla bir sınıf nesnesi için yığıt (stack) üzerinde bellek alanı ayırmanın bir yolu yok olacak.

Gene de ben de tam emin olamadım en iyisi Ali beye soralım :)

Bir de bir ek daha. Makalenin orijinalinde:

http://www.digitalmars.com/d/2.0/memory.html#stackclass

Allocating Class Instances On The Stack

Yani sınıfa ait örnekler için yığıt üzerinde bellek ayırmak diyor.

Allocating Class Objects On The Stack

Yani sınıf nesneleri için yığıt üzerinde bellek ayırmak demiyor. Ya da ben yanlış anlıyorum :)

Örneğin bir sınıfa ait örnek bir tanedir. Ama sınıf nesnelerini istediğimiz kadar oluşturabiliriz.
Bu mesaj 2 defa değişti; son değiştiren: erdem; zaman: 2011-05-13, 15:25.
Değişiklik nedeni: ek
acehreli (Moderatör) #9
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Yanıtlanan mesaj #3
Can ve Erdem zaten güzelce yanıtlamışlar. Ben tekrarlıyorum.

Öncelikle, dil kavramları ile gerçekleştirme kavramlarının birbirlerinden ayrı tutulmaları gerektiğini söylemek istiyorum. Bartosz Milewski gibi bir ustanın benimle aynı fikirde olduğunu biliyorum. Walter ve Andrei'nin bile dil olanaklarını tanımlarken 'stack'ten bahsetmelerinin yanlış olduğunu düşünüyorum.

İlgili olarak, bazen de forumlara katılan D'ciler yeni başlayanlara D'nin dilimlerini anlatırken "nasıl tanımlandığını bir görsen hemen anlarsın" diye açıklıyorlar. "Bak şu adreste bir resim var, içindeki gösterge elemanları nasıl da gösteriyor" diyorlar. O da yanlış. Dil, bu gibi alt düzey kavramlardan soyutlanmıştır. Hiçbir yeni başlayan, dilimleri anlamak için derleyicinin perde arkasında ne işlerle uğraştığını bilmek zorunda değildir ve olmamalıdır. (Ayrıca, eğer dilim kavramını gerçekleştirmesine bakmadan anlayamıyorsak o dilde bir hata var demektir. D'nin dilimleri bir noktada öyleydi.)

Dilim bir kavramdır. Dilim işlemlerinin sonuçlarında ne olacağı bellidir. Örneğin dilim[j] işlemi anlatılırken hiç kimse "derleyici perde arkasındaki eleman göstergesine j'yi ekler ve sonuçta elde ettiği adresteki nesneye * işleci ile eriştirir" demez. Doğrusu, "j'inci elemana erişim sağlar"dır. Bunun nasıl gerçekleştirildiği dilden bağımsız bir olaydır.

Program yığıtı (program stack) konusu da böyledir.

C++ standardı bu konuda çok başarılıdır: yerel değişkenlerin yığıtta bulunmaları gerektiğinden filan bahsetmez. C++ standardının gözünde örneğin "yaşamı otomatik olarak idare edilen" değişkenler vardır. O çeşit değişkenlerin yerlerinin mikro işlemcinin 'stack pointer' düzeneği ile yıldırım hızında ayrılıyor olmaları ve yine aynı hızda geri veriliyor olmaları dili ilgilendirmez. Evet, o nesneler orada yaşarlar ama C++ tanımı açısından bunun sözü edilmez. (Walter ve Andrei'nin de C++ standardı kadar özenli olmalarını umardım.)

Karşıt örnekler: ben iki adet 'stack pointer'ı olan bir mikro işlemci tasarlasam Walter'a gidip otomatik nesneler için hangisini kullanacağımı mı sormalıyım? Tabii ki olamaz. Veya 'stack pointer'ı olmayan mikro işlemci tasarlasam onun için bir D derleyicisi yazamayacak mıyım? Tabii ki olmaz.

Tabii gerçekte bütün (modern?) mikro işlemcilerde 'stack pointer' diye bir yazmaç (register) vardır ve bütün otomatik değişkenler onun gösterdiği yerde yaşarlar. İşte bu gerçek yüzünden "bu nesnenin yaşamı otomatik olarak sonlandırılır" yerine "bu nesne stack'te yaşar" deniyor. Ama denmemeli. Çünkü yeni başlayan birisine dilin gözünde nesne yaşamlarını anlatmak kolaydır ve bunları bilmek doğru programlar üretmek için şarttır; ama 'stack'ten bahsetmeye başlayınca ya yeni başlayanların mikro işlemci ve derleyici teknolojilerini de bilmelerini beklemiş oluyoruz, ya da kafalarına yarım doğrular sokuyoruz.

Yapılar, sınıflar ile büyük benzerleklik gösterirler. Sınıf gibi tanımlanırlar. Hatta sınıflar gibi, özellikler,metodlar,veriler, yapıcılar vb... içerebilirler. Buna karşın sınıflar ile yapılar arasında çok önemli farklılıklar vardır.

Herşeyden önce en önemli fark, yapıların değer türü olması ve sınıfların referans türü olmasıdır.

Aynen D'de de öyle.

Sınıflar referans türünden oldukları için, bellekte tutuluş biçimleri değer türlerine göre daha farklıdır.

O kadarı da doğru çünkü sınıflarda değişken ile nesne diye iki kavram var. Nesne her zaman için isimsizdir; programda isimli olarak gördüğümüz, değişkendir.

Referans tiplerinin sahip olduğu veriler belleğin öbek(heap) adı verilen tarafında tutulurken, referansın adı stack(yığın) da tutulur ve öbekteki verilerin bulunduğu adresi işaret eder. Ancak değer türleri belleğin stack denilen kısmında tutulurlar.

C# bilmiyorum ama o söylenenler D için ancak bazı durumlarda geçerlidir. Çürütelim... Şu örnekteki üye nerededir?

struct Yapı
{}
 
class Sınıf
{
    Yapı üye;           // <-- nerede?
    BaşkaSınıf üye2;
}

Yapı olduğu için 'stack'te midir? Yoksa bir sınıfın üyesi olduğu için öbekte midir? Peki şuradaki int ve hatta onu sarmalayan yapı nerededir?

struct Yapı
{
    int i;
}

İstersek 'stack'tedir, istersek öbektedir:

import std.stdio;
 
struct Yapı
{
    int i;
}
 
void main()
{
    auto nesne0 = Yapı();           // 'stack'te
    auto nesne1 = new Yapı();       // öbekte
 
    writeln(&nesne0.i);
    writeln(&nesne1.i);
}

Çıktısından anlaşıldığı gibi benim ortamımda iki nesne bambaşka bölgelerde yaşıyorlar:

FF88C8E8
F7428E20


(FF ve F7 ile başlayan bölgeler birbilerinden çok uzaktalar.)

Ne zaman yapı ne zaman sınıf kullanmalıyız?

C# için ne yanıt verileceğini bilemem.

D'de en genel olarak, tür bir değer ifade ediyorsa yapı kullanılır. Örneğin nesnelerin kimlikleri önemli değilse... Bir Piyon nesnesi diğerinden farklı mıdır? Programına göre değişir. Eğer Piyon programda değer olarak kullanılıyorsa struct olmalıdır.

Daha özel durumlarda çok şekilliği düşünmek gerekir. SatrançTaşı diye bir arayüz varsa ve her taş kendi bildiği davranışlara sahipse o zaman Piyon bir sınıf olmalıdır. Örneğin bir satranç taşına aslında ne olduğunu bilmeden "şu kareye gitmen yasal mıdır" diyebiliyorsak o zaman bütün satranç taşları sınıf türleridir.

Öncelikle böyle anlamsal açılardan yaklaşmak gerekir. Nesnelerin nerede durdukları ve aşağıdaki gibi performans sorunları ikincil önemdedir.

Özellikle metodlara veriler aktarırken bu verileri sınıf içerisinde tanımladığımızda, tüm veriler metoda aktarılacağını sadece bu verilerin öbekteki başlangıç adresi aktarılır ve ilgili parametrenin de bu adresteki verilere işaret etmesi sağlanmış olur.

D dilinde: "işlevlere sınıf değişkenlerinin kopyaları aktarılır; isimsiz sınıf nesnesi o iki kopya tarafından eriştirilmeye devam eder." Gösterelim:

import std.stdio;
 
class Sınıf
{
    int i;
}
 
void foo(Sınıf değişken)
{
    writeln("foo içinde değişken   : ", &değişken);
    writeln("foo içinde değişken.i : ", &(değişken.i));
}
 
void main()
{
    auto değişken = new Sınıf;
 
    writeln("main içinde değişken  : ", &değişken);
    writeln("main içinde değişken.i: ", &(değişken.i));
 
    foo(değişken);
}

Çıktısından görüldüğü gibi iki farklı değişkenden söz ediyoruz ama tek bir nesne var. i üyesinin adresi aynı:

main içinde değişken  : FFC55B4C
main içinde değişken.i: F736BE28
foo içinde değişken   : FFC55B38
foo içinde değişken.i : F736BE28


Böylece büyük boyutlu verileri stack'ta kopyalayarak gereksiz miktarda bellek harcanmasının önüne geçilmiş olunur.

Bellek harcanmamasını anlarım ama onun referans türlerinin nedeni olarak gösterilmesi yanlış. Sınıf nesnelerinin kopyalanmamalarının nedeni, kimlikleri olan nesneler olmalarıdır. Sınıf nesneleri yeni kimliğe sahip olmasınlar diye kopyalanmazlar.

Tekrar altını çiziyorum: İşleve sınıf türü gönderilirken

  • değişken kopyalanır

  • nesne kopyalanmaz

Kaldı ki, D'de gösterge diye bir kavram var. Eğer istersek korkunç derecede büyük yapıları bile gösterge ile geçirebiliriz:

struct Yapı
{
    // ... korkunç büyük olsun ...
}
 
void foo(Yapı * nesne)
{
    // ...
}
 
void main()
{
    auto nesne = Yapı();
    foo(&nesne);        // <-- değer türü olduğu halde kopyalamadık
}

Gördüğünüz gibi, değer ve referans türü seçiminde veri büyüklüğünün hiçbir önemi yoktur.

Ancak küçük boyutlarda veriler ile çalışırken bu verileri sınıflar içerisinde kullandığımızda bu kezde gereksiz yere bellek kullanıldığı öbek şişer ve performans düşer. Bu konudaki uzman görüş 16 byte'tan küçük veriler için yapıların kullanılması, 16 byte'tan büyük veriler için ise sınıfların kullanılmasıdır.

Tekrar söylüyorum; C# bilmiyorum ama eğer aynı şeyi C, C++, veya D için de söyleyeceklerse o uzmanlarla tanışmak isterdim. :) Eğer programlarınızı bu tür kaygılara göre yazacağımızı düşünmeye başladıysanız hemen durun! Türlerin yapı veya sınıf olacaklarına büyüklüklerine bakarak karar vermek programcılık cinayetidir.

Ayrıca mantık dışı ve yarım yamalak bir öğüttür. Düşünelim: kütüphanemizdeki 16 baytlık bir türü bugün yapı olarak tasarlamışken iki ay sonra 20 bayta çıktı diye sınıf mı yapacağız? Kullanıcı kodları ne olacak?

Ayrıca bunun tarih öncesinden beri geçerli olan bir adı bile var: "premature optimization is the root of all evil" ("Gereğinden önce yapılan eniyileştirme her kötülüğün anasıdır").

Bu durum D içinde geçerli midir acaba ve eğer geçerliyse bizler kodlarımızı tasarlarken nelere dikkat etmeliyiz?

D için geçerli değildir ve eğer C#'ta gösterge kavramı varsa orada da geçerli olduğunu sanmıyorum.

Ali
erdem (Moderatör) #10
Üye Tem 2009 tarihinden beri · 978 mesaj · Konum: Eskişehir
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Yanıtlanan mesaj #8
Sınıf nesneleri normalde çöp toplamalı bellekte yer alırlar. Bazı durumlarda ise program yığıtında oluşturulurlar:
    * new ile oluşturulduklarında

Ben bu ifade ile ne anlatılmak istendiğini anlamadım  :huh:
 
All class-based objects are dynamically allocated-unlike in C++, there is no way to allocate a class object on the stack.

Bununla ters düşmüyor mu :)

Bir de bir ek daha. Makalenin orijinalinde:

http://www.digitalmars.com/d/2.0/memory.html#stackclass

Allocating Class Instances On The Stack

Yani sınıfa ait örnekler için yığıt üzerinde bellek ayırmak diyor.

Allocating Class Objects On The Stack

Yani sınıf nesneleri için yığıt üzerinde bellek ayırmak demiyor. Ya da ben yanlış anlıyorum :)

Örneğin bir sınıfa ait örnek bir tanedir. Ama sınıf nesnelerini istediğimiz kadar oluşturabiliriz.

Doğru mu anlamışım acaba. Yani:

Class instances are normally allocated on the garbage collected heap. However, if they are allocated using new then they are allocated on the stack.


Burada sınıf nesnelerini kasdetmiyor sınıfa ait örnekleri kasdediyor değil mi. Ayrıca new kullanarak yer ayırmak derken sınıfın kurucu ve bozucusundaki new ve delete anahtar kelimelerini kullanarak elle bellek yönetimini yapmayı kasdediyor değil mi.

erdem:
class A 
{
    int x = 42;
}
 
unittest 
{
    auto a1 = new A;
    assert(a1.x == 42);
    auto a2 = a1;       // yeni bir A nesnesi oluşturulmaz sadece A nesnesi
                        // a2 tarafından da gösterilmeye başlanır
// ...
}

Benim değiştirdiğim kısım şu şekildeydi:

Sınıflar kişiliği olan elemanlar oldukları için kopyalanmamaları gerekir. Yukarıdaki örnekte olduğu gibi. Kopyalanmamaları gerekiyor derken aynı nesneyi gösteren birden fazla referans olmasına izin vermemeliyiz. Çünkü beklenmeyen  bir şekilde referans değiştiği zaman uygulamanın bir yerinde olan sürpriz değişiklikler diğer tarafına da yansıyabilir.

demiştim. Ama siz aynı nesneyi gösteren birden fazla referans olması konusunda - duruma göre değişebilir - demiştiniz. O yüzden ben de bunu hatırlayarak mesajı değiştirdim :)
Avatar
mert #11
Üye Ara 2010 tarihinden beri · 194 mesaj
Grup üyelikleri: Üyeler
Profili göster · Bu konuya bağlantı
Gördüğünüz gibi, değer ve referans türü seçiminde veri büyüklüğünün hiçbir önemi yoktur.

Bir "Yeni başlayan" olarak söyleyeyim; Yukarıdaki yeterince açık bir ifade. Burada Can'ın a ifade ettiği gibi kendi algoritmamıza göre kendi tercihlerimizi yapacağız ve zaman içerisinde bu seçimler oturacak. Şu ifade çok daha aydınlatıcı görünüyor
Bir Piyon nesnesi diğerinden farklı mıdır? Programına göre değişir. Eğer Piyon programda değer olarak kullanılıyorsa struct olmalıdır.

Daha özel durumlarda çok şekilliği düşünmek gerekir
Bolca pratik yapmak demek; Katılıyorum.
Eğer programlarınızı bu tür kaygılara göre yazacağımızı düşünmeye başladıysanız hemen durun! Türlerin yapı veya sınıf olacaklarına büyüklüklerine bakarak karar vermek programcılık cinayetidir.

Acemilik kaygıyı da beraberinde getiriyor. Bu tür yanılgılara düşmek de öğrenmenin bir parçası aslında. Yapı ve sınıflar yeniden çalışılacak demektir bu ki çalışılmakta hAlA.
D için geçerli değildir ve eğer C#'ta gösterge kavramı varsa orada da geçerli olduğunu sanmıyorum.

Yanıtla beraber makale değerinde bir açıklama da almış oldum: Teşekkürler.
mert
acehreli (Moderatör) #12
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Yanıtlanan mesaj #10
erdem on 2011-05-14, 04:48:
Sınıf nesneleri normalde çöp toplamalı bellekte yer alırlar. Bazı durumlarda ise program yığıtında oluşturulurlar:
    * new ile oluşturulduklarında

Ben bu ifade ile ne anlatılmak istendiğini anlamadım  :huh:

Digital Mars'taki açıklamalar açık değil. :p Orada listelenen maddelerin ve ile bağlanmaları gerektiğini düşünüyorum:

D spec:
Class instances are normally allocated on the garbage collected heap. However, if they:

    * are allocated as local symbols in a function
    * are allocated using new
    * use new with no arguments (constructor arguments are allowed)
    * have the scope storage class

Yani; yerel olmalı, new ile ayrılmış olmalı, new'ün parametre almayanı çağrılmış olmalı, ve scope depolama türünden olmalı... diyecektim ki bunula ilgili bir hata aradım ve buldum:

  http://d.puremagic.com/issues/show_bug.cgi?id=1521

Neredeyse 4 senelik hatada aynen benim söylediğim sorgulanmış: O sayfadaki maddeler ve ile mi bağlanıyorlar, veya ile mi?

Bence bu konuyu şu anlayışla geride bırakalım: Sınıf nesnesi öbekte oluşturulur; sınıf değişkeni ise tanımlandığı duruma göre değişir. Bunun benzerini çok yazdık ve gördük ama bir programcık daha:

import std.stdio;
 
struct Yapı
{
    int i;
}
 
class Sınıf
{
    int i;
}
 
scope class scope_Sınıfı
{
    int i;
}
 
void main()
{
    auto y0 = Yapı();
    auto y1 = new Yapı;
 
    auto s0 = new Sınıf;
    scope s1 = new Sınıf;
 
    // auto s2 = new scope_Sınıfı;  // derleme hatası
    scope s3 = new scope_Sınıfı;
 
    bilgiVer("normal yapı",            &y0.i);
    bilgiVer("new ile yapı",           &y1.i);
    bilgiVer("normal sınıf",           &s0.i);
    bilgiVer("normal sınıf scope ile", &s1.i);
    bilgiVer("scope sınıfı scope ile", &s3.i);
}
 
void bilgiVer(T)(dstring açıklama, T adres)
{
    writefln("%30s: %s", açıklama, adres);
}

Çıktısından anlaşıldığı gibi, yapı ve sınıf nesneleri yığıtta da öbekte de oluşturulabiliyorlar:

                   normal yapı: FFEA7A68
                  new ile yapı: F7372E20
                  normal sınıf: F7372E18
        normal sınıf scope ile: FFEA7A7C
        scope sınıfı scope ile: FFEA7A8C


All class-based objects are dynamically allocated-unlike in C++, there is no way to allocate a class object on the stack.

Bununla ters düşmüyor mu :)

Ters düşüyor. O sayfadaki açıklama hatalı.

Bir de bir ek daha. Makalenin orijinalinde:

http://www.digitalmars.com/d/2.0/memory.html#stackclass

Allocating Class Instances On The Stack

Yani sınıfa ait örnekler için yığıt üzerinde bellek ayırmak diyor.

Allocating Class Objects On The Stack

Yani sınıf nesneleri için yığıt üzerinde bellek ayırmak demiyor. Ya da ben yanlış anlıyorum :)

Orada "instance" ile "object" aynı anlamda kullanılmış.

erdem:
class A 
{
    int x = 42;
}
 
unittest 
{
    auto a1 = new A;
    assert(a1.x == 42);
    auto a2 = a1;       // yeni bir A nesnesi oluşturulmaz sadece A nesnesi
                        // a2 tarafından da gösterilmeye başlanır
// ...
}

Benim değiştirdiğim kısım şu şekildeydi:

Sınıflar kişiliği olan elemanlar oldukları için kopyalanmamaları gerekir.

Öyle çok kısıtlayıcı oluyor. Duruma göre nesneleri özellikle kopyalamak zorunda kalabiliriz. Hatta kopyalamazsak hatalı olabilir. Bunun benzerini dizilerde görebiliriz: Referans türleri oldukları halde .dup ile kopyalayabiliyoruz ve duruma göre kopyalamamız gerekiyor.

Yukarıdaki örnekte olduğu gibi. Kopyalanmamaları gerekiyor derken aynı nesneyi gösteren birden fazla referans olmasına izin vermemeliyiz.

Ama o zaman nesnelerin kişilikli olmalarına ters düşmüş oluruz. Duruma göre tam tersi olarak birden fazla referansın aynı nesneyi göstermesini isteriz. Örneğin bir çatı, o çatıyı kullanan kodların oluşturduğu bir sınıf değişkenini (referansını) alabilir ve ileride etkileşmek için saklayabilir. Nesneyi oluşturan kodlar da aynı nedenle saklayabilirler.

Sonuçta aynı nesneye erişim sağlayan değişken (referans) hem çatının içindeki bir dizide duruyor olabilir, hem de onu oluşturan tarafta örneğin bir eşleme tablosunda... Çok doğal bir durum. (Şimdilerde kullandığımız bir C++ çatısında aynen böyle oluyor.)

Çünkü beklenmeyen  bir şekilde referans değiştiği zaman uygulamanın bir yerinde olan sürpriz değişiklikler diğer tarafına da yansıyabilir.

Kabul ama o zaman programda bir hata var demektir. Eğer bir tarafındaki değişikliklerin öteki tarafı etkilemesini istemiyorsak .dup ile kopyalamış olmalıyızdır.

Ali
acehreli (Moderatör) #13
Kullanıcı başlığı: Ali Çehreli
Üye Haz 2009 tarihinden beri · 4527 mesaj
Grup üyelikleri: Genel Moderatörler, Üyeler
Profili göster · Bu konuya bağlantı
Yanıtlanan mesaj #11
mert:
Bolca pratik yapmak demek; Katılıyorum.

Başka yolu yok galiba. Ancak deneye yanıla öğrenebiliyoruz. :)

Acemilik kaygıyı da beraberinde getiriyor. Bu tür yanılgılara düşmek de öğrenmenin bir parçası aslında.

Çok doğru! Acemiyken, yazılmış olan cümlelerden hangisinin ne kadar önemli olduğu bile anlaşılamıyor. Bir çok cümle, kavram, "yapın" öğüdü, "yapmayın" öğüdü, teknik bilgi, şaka, vs. :) Bazen aslında önemsiz olan bir bölümü, bildiklerimize uydu diye anladığımız için diğerlerinden daha üstte tutuyoruz.

Son olarak, yapı-sınıf seçimi konusunda çok kaba bir ilke: Öncelikle yapı düşünün; yetersiz kalırsa veya dertli olmaya başlarsa sınıf olmalıymış demektir.

Yapı olarak tanımlarsanız ve o türün çeşitleri olacaksa, örneğin çeşitli hayvanları ifade edecekse, o zaman C'de çok uygulandığı gibi bir enum'dan yardım almanız gerekir. Nesne yönelimli programlamanın farkını gösteren klasik örnek:

enum HayvanÇeşidi { kedi, köpek, at }
 
struct Hayvan
{
    HayvanÇeşidi çeşit;
 
    void şarkıSöyle()
    {
        final switch (çeşit) {
        case HayvanÇeşidi.kedi:
            writeln("miyav");
            break;
 
        case HayvanÇeşidi.köpek:
            writeln("hav");
            break;
 
        case HayvanÇeşidi.at:
            writeln("iii");
            break;
        }
    }
}

Yukarıda görüldüğü gibi yapı da olur. Ama Hayvan daha karmaşık olmaya başladıkça, örneğin üye işlevlerin sayısı arttıkça o yöntem daha dertli hale gelir. Onun için belki de baştan sınıf olmalıymıştır (güzel Türkçemiz! :-p):

Aslında karşıt ilke de olur: öncelik her şeyi sınıf olarak düşünün, uygunsuz kaçmaya başlarsa yapı olarak değiştirirsiniz.

Belki daha güzel bir ilke: çeşidine göre farklı davranacak olan üye işlevi varsa sınıf olsun. Örneğin eğer yalnızca dikdörtgenlerle ilgileniyorsak ve bu dikdörtgenlerin yalnızca renk, en, boy, vs. gibi nitelikleri farklıysa; çizilmeleri, alan hesapları, vs. hep aynıdır. O zaman Dikdörtgen türünü yapı olarak tasarlamak uygundur. Ama farklı şekiller varsa, örneğin alan hesapları farklı olacağından onları sınıf yapmak uygundur.

(Zaten çok söylenmiş olan şeyleri tekrarladım. :))

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:
Forum: Ders Arası RSS
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-11-18, 22:39:23 (UTC -08:00)