Nesne dilimleme nedir?


Yanıtlar:


609

"Dilimleme", türetilmiş bir sınıfın bir nesnesini bir temel sınıf örneğine atadığınız ve böylece bilginin bir kısmını kaybettiğiniz yerdir - bazıları "dilimlenir".

Örneğin,

class A {
   int foo;
};

class B : public A {
   int bar;
};

Yani bir nesnenin Biki veri üyesi vardır foove bar.

Eğer bunu yazacak olsaydınız:

B b;

A a = b;

Sonra büye hakkında bilgi barkaybolur a.


66
Çok bilgilendirici, ancak yöntem çağrıları sırasında dilimlemenin nasıl gerçekleştiğine ilişkin bir örnek için stackoverflow.com/questions/274626#274636 adresine bakın (bu, tehlikeyi düz atama örneğinden biraz daha iyi vurgulamaktadır).
Blair Conrad

55
İlginç. C ++ 'da 15 yıldır programlama yapıyorum ve bu sorun hiç aklıma gelmedi, çünkü nesneleri her zaman bir verimlilik ve kişisel stil meselesi olarak referans olarak geçtim. İyi alışkanlıkların size nasıl yardımcı olabileceğini göstermeye gider.
Karl Bielefeldt

10
@Felix Teşekkürler ama (bir işaretçi aritmetik değil çünkü) geri döküm sanmıyorum A a = b; a, şimdi Akopya olan tür nesnedir B::foo. Sanırım şimdi geri dökmek yanlış olur.

37
Bu "dilimleme" veya en azından iyi huylu bir çeşidi değildir. Gerçek sorun yaparsanız oluşur B b1; B b2; A& b2_ref = b2; b2 = b1. Sen Kopyaladığınız düşünebilir b1için b2ama sen var! Bir kopyaladığınız kısmı arasında b1hiç b2(bir parçası b1olduğu Bmiras A) ve diğer bölümlerini sol b2değişmeden. b2Şimdi bir kaç bitten oluşan frankeştayn yaratıktır b1bazı parçaları izledi b2. Ihh! Aşağı oylama, çünkü cevabın çok yanıltıcı olduğunu düşünüyorum.
fgp

24
@fgp Yorumunuz B b1; B b2; A& b2_ref = b2; b2_ref = b1" Gerçek sorun, siz " sanal olmayan atama işlecine sahip bir sınıftan türetmeniz durumunda ortaya çıkacaktır . AHatta türetme amaçlı mıdır ? Sanal işlevi yoktur. Bir türden türetiyorsanız, üye işlevlerinin çağrılabileceği gerçeğiyle uğraşmak zorundasınız!
curiousguy

510

Buradaki cevapların çoğu dilimleme ile ilgili asıl sorunun ne olduğunu açıklayamıyor. Hain olanları değil, sadece iyi huylu dilimleme vakalarını açıklarlar. Diğer cevaplar gibi, iki sınıfla uğraştığınızı Ave (herkese açık) Bnereden Bkaynaklandığını varsayın A.

Bu durumda, C ++ size bir örneğini geçmesine izin verir Betmek A'ın atama operatörü (ve ayrıca kopya yapıcısına). Bunun nedeni , atama işleçlerinin ve kopya oluşturucularının argümanlarının olmasını beklediği şey olan bir örneği a'ya Bdönüştürülebilir const A&.

İyi huylu dava

B b;
A a = b;

Orada kötü bir şey olmaz - bunun bir örneği Aolan bir kopyasını istediniz Bve tam olarak bunu elde edersiniz. Tabii, abazı büyelerini içermeyecek , ama nasıl olmalı? Her Aşeyden önce, bir değil B, bu yüzden onları saklayabilse bile , bu üyeleri bile duymamıştı .

Hain Dava

B b1;
B b2;
A& a_ref = b2;
a_ref = b1;
//b2 now contains a mixture of b1 and b2!

Bunun daha sonra b2bir kopyası olacağını düşünebilirsiniz b1. Ama ne yazık ki öyle değil ! Eğer incelerseniz, bunun b2bazı parçalarından b1( Bmiras alınan parçalar A) ve bazı parçalarından b2(sadece Biçeren parçalar ) yapılmış bir Frankensteinian yaratık olduğunu keşfedeceksiniz . Ah!

Ne oldu? Aslında, C ++ varsayılan olarak atama işleçlerini işlemez virtual. Böylece, hat a_ref = b1, atama operatörünü Adeğil , atama operatörünü çağırır B. Bu sanal olmayan fonksiyonlar için, çünkü ilan : (resmen statik tipi (olan) A&) karşıt olarak, denir fonksiyonu belirleyen gerçek (resmi: Dinamik ) türü (olurdu B, çünkü a_refreferansları bir örneği B) . Şimdi, Aatama operatörü açıkça beyan edilen üyeler hakkında bilgi Asahibi olduğundan, üyeleri Bdeğiştirmeden ekleyerek yalnızca üyeler kopyalanacak .

Bir çözüm

Bir nesnenin yalnızca parçalarına atamak genellikle çok mantıklı değildir, ancak C ++ maalesef bunu yasaklamak için yerleşik bir yol sağlamaz. Ancak kendiniz de yapabilirsiniz. İlk adım atama işlecini sanal hale getirmektir . Bu hep olduğunu garanti edecek fiili denir türünün atama operatörü değil beyan gibiler. İkinci adım, dynamic_castatanan nesnenin uyumlu bir türe sahip olduğunu doğrulamaktır. Üçüncü adım, bir (korumalı!) Üyesi gerçek atama yapmaktır assign(), çünkü B'ler assign()muhtemelen kullanmak isteyeceğiniz A' s assign()kopyalamak için A, 'in üyeleri.

class A {
public:
  virtual A& operator= (const A& a) {
    assign(a);
    return *this;
  }

protected:
  void assign(const A& a) {
    // copy members of A from a to this
  }
};

class B : public A {
public:
  virtual B& operator= (const A& a) {
    if (const B* b = dynamic_cast<const B*>(&a))
      assign(*b);
    else
      throw bad_assignment();
    return *this;
  }

protected:
  void assign(const B& b) {
    A::assign(b); // Let A's assign() copy members of A from b to this
    // copy members of B from b to this
  }
};

Saf kolaylık, Not B's operator=o zamandan beri covariantly, dönüş türü geçersiz kılar bilir o bir örneğini döndürüyor olduğunu B.


12
IMHO, sorun, kalıtımla ima edilebilecek iki farklı ikame türünün olmasıdır: ya bir deriveddeğer bekleyen koda herhangi bir değer verilebilir baseya da herhangi bir türetilmiş referans temel referans olarak kullanılabilir. Her iki kavramı ayrı ayrı ele alan bir yazı sistemi olan bir dil görmek istiyorum. Türetilmiş bir referansın bir baz referansının yerine geçmesi gereken birçok durum vardır, ancak türetilmiş örneklerin taban referanslarının yerine geçmemesi gerekir; örneklerin dönüştürülebilir olması ancak referansların yerine geçmemesi gereken birçok durum da vardır.
supercat

16
"Hain" durumunuzda neyin bu kadar kötü olduğunu anlamıyorum. Aşağıdakileri yapmak istediğinizi belirttiniz: 1) A ve 2 sınıfı bir nesneye referans almak) b1 nesnesini A sınıfına dökmek ve eşyalarını A sınıfının bir referansına kopyalamak Burada gerçekten yanlış olan şey, arkasındaki doğru mantıktır verilen kod. Başka bir deyişle, küçük bir görüntü çerçevesi (A) aldınız, daha büyük bir görüntünün üzerine (B) yerleştirdiniz ve daha sonra daha büyük görüntünüzün çirkin göründüğünden şikayet ederek bu çerçeveyi boyadınız :) Ama bu çerçeveli alanı düşünürsek, tıpkı ressamın istediği gibi oldukça iyi görünüyor, değil mi? :)
Mladen

12
Sorun, farklı bir ifadeyle , C ++ 'ın varsayılan olarak çok güçlü bir tür ikame edilebilirlik varsaymasıdır - alt sınıf örneklerinde düzgün bir şekilde temel sınıf işlemlerinin yapılmasını gerektirir. Ve bu, derleyicinin atama gibi otomatik olarak oluşturduğu işlemler için bile. Bu nedenle, bu konuda kendi işlemlerinizi batırmamak yeterli değildir, ayrıca derleyici tarafından oluşturulan yanlış olanları da açıkça devre dışı bırakmanız gerekir. Veya tabii ki, genellikle iyi bir öneri olan kamu kalıtımından uzak durun ;-)
fgp

14
Diğer bir yaygın yaklaşım, kopyalama ve atama işlecini devre dışı bırakmaktır. Kalıtım hiyerarşisi içindeki sınıflar için genellikle referans veya işaretçi yerine değer kullanmak için bir neden yoktur.
Siyuan Ren

13
Nedir? Operatörlerin sanal olarak işaretlenebileceğine dair hiçbir fikrim yoktu
paulm

154

Bir temel sınıfınız Ave türetilmiş bir sınıfınız varsa B, aşağıdakileri yapabilirsiniz.

void wantAnA(A myA)
{
   // work with myA
}

B derived;
// work with the object "derived"
wantAnA(derived);

Şimdi yöntemin wantAnAbir kopyası gerekiyor derived. Ancak, derivedsınıf Btemel sınıfında olmayan ek üye değişkenleri icat edebileceğinden nesne tamamen kopyalanamaz A.

Bu nedenle, çağırmak wantAnAiçin derleyici türetilmiş sınıfın tüm ek üyelerini "keser". Sonuç, oluşturmak istemediğiniz bir nesne olabilir, çünkü

  • eksik olabilir,
  • bir A-nesne gibi davranır (sınıfın tüm özel davranışları Bkaybolur).

41
C ++ Java değildir ! Eğer wantAnA(adından da anlaşılacağı gibi!) Bir istiyorsa A, o zaman bu olur. Ve bir örneği Agibi davranacak A. Bu nasıl şaşırtıcı?
fgp

83
@fgp: Şaşırtıcı, çünkü işleve bir A iletmiyorsunuz .
Black

10
@fgp: Davranış benzer. Ancak, ortalama C ++ programcısı için daha az belirgin olabilir. Soruyu anladığım kadarıyla kimse "şikayet etmiyor". Derleyicinin durumu nasıl ele aldığı ile ilgilidir. Imho, referansları geçerek dilimlemekten kaçınmak daha iyidir.
Siyah

9
@ThomasW Hayır, kalıtım atmazdım, ama referanslar kullanırım. WantAnA imzası wantAnA (cons A ve myA) 'yı geçersiz kılarsa , dilimleme yapılmamıştır. Bunun yerine, arayanın nesnesine salt okunur bir başvuru iletilir.
Siyah

14
sorun çoğunlukla derleyicinin derivedtürden gerçekleştirdiği otomatik dökümde A. Örtülü döküm her zaman C ++ 'da beklenmeyen bir davranış kaynağıdır, çünkü koda yerel olarak bir dökümün gerçekleştiğine bakmaktan anlamak zordur.
pqnet

41

Bunların hepsi iyi cevaplar. Ben sadece referans ile değer vs nesneleri geçerken bir yürütme örneği eklemek istiyorum:

#include <iostream>

using namespace std;

// Base class
class A {
public:
    A() {}
    A(const A& a) {
        cout << "'A' copy constructor" << endl;
    }
    virtual void run() const { cout << "I am an 'A'" << endl; }
};

// Derived class
class B: public A {
public:
    B():A() {}
    B(const B& a):A(a) {
        cout << "'B' copy constructor" << endl;
    }
    virtual void run() const { cout << "I am a 'B'" << endl; }
};

void g(const A & a) {
    a.run();
}

void h(const A a) {
    a.run();
}

int main() {
    cout << "Call by reference" << endl;
    g(B());
    cout << endl << "Call by copy" << endl;
    h(B());
}

Çıktı:

Call by reference
I am a 'B'

Call by copy
'A' copy constructor
I am an 'A'

Merhaba. Harika cevap ama bir sorum var. Eğer böyle bir şey yaparsam ** dev d; base * b = & d; ** Dilimleme de gerçekleşir mi?
Adrian

@Adrian Türetilmiş sınıfta bazı yeni üye işlevleri veya üye değişkenleri tanıtırsanız, bunlara doğrudan temel sınıf işaretçisinden erişilemez. Ancak bunlara aşırı yüklenmiş temel sınıf sanal işlevleri içinden erişebilirsiniz. Şuna
Vishal Sharma

30

Google'da "C ++ dilimleme" için üçüncü maç bana bu Wikipedia makalesini http://en.wikipedia.org/wiki/Object_slicing ve bu (ısıtmalı, ancak ilk birkaç mesaj sorunu tanımlar) verir: http://bytes.com/ forum / thread163565.html

Süper sınıfa bir alt sınıf nesnesi atadığınızda. Üst sınıf, alt sınıftaki ek bilgilerin hiçbirini bilmez ve saklamak için yer yoktur, bu nedenle ek bilgiler "dilimlenir".

Bu bağlantılar "iyi yanıt" için yeterli bilgi vermezse, daha fazla aradığınızı bize bildirmek için lütfen sorunuzu düzenleyin.


29

Dilimleme sorunu ciddidir çünkü bellek bozulmasına neden olabilir ve bir programın bundan muzdarip olmadığını garanti etmek çok zordur. Dili dilden çıkarmak için, kalıtımı destekleyen sınıflara yalnızca referans olarak erişilebilir (değere göre değil). D programlama dili bu özelliğe sahiptir.

A sınıfını ve A sınıfından türetilmiş B sınıfını düşünün. A parçasında p işaretçisi ve p'nin B ek verilerine işaret eden bir B örneği varsa bellek bozulması oluşabilir. Daha sonra, ek veriler dilimlendiğinde, p çöplere işaret eder.


3
Lütfen bellek bozulmasının nasıl olabileceğini açıklayın.
foraidt

4
Fotokopi makinesinin vptr'yi sıfırlayacağını unuttum, benim hatam. Ancak A'nın bir işaretçisi varsa ve B'nin bunu B'nin dilimlenmiş bölümüne işaret etmesi için ayarlayabilir.
Walter Bright

18
Bu sorun sadece dilimleme ile sınırlı değildir. İşaretçiler içeren tüm sınıflar, varsayılan atama işleci ve kopya oluşturucu ile şüpheli davranışlara sahip olacaktır.
Weeble

2
@Weeble - Bu nedenle bu durumda varsayılan yıkıcı, atama işleci ve kopya oluşturucuyu geçersiz kılarsınız.
Bjarke Freund-Hansen

7
@Weeble: Nesne dilimlemeyi genel işaretçi düzeltmelerinden daha kötü yapan şey, dilimlemenin gerçekleşmesini engellediğinizden emin olmak için, bir temel sınıfın türetilmiş her sınıf için dönüştürme yapıcıları sağlaması gerektiğidir . (Neden? Kaçırılan türetilmiş sınıflar Derived, dolaylı olarak dönüştürülebilir olduğundan , temel sınıfın kopya ctoru tarafından alınmaya Baseaçıktır.) Bu, Açık-Kapalı Prensip'e ve büyük bir bakım yüküne açıkça karşıdır.
j_random_hacker

11

C ++ 'da, türetilmiş bir sınıf nesnesi temel sınıf nesnesine atanabilir, ancak diğer yol mümkün değildir.

class Base { int x, y; };

class Derived : public Base { int z, w; };

int main() 
{
    Derived d;
    Base b = d; // Object Slicing,  z and w of d are sliced off
}

Nesne dilimleme, türetilmiş bir sınıf nesnesi bir temel sınıf nesnesine atandığında, türetilmiş bir sınıf nesnesinin ek nitelikleri, temel sınıf nesnesini oluşturmak için dilimlenir.


8

C ++ 'da dilimleme sorunu, çoğunlukla C yapılarıyla uyumluluk nedeniyle kalan nesnelerinin değer semantiğinden kaynaklanmaktadır. Nesneleri yapan diğer birçok dilde bulunan "normal" nesne davranışını elde etmek için açık referans veya işaretçi sözdizimi kullanmanız gerekir; örneğin, nesneler her zaman başvuru ile iletilir.

Kısa cevaplar, bir temel nesneye değere göre türetilmiş bir nesne atayarak nesneyi dilimlemenizdir , yani kalan nesne türetilmiş nesnenin yalnızca bir parçasıdır. Değer semantiğini korumak için dilimleme makul bir davranıştır ve diğer birçok dilde mevcut olmayan nispeten nadir kullanımları vardır. Bazıları bunu C ++ 'ın bir özelliği olarak görürken, birçoğu onu C ++' ın tuhaflıklarından / yanlışlıklarından biri olarak görüyor.


5
" normal nesne davranışı " olmayan "normal" nesne davranışı ", bu referans anlambilimidir . Ve herhangi bir rastgele OOP rahibinin size söylediği C , uyumluluk veya diğer anlamsızlıklarla hiçbir şekilde ilişkili değildirstruct .
curiousguy

4
@curiousguy Amen, kardeşim. Değer semantiği C ++ 'yı delicesine güçlü kılan şeylerden biri olduğunda, C ++' ın Java olmamasından ne kadar sıkıldığını görmek üzücü.
fgp

Bu bir özellik değil, bir tuhaflık / yanlışlık değil. Bir yığın veya (aynı) ayırıcı tür değişkenine sahip bir işlev çağırmak bellekte Basetam olarak sizeof(Base)bayt alması gerektiğinden , yığın üzerinde kopyalama davranışı normaldir , belki de bu nedenle "atama" (yığın üzerinde kopyalama) ) türetilmiş sınıf üyelerini kopyalamaz, ofsetleri sizeof dışındadır. İşaretçi bellek yeri ve büyüklüğü sabit olduğundan yığın çok Uçucu oysa, herkes gibi, sadece kullanım işaretçisi, "Veri kaybını" önlemek için
Croll

Kesinlikle C ++ 'ın bir yanlış özelliği. Türetilmiş bir nesneyi bir temel nesneye atamak yasaklanırken, türetilmiş bir nesneyi bir referansa veya temel sınıfın bir işaretçisine bağlamak tamam olmalıdır.
John Z. Li

7

Peki ... Elde edilen bilgileri kaybetmek neden kötü? ... çünkü türetilmiş sınıfın yazarı temsili değiştirmiş olabilir, böylece fazladan bilginin kesilmesi nesne tarafından temsil edilen değeri değiştirir. Bu, türetilmiş sınıfın, belirli işlemler için daha verimli, ancak temel gösterime geri dönüşü pahalı olan bir gösterimi önbelleğe almak için kullanılırsa oluşabilir.

Ayrıca birinin dilimlemekten kaçınmak için ne yapmanız gerektiğini de belirtmesi gerektiğini düşündüm ... C ++ Kodlama Standartları, 101 kural kılavuzları ve en iyi uygulamaların bir kopyasını alın. Dilimleme ile uğraşmak # 54.

Sorunu tamamen ele almak için biraz karmaşık bir model önermektedir: korumalı bir kopya oluşturucu, korumalı saf sanal DoClone ve (daha fazla) türetilmiş bir sınıfın DoClone'u doğru bir şekilde uygulayamadığını söyleyecek bir onaylayıcıya sahip bir genel Klon bulundurun. (Klonlama yöntemi, polimorfik nesnenin derinlemesine bir kopyasını oluşturur.)

İsterseniz, açık dilimlemeye izin veren temel oluşturucuda kopya kurucusunu da işaretleyebilirsiniz.


3
" Kopya oluşturucuyu da tabana açık olarak işaretleyebilirsiniz " ki bu hiç yardımcı olmaz .
curiousguy

6

1. DİLİMLEME SORUNUNUN TANIMI

D temel sınıf B'nin türetilmiş bir sınıfıysa, Tür türündeki bir değişkene (veya parametreye) Türetilmiş türünde bir nesne atayabilirsiniz.

MİSAL

class Pet
{
 public:
    string name;
};
class Dog : public Pet
{
public:
    string breed;
};

int main()
{   
    Dog dog;
    Pet pet;

    dog.name = "Tommy";
    dog.breed = "Kangal Dog";
    pet = dog;
    cout << pet.breed; //ERROR

Yukarıdaki atamaya izin verilse de, değişken evcil hayvana atanan değer cins alanını kaybeder. Buna dilimleme problemi denir .

2. DİLİMLEME SORUNUNUN DÜZELTİLMESİ

Sorunu yenmek için dinamik değişkenlere işaretçiler kullanıyoruz.

MİSAL

Pet *ptrP;
Dog *ptrD;
ptrD = new Dog;         
ptrD->name = "Tommy";
ptrD->breed = "Kangal Dog";
ptrP = ptrD;
cout << ((Dog *)ptrP)->breed; 

Bu durumda, ptrD (alt sınıf nesnesi) tarafından gösterilen dinamik değişkenin veri üyeleri veya üye işlevleri kaybolmaz. Ayrıca, işlevleri kullanmanız gerekirse, işlevin sanal bir işlev olması gerekir.


7
"Dilimleme" bölümünü anlıyorum ama "problem" i anlamıyorum. dogSınıfın bir parçası olmayan bir durumun Pet( breedveri üyesi) değişkene kopyalanmaması nasıl bir problemdir pet? Kod sadece Petveri üyeleriyle ilgileniyor - görünüşe göre. İstenmeyen dilimleme kesinlikle bir "problem" dir, ancak burada göremiyorum.
curiousguy

4
" ((Dog *)ptrP)" Kullanmanızı öneririmstatic_cast<Dog*>(ptrP)
curiousguy

Ben 'ptrP' ile silerken 'cins' dize sonunda sanal bir yıkıcı ('dize' yıkıcı çağrılmayacak) olmadan bellek sızıntısı yapacağını işaret öneririz ... Neden sorunlu gösteriyor? Düzeltme çoğunlukla uygun sınıf tasarımdır. Bu durumda sorun, miras alınırken görünürlüğü kontrol etmek için yapıcıların yazılması sıkıcı ve kolayca unutulmasıdır. Herhangi bir polimorfizm olmadığı veya hatta belirtildiği için tehlike bölgesinin yakınında herhangi bir yere girmeyeceksiniz (dilimleme nesnenizi kesecek, ancak programınızın çökmesine neden olmayacaktır).
Dostum

24
-1 Bu, gerçek sorunu tamamen açıklayamamaktadır. C ++ değer semantiğine sahiptir, Java gibi referans semantiğe sahip değildir , bu yüzden tümüyle beklenmelidir. Ve "düzeltme" gerçekten korkunç C ++ kodunun bir örneğidir . Dinamik ayırmaya başvurarak bu tür dilimleme gibi mevcut olmayan sorunları "düzeltmek", buggy kodu, sızdırılmış bellek ve korkunç performans için bir reçetedir. Orada Not olduğunu vardır dilimleme kötü vakalar, ancak bu cevap failes onları işaret etmek. İpucu: başvurular yoluyla atamanız durumunda sorun başlar .
fgp

Tanımlı olmayan ( Dog::breed) türünde bir üyeye erişmeye çalışmanın DİLİMLEME ile ilgili bir HATA'nın yolu olmadığını anlıyor musunuz ?
Croll

4

Bana öyle geliyor ki, dilimleme, kendi derslerinizin ve programınızın zayıf bir şekilde tasarlandığı / tasarlandığı durumdan başka bir sorun değil.

Bir alt sınıf nesnesini parametre olarak superclass türünde bir parametre alan bir yönteme geçirirsem, bunun farkında olmalı ve içsel olarak bilmeliyim, çağrılan yöntem yalnızca üst sınıf (aka baseclass) nesnesiyle çalışacaktır.

Bana öyle geliyor ki, sadece bir temel sınıfın talep edildiği bir alt sınıfın sunulmasının, bir şekilde alt sınıfa özgü sonuçlara yol açması, dilimlemenin bir sorun olmasına neden olacağı mantıksız beklentisi. Ya yöntem kullanımında zayıf tasarımı ya da kötü bir alt sınıf uygulaması. Genellikle iyi OOP tasarımından yararlanma veya performans kazanımları lehine feda etmenin sonucudur.


3
Ama unutmayın, Minok, o nesnenin referansını GEÇMEDİĞİNİZ. Bu nesnenin YENİ bir kopyasını geçiriyorsunuz, ancak işlem sırasında kopyalamak için temel sınıfı kullanıyorsunuz.
Arafangion

korumalı kopya / atama temel sınıf ve bu sorun çözüldü.
Dostum

1
Haklısın. İyi uygulama, soyut temel sınıfları kullanmak veya kopyalama / atamaya erişimi kısıtlamaktır. Ancak, bir kez orada olduğunu tespit etmek o kadar kolay değildir ve ilgilenmeyi unutmak kolaydır. Dilimlenmiş * sanal yöntemleri aramak, erişim ihlali olmadan kaçarsanız gizemli şeylerin gerçekleşmesini sağlayabilir.
Dostum

1
Üniversitedeki C ++ programlama kurslarımdan, oluşturduğumuz her sınıf için varsayılan kurucular, kopya kurucular ve atama operatörleri ve bir yıkıcı yazmamız gereken en iyi uygulamalar olduğunu hatırlıyorum. Bu şekilde, kopya yazımının ve benzerinin, dersi yazarken, ihtiyacınız olan şekilde gerçekleştiğinden emin oldunuz ... daha sonra ortaya çıkan tuhaf davranışlardan ziyade.
Minok

3

Tamam, nesne dilimlemeyi açıklayan birçok gönderiyi okuduktan sonra nasıl deneyeceğim ama nasıl sorunlu hale geldiğini değil.

Bellek bozulmasına neden olabilecek kısır senaryo şudur:

  • Sınıf, polimorfik bir baz sınıfında (yanlışlıkla derleyici tarafından üretilen) atama sağlar.
  • İstemci türetilmiş bir sınıf örneğini kopyalar ve dilimler.
  • İstemci, dilimlenmiş duruma erişen bir sanal üye işlevini çağırır.

3

Dilimleme, bir alt sınıf tarafından eklenen verilerin, alt sınıfın bir nesnesi değere göre aktarıldığında veya döndürüldüğünde veya bir temel sınıf nesnesini bekleyen bir işlevden atıldığı anlamına gelir.

Açıklama: Aşağıdaki sınıf bildirimini göz önünde bulundurun:

           class baseclass
          {
                 ...
                 baseclass & operator =(const baseclass&);
                 baseclass(const baseclass&);
          }
          void function( )
          {
                baseclass obj1=m;
                obj1=m;
          }

Temel sınıf kopyalama işlevleri türetilmiş hakkında hiçbir şey bilmediğinden, türetilenin yalnızca temel kısmı kopyalanır. Bu genellikle dilimleme olarak adlandırılır.


1
class A 
{ 
    int x; 
};  

class B 
{ 
    B( ) : x(1), c('a') { } 
    int x; 
    char c; 
};  

int main( ) 
{ 
    A a; 
    B b; 
    a = b;     // b.c == 'a' is "sliced" off
    return 0; 
}

4
Ekstra ayrıntı vermek ister misiniz? Cevabınız önceden gönderilen cevaplardan nasıl farklı?
Alexis Pigeon

2
Sanırım daha fazla açıklama kötü olmazdı.
lüper

-1

türetilmiş bir sınıf nesnesi bir temel sınıf nesnesine atandığında, türetilmiş bir sınıf nesnesinin ek nitelikleri temel sınıf nesnesinden dilimlenir (atılır).

class Base { 
int x;
 };

class Derived : public Base { 
 int z; 
 };

 int main() 
{
Derived d;
Base b = d; // Object Slicing,  z of d is sliced off
}

-1

Temel sınıf Nesnesine Türetilmiş bir sınıf Nesnesi atandığında, türetilmiş sınıf nesnesinin tüm üyeleri, temel sınıfta bulunmayan üyeler dışında temel sınıf nesnesine kopyalanır. Bu üyeler derleyici tarafından dilimlenir. Buna Nesne Dilimleme denir.

İşte bir örnek:

#include<bits/stdc++.h>
using namespace std;
class Base
{
    public:
        int a;
        int b;
        int c;
        Base()
        {
            a=10;
            b=20;
            c=30;
        }
};
class Derived : public Base
{
    public:
        int d;
        int e;
        Derived()
        {
            d=40;
            e=50;
        }
};
int main()
{
    Derived d;
    cout<<d.a<<"\n";
    cout<<d.b<<"\n";
    cout<<d.c<<"\n";
    cout<<d.d<<"\n";
    cout<<d.e<<"\n";


    Base b = d;
    cout<<b.a<<"\n";
    cout<<b.b<<"\n";
    cout<<b.c<<"\n";
    cout<<b.d<<"\n";
    cout<<b.e<<"\n";
    return 0;
}

Üretecek:

[Error] 'class Base' has no member named 'd'
[Error] 'class Base' has no member named 'e'

Bu iyi bir örnek olmadığı için indirildi. Eğer d'den b'ye kopyalamak yerine d ve e'nin hala mevcut olacağı ancak Base'in bu üyeleri yoksa bir işaretçi kullanırsanız çalışmaz. Örneğin yalnızca sınıfta olmayan üyelere erişemeyeceğinizi gösterir.
Stefan Fabian

-2

Ben sadece dilimleme sorunu koştu ve hemen buraya indi. İki sentimi buna ekleyeyim.

"Üretim kodundan" bir örnek alalım (ya da yakın gelen bir şey):


Diyelim ki eylemler gönderen bir şeyimiz var. Örneğin bir kontrol merkezi kullanıcı arayüzü.
Bu kullanıcı arayüzünün şu anda gönderilebilen şeylerin bir listesini alması gerekiyor. Bu nedenle, gönderim bilgilerini içeren bir sınıf tanımlarız. Haydi diyelim Action. Yani bir Actionüye değişkenleri vardır. Sadelik için a std::string nameve a olmak üzere sadece 2 tane var std::function<void()> f. Sonra void activate()sadece birf üyeyi .

Böylece UI std::vector<Action>tedarik edilir. Şöyle bazı işlevleri düşünün:

void push_back(Action toAdd);

Şimdi arayüzün nasıl göründüğünü belirledik. Şimdiye kadar sorun yok. Ancak bu proje üzerinde çalışan başka bir adam aniden Actionnesnede daha fazla bilgiye ihtiyaç duyan özel eylemler olduğuna karar veriyor . Hangi sebepten ötürü. Bu da lambda yakalamaları ile çözülebilir. Bu örnek koddan 1-1 alınmaz.

Böylece adam Actionkendi lezzetini eklemekten geliyor.
Evde demlenmiş sınıfının bir örneğini geçirir, push_backancak program haywire'a gider.

Peki ne oldu?
Tahmin edebileceğiniz gibi: nesne dilimlenmiştir.

Örneğe ait fazladan bilgiler kaybolmuştur ve fşimdi tanımlanmamış davranışa eğilimlidir.


Ben bu örnek bahsederken gerçekten şeyleri hayal bile edemeyen insanlar için yaklaşık ışık getiriyor umut As ve Bbazı biçimde kullanılmasıyla elde edilen s.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.