Varsayılan kurucu ve yıkıcı için “= varsayılan” “{}” den farkı nedir?


169

Başlangıçta bunu sadece yıkıcılar hakkında bir soru olarak gönderdim, ancak şimdi varsayılan yapıcıyı da ekliyorum. İşte asıl soru:

Sınıfıma sanal, ancak derleyicinin üreteceği şeyle aynı olan bir yıkıcı vermek istersem, kullanabilirim =default:

class Widget {
public:
   virtual ~Widget() = default;
};

Ama boş bir tanım kullanarak daha az yazarak aynı etkiyi elde edebilirim:

class Widget {
public:
   virtual ~Widget() {}
};

Bu iki tanımın farklı davrandığı bir yol var mı?

Bu soru için gönderilen cevaplara dayanarak, varsayılan kurucu için durum benzer görünüyor. Yıkıcılar için " =default" ve " {}" arasında anlam bakımından neredeyse hiçbir fark olmadığı göz önüne alındığında, varsayılan kurucular için bu seçenekler arasında hemen hemen hiçbir anlam farkı yoktur? Yani, bu tür nesnelerin hem yaratılacağı hem de yok edileceği bir tür oluşturmak istediğimi varsayarsak, neden şunu söylemek isterim?

Widget() = default;

onun yerine

Widget() {}

?

Orijinal gönderisinin bazı SO kurallarını ihlal etmesinden sonra bu soruyu genişlettiğinizde özür dilerim. Varsayılan kurucular için neredeyse özdeş bir soru yayınlamak, daha az arzu edilen seçenek olarak beni etkiledi.


1
Bildiğimden değil, ama = defaultdaha açık imo ve yapıcılar ile destek ile tutarlı değil.
chris

11
Emin değilim, ama sanırım birincisi "önemsiz yıkıcı" tanımına uygun, ikincisi ise bilmiyor. Yani std::has_trivial_destructor<Widget>::valuebir trueilk için değil, falsesaniye. Bunun ne anlama geldiğini de bilmiyorum. :)
GManNickG

10
Sanal bir yıkıcı asla önemsiz değildir.
Luc Danton

@LucDanton: Sanırım gözlerimi açıyorum ve koda bakmak da işe yarayacaktı! Düzelttiğiniz için teşekkürler.
GManNickG

Yanıtlar:


103

Bu, inşaatçıları sorurken yıkıcılardan tamamen farklı bir sorudur.

Yıkıcınız ise virtual, Howard'ın belirttiği gibi fark göz ardı edilebilir . Ancak, yıkıcınız sanal değilse , bu tamamen farklı bir hikaye. Aynı şey yapıcılar için de geçerlidir.

= defaultÖzel üye işlevleri (varsayılan yapıcı, kopyala / taşı yapıcılar / atama, yıkıcılar vb.) İçin sözdizimini kullanmak , yapmaktan çok farklı bir şey anlamına gelir {}. İkincisi ile fonksiyon "kullanıcı tarafından sağlanır" hale gelir. Ve bu her şeyi değiştirir.

Bu, C ++ 11'in tanımına göre önemsiz bir sınıftır:

struct Trivial
{
  int foo;
};

Bir tanesini varsayılan olarak oluşturmaya çalışırsanız, derleyici otomatik olarak bir varsayılan kurucu oluşturur. Aynı şey kopyalama / hareket ve tahrip için de geçerlidir. Kullanıcı bu üye işlevlerinden herhangi birini sağlamadığından, C ++ 11 belirtimi bunu "önemsiz" bir sınıf olarak görür. Bu nedenle, bunları başlatmak için içeriklerini memcpy gibi yapmak yasaldır.

Bu:

struct NotTrivial
{
  int foo;

  NotTrivial() {}
};

Adından da anlaşılacağı gibi, bu artık önemsiz değil. Kullanıcı tarafından sağlanan varsayılan bir yapıcıya sahiptir. Boş olup olmadığı önemli değil; C ++ 11 kuralları söz konusu olduğunda, bu önemsiz bir tür olamaz.

Bu:

struct Trivial2
{
  int foo;

  Trivial2() = default;
};

Yine adından da anlaşılacağı gibi, bu önemsiz bir tür. Neden? Çünkü derleyiciye otomatik olarak varsayılan kurucuyu oluşturmasını söylediniz. Bu nedenle kurucu "kullanıcı tarafından sağlanmaz". Bu nedenle, tür, kullanıcı tarafından sağlanan bir varsayılan kurucuya sahip olmadığından önemsiz olarak sayılır.

= defaultSözdizimi bu tip işlevler yaratılmasını önlemek üye işlevleri ekleyin kopya kurucular / atama, gibi şeyler yapmak için esas yoktur. Ancak derleyiciden özel davranışı da tetikler, bu nedenle varsayılan kurucularda / yıkıcılarda da yararlıdır.


2
Bu nedenle, anahtar sorun, ortaya çıkan sınıfın önemsiz olup olmadığı gibi görünüyor ve bu sorunun altında yatan, kullanıcı tarafından bildirilen ( =defaultişlevler için durum ) özel bir işlev ile kullanıcı tarafından sağlanan (durum için {}) işlevler arasındaki farktır . Hem kullanıcı tarafından bildirilen hem de kullanıcı tarafından sağlanan işlevler, diğer özel üye işlevlerinin oluşturulmasını önleyebilir (örn., Kullanıcı tarafından bildirilen bir yıkıcı, taşıma işlemlerinin oluşturulmasını önler), ancak yalnızca kullanıcı tarafından sağlanan özel işlev, bir sınıfı önemsiz hale getirir. Sağ?
KnowItAllWannabe

@KnowItAllWannabe: Genel fikir bu, evet.
Nicol Bolas

Bunu kabul edilen cevap olarak seçiyorum, çünkü hem yapıcıları hem de (Howard'ın cevabına referans olarak) yıkıcıları kapsıyor.
KnowItAllWannabe

Burada eksik bir kelime gibi görünüyor "C ++ 11 kuralları söz konusu olduğunda, önemsiz bir tür haklar" Ben düzeltmek istiyorum ama ne% 100 emin ne amaçlanan emin değilim.
jcoder

2
= defaultdiğer kurucuların varlığına rağmen derleyiciyi varsayılan bir kurucu oluşturmaya zorlamak için yararlı görünüyor; kullanıcı tarafından bildirilen başka bir kurucu sağlanmışsa, varsayılan kurucu örtük olarak bildirilmez.
bgfvdu3w

42

İkisi de önemsiz değil.

Her ikisi de, tabanların ve üyelerin istisnasız spesifikasyonuna bağlı olarak aynı istisnasız spesifikasyona sahiptir.

Şimdiye kadar tespit ettiğim tek fark Widget, erişilemeyen veya silinmiş bir yıkıcıya sahip bir taban veya üye içeriyorsa:

struct A
{
private:
    ~A();
};

class Widget {
    A a_;
public:
#if 1
   virtual ~Widget() = default;
#else
   virtual ~Widget() {}
#endif
};

O zaman =defaultçözüm derlenecek, ancak Widgetyıkılabilir bir tür olmayacak. Yani a'yı yok etmeye çalışırsanız Widgetderleme zamanı hatası alırsınız. Ama eğer yapmazsan, çalışan bir programın var.

Otoh, kullanıcı tarafından sağlanan yıkıcıyı tedarik ederseniz, bir şeyleri yok etseniz de etmeseniz de işler derlenmeyecektir Widget:

test.cpp:8:7: error: field of type 'A' has private destructor
    A a_;
      ^
test.cpp:4:5: note: declared private here
    ~A();
    ^
1 error generated.

9
İlginç: başka bir deyişle, =default;derleyici kullanılmadıkça yıkıcı oluşturmaz ve bu nedenle bir hata tetiklemez. Bu bir hata olmasa bile, bana garip geliyor. Bu davranışın standartta zorunlu olduğunu düşünemiyorum .
Nik Bougalis

"O zaman = varsayılan çözüm derlenir" Hayır olmaz.
Nano

Hata mesajı neydi ve VS'nin hangi sürümü?
Howard Hinnant

35

Arasındaki önemli fark

class B {
    public:
    B(){}
    int i;
    int j;
};

ve

class B {
    public:
    B() = default;
    int i;
    int j;
};

ile tanımlanan varsayılan kurucunun B() = default;dikkate alınması kullanıcı tanımlı olmadığı . Bu, değerin başlatılması durumunda olduğu gibi

B* pb = new B();  // use of () triggers value-initialization

bir kurucu kullanmayan özel bir başlatma türü olacaktır ve yerleşik tipler için bu sıfır başlatma ile sonuçlanacaktır . Bu durumda B(){}gerçekleşmez. C ++ Standardı n3337 § 8.5 / 7 diyor ki

T türündeki bir nesneyi değer başlatmak için:

- T, kullanıcı tarafından sağlanan bir kurucuya (12.1) sahip (muhtemelen cv-nitelikli) bir sınıf tipidir (Madde 9) ise , T için varsayılan kurucu çağrılır (ve T'de erişilebilir bir varsayılan kurucu yoksa başlatma yanlış biçimlendirilir) );

- eğer T (muhtemelen CV nitelikli) sendika dışı bir sınıf tipiyse kullanıcı tarafından sağlanan bir kurucu , nesne sıfır olarak başlatılır ve T'nin dolaylı olarak bildirilen varsayılan kurucusu önemsiz değilse, o kurucu çağrılır.

- T bir dizi tipiyse, her eleman değere sıfırlanır; - aksi takdirde, nesne sıfırla başlatılır.

Örneğin:

#include <iostream>

class A {
    public:
    A(){}
    int i;
    int j;
};

class B {
    public:
    B() = default;
    int i;
    int j;
};

int main()
{
    for( int i = 0; i < 100; ++i) {
        A* pa = new A();
        B* pb = new B();
        std::cout << pa->i << "," << pa->j << std::endl;
        std::cout << pb->i << "," << pb->j << std::endl;
        delete pa;
        delete pb;
    }
  return 0;
}

olası sonuç:

0,0
0,0
145084416,0
0,0
145084432,0
0,0
145084416,0
//...

http://ideone.com/k8mBrd


O zaman, neden "{}" ve "= default" her zaman bir std :: string ideone.com/LMv5Uf başlatır ?
nawfel bgh

1
@nawfelbgh Varsayılan yapıcı A () {}, POD olmayan tür olduğundan std :: string için varsayılan yapıcıyı çağırır. Std :: string öğesinin varsayılan kralı, boş, 0 boyutlu dizeye başlatır . Skalerlerin varsayılan kralı hiçbir şey yapmaz: otomatik saklama süresine (ve alt nesnelerine) sahip nesneler belirsiz değerler için başlatılır.
4pie0
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.