Bu özel durumda, üye başlatıcı listesi kullanmakla yapıcıdaki değerleri atamak arasında bir fark var mı?


91

Dahili olarak ve üretilen kod hakkında, aşağıdakiler arasında gerçekten bir fark var mı:

MyClass::MyClass(): _capacity(15), _data(NULL), _len(0)
{
}

ve

MyClass::MyClass()
{
  _capacity=15;
  _data=NULL;
  _len=0
}

Teşekkürler...

Yanıtlar:


63

Bu değerlerin ilkel türler olduğunu varsayarsak, hayır, hiçbir fark yoktur. Başlatma listeleri, yalnızca üye olarak nesneleriniz olduğunda bir fark yaratır, çünkü varsayılan başlatma ve ardından atama kullanmak yerine, başlatma listesi nesneyi nihai değerine başlatmanıza izin verir. Bu aslında fark edilir derecede daha hızlı olabilir.


18
Richard'ın dediği gibi, bu değerlerin ilkel türler ve const olması bir fark yaratır, ilklendirme listeleri const üyelerine değer atamanın tek yoludur.
thbusch

12
Yalnızca değişkenler referans veya sabit olmadığında açıklandığı gibi çalışır, eğer sabit veya referans ise, başlatma listesi kullanmadan bile derlenmez.
stefanB

77

Sabit üyeleri, referansları ve temel sınıfı başlatmak için başlatma listesini kullanmanız gerekir

Sabit üyeyi, referansları başlatmanız ve parametreleri temel sınıf yapıcılarına iletmeniz gerektiğinde, yorumlarda belirtildiği gibi, başlatma listesini kullanmanız gerekir.

struct aa
{
    int i;
    const int ci;       // constant member

    aa() : i(0) {} // will fail, constant member not initialized
};

struct aa
{
    int i;
    const int ci;

    aa() : i(0) { ci = 3;} // will fail, ci is constant
};

struct aa
{
    int i;
    const int ci;

    aa() : i(0), ci(3) {} // works
};

Örnek (kapsamlı olmayan) sınıf / yapı başvuru içerir:

struct bb {};

struct aa
{
    bb& rb;
    aa(bb& b ) : rb(b) {}
};

// usage:

bb b;
aa a(b);

Ve bir parametre gerektiren temel sınıfı başlatma örneği (ör. Varsayılan kurucu yok):

struct bb {};

struct dd
{
    char c;
    dd(char x) : c(x) {}
};

struct aa : dd
{
    bb& rb;
    aa(bb& b ) : dd('a'), rb(b) {}
};

4
Ve eğer _capacity, _datave _lenerişilebilir bir varsayılan kurucular olmadan sınıf türleri var mı?
CB Bailey

2
Hangi kurucuların mevcut olduğunu, daha fazla değer belirlemeniz gerekiyorsa bunları kurucunuzun gövdesinden çağırırsınız. Buradaki fark, üyeyi constyapıcı gövdesinde başlatamazsınız , başlatma listesini kullanmanız gerekir - constüye olmayanlar başlatma listesinde veya yapıcının gövdesinde başlatılabilir.
stefanB

@stefan: Kurucuları çağıramazsınız. Varsayılan kurucusu olmayan bir sınıfın, tıpkı const üyeleri gibi başlatıcı listesinde başlatılması gerekir.
GManNickG

2
ayrıca başlatma listesindeki referansları da başlatmalısınız
Andriy Tylychko

2
@stefanB: Bunu gerçekten anlamadığınızı ima ettiysem özür dilerim. Cevabınızda, yalnızca bir üs veya üye başlatıcı listesinde ne zaman isimlendirilmesi gerektiğini belirttiniz, başlatıcı listesinde başlatma ile kurucu gövdede atama arasındaki kavramsal farkın gerçekte ne olduğunu açıklamadınız, yanlış anlamam burada. nereden gelmiş olabilir.
CB Bailey

18

Evet. İlk durumda _capacity, _datave _lensabitler olarak beyan edebilirsiniz :

class MyClass
{
private:
    const int _capacity;
    const void *_data;
    const int _len;
// ...
};

Çalışma constzamanında değerlerini hesaplarken bu örnek değişkenlerinin -ness'ini sağlamak istiyorsanız bu önemli olacaktır , örneğin:

MyClass::MyClass() :
    _capacity(someMethod()),
    _data(someOtherMethod()),
    _len(yetAnotherMethod())
{
}

constörnekleri gerekir başlatıcı listesinde başlatılması ya da altta yatan türleri (ilkel türleri do) Kamu parametresiz kurucular sağlamalıdır.


2
Aynı şey referanslar için de geçerli. Sınıfınızın referans üyeleri varsa , başlatıcı listesinde başlatılmaları gerekir .
Mark Ransom

7

Sanırım bu http://www.cplusplus.com/forum/articles/17820/ bağlantısının mükemmel bir açıklama sağladığını düşünüyorum - özellikle C ++ 'da yeni olanlar için.

Başlatıcı listelerinin daha verimli olmasının nedeni, kurucu gövdesi içinde, başlatmanın değil, yalnızca atamaların yer almasıdır. Bu nedenle, yerleşik olmayan bir türle uğraşıyorsanız, o nesnenin varsayılan kurucusu, kurucunun gövdesi girilmeden önce zaten çağrılmıştır. Yapıcı gövdesinin içinde, o nesneye bir değer atarsınız.

Aslında bu, varsayılan kurucuya yapılan bir çağrı ve ardından kopyalama-atama operatörüne yapılan bir çağrıdır. Başlatıcı listesi, kopya oluşturucuyu doğrudan çağırmanıza izin verir ve bu bazen önemli ölçüde daha hızlı olabilir (başlatıcı listesinin yapıcının gövdesinden önce olduğunu hatırlayın)


5

Varsayılan kurucunun olmadığı sınıf türü üyeleriniz varsa, sınıfınızı oluşturmanın tek yolunun başlatma olduğunu ekleyeceğim.


3

Büyük bir fark, atamanın bir üst sınıfın üyelerini başlatabilmesidir; başlatıcı yalnızca geçerli sınıf kapsamında bildirilen üyeler üzerinde çalışır.


2

İlgili türlere bağlıdır. Aradaki fark benzer

std::string a;
a = "hai";

ve

std::string a("hai");

burada ikinci biçim başlatma listesi - yani, türün yapıcı bağımsız değişkenleri gerektirmesi veya yapıcı bağımsız değişkenleriyle daha verimli olması fark yaratır.


1

Gerçek fark, gcc derleyicisinin makine kodunu nasıl ürettiği ve belleği nasıl yerleştirdiğiyle ilgilidir. Açıklamak:

  • (aşama1) Başlangıç ​​gövdesinden önce (başlangıç ​​listesi dahil): derleyici, sınıf için gerekli belleği ayırır. Sınıf zaten yaşıyor!
  • (aşama2) Başlatma gövdesinde: bellek tahsis edildiğinden, her atama artık halihazırda çıkan / 'başlatılmış' değişken üzerinde bir işlemi gösterir.

Const tipi üyeleri kullanmanın kesinlikle başka yolları da vardır. Ancak hayatlarını kolaylaştırmak için gcc derleyici yazarları bazı kurallar koymaya karar verir

  1. const türü üyeleri, başlangıç ​​gövdesinden önce başlatılmalıdır.
  2. Aşama1'den sonra, herhangi bir yazma işlemi yalnızca sabit olmayan üyeler için geçerlidir.

1

Temel sınıf örneklerini ve statik olmayan üye değişkenleri başlatmanın tek bir yolu vardır ve bu da başlatıcı listesini kullanmaktır.

Oluşturucunuzun başlatıcı listesinde bir temel veya statik olmayan üye değişken belirtmezseniz, bu üye veya taban varsayılan olarak başlatılır (üye / taban POD olmayan bir sınıf türü veya POD dışı sınıf dizisi ise) türleri) veya aksi takdirde başlatılmamış bırakılır.

Yapıcı gövdesine girildikten sonra, tüm bazlar veya üyeler başlatılmış veya sıfırlanmamış bırakılmış olacaktır (yani, belirsiz bir değere sahip olacaklardır). Yapıcı gövdede, bunların nasıl başlatılacağını etkileme fırsatı yoktur.

Yapıcı gövdesindeki üyelere yeni değerler atayabilirsiniz, ancak constatanamaz hale getirilmiş sınıf türündeki üyelere veya üyelere atamak ve referansları yeniden atamak mümkün değildir.

Yerleşik türler ve bazı kullanıcı tanımlı türler için, yapıcı gövdesinde atama, başlatıcı listesindeki aynı değerle başlatma ile tam olarak aynı etkiye sahip olabilir.

Bir başlatıcı listesindeki bir üyeyi veya tabanı adlandırmada başarısız olursanız ve bu varlık bir başvuru ise, kullanıcı tarafından tanımlanmış erişilebilir constbir kurucuya sahip olmayan bir sınıf türüne sahipse, uygunsa ve POD türüne sahipse veya bir POD sınıfı türü veya POD sınıf türü dizisi ise constkalifiye bir üye içeriyorsa (doğrudan veya dolaylı olarak) program kötü biçimlidir.


0

Bir başlatıcı listesi yazarsanız, hepsini tek adımda yaparsınız; Bir başlatıcı listesi yazmazsanız, 2 adım yapacaksınız: biri bildirim için, diğeri de değeri atamak için.


0

Bir yapıcıdaki başlatma listesi ile başlatma ifadesi arasında bir fark vardır. Aşağıdaki kodu ele alalım:

#include <initializer_list>
#include <iostream>
#include <algorithm>
#include <numeric>

class MyBase {
public:
    MyBase() {
        std::cout << __FUNCTION__ << std::endl;
    }
};

class MyClass : public MyBase {
public:
    MyClass::MyClass() : _capacity( 15 ), _data( NULL ), _len( 0 ) {
        std::cout << __FUNCTION__ << std::endl;
    }
private:
    int _capacity;
    int* _data;
    int _len;
};

class MyClass2 : public MyBase {
public:
    MyClass2::MyClass2() {
        std::cout << __FUNCTION__ << std::endl;
        _capacity = 15;
        _data = NULL;
        _len = 0;
    }
private:
    int _capacity;
    int* _data;
    int _len;
};

int main() {
    MyClass c;
    MyClass2 d;

    return 0;
}

MyClass kullanıldığında, tüm üyeler, çalıştırılan bir yapıcıdaki ilk ifadeden önce başlatılacaktır.

Ancak MyClass2 kullanıldığında, bir yapıcıdaki ilk ifade çalıştırıldığında tüm üyeler başlatılmaz.

Daha sonraki durumda, belirli bir üye başlatılmadan önce birisi kurucuya bazı kodlar eklediğinde regresyon sorunu olabilir.


0

İşte başkalarının buna değindiğini görmediğim bir nokta:

class temp{
public:
   temp(int var);
};

Temp sınıfının varsayılan bir ctoru yoktur. Aşağıdaki gibi başka bir sınıfta kullandığımızda:

class mainClass{
public:
 mainClass(){}
private:
  int a;
  temp obj;
};

kod derlenmez, derleyicinin nasıl başlatılacağını bilmemesine objneden olur, çünkü sadece bir int değeri alan açık bir ctor'a sahiptir, bu nedenle ctor'u aşağıdaki gibi değiştirmeliyiz:

mainClass(int sth):obj(sth){}

Yani, bu sadece sabit ve referanslarla ilgili değil!

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.