Dalam kasus khusus ini, apakah ada perbedaan antara menggunakan daftar penginisialisasi anggota dan menetapkan nilai dalam konstruktor?


90

Secara internal dan tentang kode yang dihasilkan, apakah benar-benar ada perbedaan antara:

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

dan

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

Terima kasih...

Jawaban:


61

Dengan asumsi bahwa nilai-nilai tersebut adalah tipe primitif, maka tidak, tidak ada perbedaan. Daftar inisialisasi hanya membuat perbedaan ketika Anda memiliki objek sebagai anggota, karena alih-alih menggunakan inisialisasi default yang diikuti dengan penugasan, daftar inisialisasi memungkinkan Anda menginisialisasi objek ke nilai akhirnya. Ini sebenarnya bisa terasa lebih cepat.


17
seperti yang dikatakan Richard, akan ada bedanya jika nilai-nilai itu adalah tipe primitif dan konst, daftar inisialisasi adalah satu-satunya cara untuk menetapkan nilai ke anggota const.
thbusch

11
Ini hanya berfungsi seperti yang dijelaskan ketika variabel bukan referensi atau konstanta, jika mereka konstan atau referensi bahkan tidak akan dikompilasi tanpa menggunakan daftar inisialisasi.
StefanB

77

Anda perlu menggunakan daftar inisialisasi untuk menginisialisasi anggota konstan, referensi, dan kelas dasar

Saat Anda perlu menginisialisasi anggota konstan, referensi, dan parameter meneruskan ke konstruktor kelas dasar, seperti yang disebutkan dalam komentar, Anda perlu menggunakan daftar inisialisasi.

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
};

Contoh kelas / struct (tidak lengkap) berisi referensi:

struct bb {};

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

// usage:

bb b;
aa a(b);

Dan contoh inisialisasi kelas dasar yang membutuhkan parameter (mis. Tidak ada konstruktor default):

struct bb {};

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

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

4
Dan jika _capacity, _datadan _lenmemiliki tipe kelas tanpa konstruktor default yang dapat diakses?
CB Bailey

2
Anda memanggil konstruktor apa yang tersedia, jika Anda perlu menyetel lebih banyak nilai maka Anda memanggilnya dari badan konstruktor Anda. Perbedaannya di sini adalah Anda tidak dapat menginisialisasi constanggota dalam tubuh konstruktor, Anda harus menggunakan daftar inisialisasi - non constanggota dapat diinisialisasi dalam daftar inisialisasi atau dalam badan konstruktor.
StefanB

@ Stefan: Anda tidak dapat memanggil konstruktor. Sebuah kelas tanpa konstruktor default harus diinisialisasi dalam daftar penginisialisasi, seperti anggota const.
GManNickG

2
Anda juga harus menginisialisasi referensi dalam daftar inisialisasi
Andriy Tylychko

2
@stefanB: Saya mohon maaf jika saya menyiratkan bahwa Anda tidak memahami hal ini padahal sebenarnya Anda mengerti. Dalam jawaban Anda, Anda hanya menyatakan ketika sebuah basis atau anggota harus diberi nama dalam daftar penginisialisasi, Anda belum menjelaskan apa perbedaan konseptual antara menginisialisasi dalam daftar penginisialisasi dan menetapkan dalam badan konstruktor sebenarnya adalah tempat kesalahpahaman saya. mungkin berasal dari.
CB Bailey

18

Iya. Dalam kasus pertama, Anda dapat mendeklarasikan _capacity, _datadan _lensebagai konstanta:

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

Ini akan menjadi penting jika Anda ingin memastikan const-ness variabel instance ini saat menghitung nilainya pada waktu proses, misalnya:

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

constinstance harus diinisialisasi dalam daftar penginisialisasi atau tipe yang mendasari harus menyediakan konstruktor tanpa parameter publik (yang dilakukan oleh tipe primitif).


2
Hal yang sama berlaku untuk referensi. Jika kelas Anda memiliki anggota referensi, mereka harus diinisialisasi dalam daftar penginisialisasi.
Mark Ransom

7

Saya pikir tautan ini http://www.cplusplus.com/forum/articles/17820/ memberikan penjelasan yang sangat baik - terutama bagi mereka yang baru mengenal C ++.

Alasan mengapa daftar intialiser lebih efisien adalah karena di dalam badan konstruktor, hanya penugasan yang dilakukan, bukan inisialisasi. Jadi, jika Anda berurusan dengan tipe non-built-in, konstruktor default untuk objek itu telah dipanggil sebelum isi konstruktor dimasukkan. Di dalam badan konstruktor, Anda memberikan nilai ke objek itu.

Akibatnya, ini adalah panggilan ke konstruktor default yang diikuti dengan panggilan ke operator penugasan salinan. Daftar penginisialisasi memungkinkan Anda memanggil konstruktor salinan secara langsung, dan ini terkadang jauh lebih cepat (ingat bahwa daftar penginisialisasi ada sebelum badan konstruktor)


5

Saya akan menambahkan bahwa jika Anda memiliki anggota tipe kelas tanpa konstruktor default yang tersedia, inisialisasi adalah satu-satunya cara untuk membangun kelas Anda.


3

Perbedaan besar adalah bahwa tugas dapat menginisialisasi anggota kelas induk; penginisialisasi hanya bekerja pada anggota yang dideklarasikan pada lingkup kelas saat ini.


2

Tergantung pada jenis yang terlibat. Perbedaannya serupa di antara keduanya

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

dan

std::string a("hai");

di mana bentuk kedua adalah daftar inisialisasi- yaitu, membuat perbedaan jika tipe memerlukan argumen konstruktor atau lebih efisien dengan argumen konstruktor.


1

Perbedaan sebenarnya terletak pada bagaimana compiler gcc menghasilkan kode mesin dan meletakkan memori. Menjelaskan:

  • (fase1) Sebelum tubuh init (termasuk daftar init): kompilator mengalokasikan memori yang diperlukan untuk kelas. Kelas sudah hidup!
  • (fase2) Dalam tubuh init: karena memori dialokasikan, setiap tugas sekarang menunjukkan operasi pada variabel yang sudah keluar / 'diinisialisasi'.

Pasti ada cara lain untuk menangani anggota tipe const. Tetapi untuk memudahkan hidup mereka, penulis kompilator gcc memutuskan untuk menyiapkan beberapa aturan

  1. anggota tipe const harus diinisialisasi sebelum badan init.
  2. Setelah fase1, operasi tulis apa pun hanya berlaku untuk anggota non-konstan.

1

Hanya ada satu cara untuk menginisialisasi instance kelas dasar dan variabel anggota non-statis dan itu menggunakan daftar penginisialisasi.

Jika Anda tidak menentukan variabel anggota dasar atau non-statis dalam daftar penginisialisasi konstruktor Anda, maka anggota atau basis tersebut akan diinisialisasi secara default (jika anggota / basis adalah jenis kelas non-POD atau array kelas non-POD jenis) atau dibiarkan tidak dimulai sebaliknya.

Setelah badan konstruktor dimasukkan, semua basis atau anggota akan diinisialisasi atau dibiarkan tidak diinisialisasi (yaitu mereka akan memiliki nilai tak tentu). Tidak ada kesempatan dalam badan konstruktor untuk mempengaruhi bagaimana mereka harus diinisialisasi.

Anda mungkin dapat menetapkan nilai baru untuk anggota dalam badan konstruktor tetapi tidak mungkin untuk menetapkan constanggota atau anggota jenis kelas yang telah dibuat non-assignable dan tidak mungkin untuk rebind referensi.

Untuk tipe bawaan dan beberapa tipe yang ditentukan pengguna, menetapkan dalam badan konstruktor mungkin memiliki efek yang sama persis seperti menginisialisasi dengan nilai yang sama di daftar penginisialisasi.

Jika Anda gagal menamai anggota atau basis dalam daftar penginisialisasi dan entitas tersebut adalah referensi, memiliki tipe kelas tanpa konstruktor default yang dideklarasikan pengguna yang dapat diakses, constmemenuhi syarat dan memiliki tipe POD atau merupakan tipe kelas POD atau larik tipe kelas POD berisi anggota yang constmemenuhi syarat (secara langsung atau tidak langsung) maka program tersebut tidak berbentuk.


0

Jika Anda menulis daftar penginisialisasi, Anda melakukan semuanya dalam satu langkah; jika Anda tidak menulis daftar initilizer, Anda akan melakukan 2 langkah: satu untuk deklarasi dan satu lagi untuk menambahkan nilainya.


0

Ada perbedaan antara daftar inisialisasi dan pernyataan inisialisasi dalam konstruktor. Mari pertimbangkan kode di bawah ini:

#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;
}

Ketika MyClass digunakan, semua anggota akan diinisialisasi sebelum pernyataan pertama dalam konstruktor dijalankan.

Tapi, ketika MyClass2 digunakan, semua anggota tidak diinisialisasi ketika pernyataan pertama dalam konstruktor dijalankan.

Dalam kasus selanjutnya, mungkin ada masalah regresi ketika seseorang menambahkan beberapa kode dalam konstruktor sebelum anggota tertentu diinisialisasi.


0

Inilah poin di mana saya tidak melihat orang lain merujuknya:

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

Kelas temp tidak memiliki ctor default. Saat kami menggunakannya di kelas lain sebagai berikut:

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

kode tidak akan dikompilasi, karena kompilator tidak tahu bagaimana menginisialisasi obj, karena ia hanya memiliki ctor eksplisit yang menerima nilai int, jadi kita harus mengubah ctor sebagai berikut:

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

Jadi, ini bukan hanya tentang const dan referensi!

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.