Bagikan melalui


Penginisialisasi

Penginisialisasi menentukan nilai awal variabel. Anda dapat menginisialisasi variabel dalam konteks ini:

  • Dalam definisi variabel:

    int i = 3;
    Point p1{ 1, 2 };
    
  • Sebagai salah satu parameter fungsi:

    set_point(Point{ 5, 6 });
    
  • Sebagai nilai pengembalian fungsi:

    Point get_new_point(int x, int y) { return { x, y }; }
    Point get_new_point(int x, int y) { return Point{ x, y }; }
    

Penginisialisasi dapat mengambil formulir ini:

  • Ekspresi (atau daftar ekspresi yang dipisahkan koma) dalam tanda kurung:

    Point p1(1, 2);
    
  • Tanda sama dengan diikuti dengan ekspresi:

    string s = "hello";
    
  • Daftar inisialisasi kurung. Daftar mungkin kosong atau mungkin terdiri dari sekumpulan daftar, seperti dalam contoh berikut:

    struct Point{
        int x;
        int y;
    };
    class PointConsumer{
    public:
        void set_point(Point p){};
        void set_points(initializer_list<Point> my_list){};
    };
    int main() {
        PointConsumer pc{};
        pc.set_point({});
        pc.set_point({ 3, 4 });
        pc.set_points({ { 3, 4 }, { 5, 6 } });
    }
    

Jenis inisialisasi

Ada beberapa jenis inisialisasi, yang mungkin terjadi di titik yang berbeda dalam eksekusi program. Berbagai jenis inisialisasi tidak saling eksklusif—misalnya, inisialisasi daftar dapat memicu inisialisasi nilai dan dalam keadaan lain, inisialisasi agregat dapat dipicu.

Inisialisasi nol

Inisialisasi nol adalah pengaturan variabel ke nilai nol yang dikonversi secara implisit ke jenis :

  • Variabel numerik diinisialisasi ke 0 (atau 0,0, atau 0,00000000000, dll.).

  • Variabel karakter diinisialisasi ke '\0'.

  • Pointer diinisialisasi ke nullptr.

  • Array, kelas POD , struct, dan union memiliki anggota mereka yang diinisialisasi ke nilai nol.

Inisialisasi nol dilakukan pada waktu yang berbeda:

  • Pada startup program, untuk semua variabel bernama yang memiliki durasi statis. Variabel ini nantinya dapat diinisialisasi lagi.

  • Selama inisialisasi nilai, untuk jenis skalar dan jenis kelas POD yang diinisialisasi dengan menggunakan kurung kurawal kosong.

  • Untuk array yang hanya memiliki subset anggota mereka yang diinisialisasi.

Berikut adalah beberapa contoh inisialisasi nol:

struct my_struct{
    int i;
    char c;
};

int i0;              // zero-initialized to 0
int main() {
    static float f1;  // zero-initialized to 0.000000000
    double d{};     // zero-initialized to 0.00000000000000000
    int* ptr{};     // initialized to nullptr
    char s_array[3]{'a', 'b'};  // the third char is initialized to '\0'
    int int_array[5] = { 8, 9, 10 };  // the fourth and fifth ints are initialized to 0
    my_struct a_struct{};   // i = 0, c = '\0'
}

Inisialisasi default

Inisialisasi default untuk kelas, struktur, dan serikat adalah inisialisasi dengan konstruktor default. Konstruktor default dapat dipanggil tanpa ekspresi inisialisasi atau dengan new kata kunci:

MyClass mc1;
MyClass* mc3 = new MyClass;

Jika kelas, struct, atau union tidak memiliki konstruktor default, pengkompilasi akan memancarkan kesalahan.

Variabel skalar diinisialisasi secara default saat ditentukan tanpa ekspresi inisialisasi. Mereka memiliki nilai yang tidak ditentukan.

int i1;
float f;
char c;

Array diinisialisasi secara default saat didefinisikan tanpa ekspresi inisialisasi. Saat array diinisialisasi secara default, anggotanya diinisialisasi secara default dan memiliki nilai yang tidak ditentukan, seperti dalam contoh berikut:

int int_arr[3];

Jika anggota array tidak memiliki konstruktor default, pengkompilasi akan memancarkan kesalahan.

Inisialisasi default variabel konstanta

Variabel konstanta harus dideklarasikan bersama dengan penginisialisasi. Jika mereka adalah jenis skalar, mereka menyebabkan kesalahan kompilator, dan jika mereka adalah jenis kelas yang memiliki konstruktor default, mereka menyebabkan peringatan:

class MyClass{};
int main() {
    //const int i2;   // compiler error C2734: const object must be initialized if not extern
    //const char c2;  // same error
    const MyClass mc1; // compiler error C4269: 'const automatic data initialized with compiler generated default constructor produces unreliable results
}

Inisialisasi default variabel statis

Variabel statis yang dinyatakan tanpa penginisialisasi diinisialisasi menjadi 0 (dikonversi secara implisit ke jenis ).

class MyClass {
private:
    int m_int;
    char m_char;
};

int main() {
    static int int1;       // 0
    static char char1;     // '\0'
    static bool bool1;   // false
    static MyClass mc1;     // {0, '\0'}
}

Untuk informasi selengkapnya tentang inisialisasi objek statis global, lihat fungsi utama dan argumen baris perintah.

Inisialisasi nilai

Inisialisasi nilai terjadi dalam kasus berikut:

  • nilai bernama diinisialisasi menggunakan inisialisasi kurung kurawal kosong

  • objek sementara anonim diinisialisasi menggunakan tanda kurung atau kurung kurung kurawal kosong

  • objek diinisialisasi dengan new kata kunci ditambah tanda kurung kosong atau kurung kurawal

Inisialisasi nilai melakukan hal berikut:

  • untuk kelas dengan setidaknya satu konstruktor publik, konstruktor default dipanggil

  • untuk kelas nonunion tanpa konstruktor yang dideklarasikan, objek diinisialisasi nol dan konstruktor default dipanggil

  • untuk array, setiap elemen diinisialisasi nilainya

  • dalam semua kasus lain, variabel tidak diinisialisasi

class BaseClass {
private:
    int m_int;
};

int main() {
    BaseClass bc{};     // class is initialized
    BaseClass*  bc2 = new BaseClass();  // class is initialized, m_int value is 0
    int int_arr[3]{};  // value of all members is 0
    int a{};     // value of a is 0
    double b{};  // value of b is 0.00000000000000000
}

Menyalin inisialisasi

Inisialisasi salin adalah inisialisasi satu objek menggunakan objek yang berbeda. Ini terjadi dalam kasus-kasus berikut:

  • variabel diinisialisasi menggunakan tanda sama dengan

  • argumen diteruskan ke fungsi

  • objek dikembalikan dari fungsi

  • pengecualian dilemparkan atau ditangkap

  • anggota data non-statis diinisialisasi menggunakan tanda sama dengan

  • anggota kelas, struktur, dan serikat diinisialisasi dengan inisialisasi salinan selama inisialisasi agregat. Lihat Inisialisasi agregat misalnya.

Kode berikut menunjukkan beberapa contoh inisialisasi salinan:

#include <iostream>
using namespace std;

class MyClass{
public:
    MyClass(int myInt) {}
    void set_int(int myInt) { m_int = myInt; }
    int get_int() const { return m_int; }
private:
    int m_int = 7; // copy initialization of m_int

};
class MyException : public exception{};
int main() {
    int i = 5;              // copy initialization of i
    MyClass mc1{ i };
    MyClass mc2 = mc1;      // copy initialization of mc2 from mc1
    MyClass mc1.set_int(i);    // copy initialization of parameter from i
    int i2 = mc2.get_int(); // copy initialization of i2 from return value of get_int()

    try{
        throw MyException();
    }
    catch (MyException ex){ // copy initialization of ex
        cout << ex.what();
    }
}

Inisialisasi salin tidak dapat memanggil konstruktor eksplisit.

vector<int> v = 10; // the constructor is explicit; compiler error C2440: can't convert from 'int' to 'std::vector<int,std::allocator<_Ty>>'
regex r = "a.*b"; // the constructor is explicit; same error
shared_ptr<int> sp = new int(1729); // the constructor is explicit; same error

Dalam beberapa kasus, jika konstruktor salinan kelas dihapus atau tidak dapat diakses, inisialisasi salin menyebabkan kesalahan kompilator.

Inisialisasi langsung

Inisialisasi langsung adalah inisialisasi menggunakan kurung (tidak kosong). Tidak seperti inisialisasi salinan, ini dapat memanggil konstruktor eksplisit. Ini terjadi dalam kasus-kasus berikut:

  • variabel diinisialisasi dengan kurung kurung atau tanda kurung tidak kosong

  • variabel diinisialisasi dengan new kata kunci ditambah kurung tidak kosong

  • variabel diinisialisasi dengan static_cast

  • dalam konstruktor, kelas dasar, dan anggota non-statis diinisialisasi dengan daftar inisialisasi

  • dalam salinan variabel yang diambil di dalam ekspresi lambda

Kode berikut menunjukkan beberapa contoh inisialisasi langsung:

class BaseClass{
public:
    BaseClass(int n) :m_int(n){} // m_int is direct initialized
private:
    int m_int;
};

class DerivedClass : public BaseClass{
public:
    // BaseClass and m_char are direct initialized
    DerivedClass(int n, char c) : BaseClass(n), m_char(c) {}
private:
    char m_char;
};
int main(){
    BaseClass bc1(5);
    DerivedClass dc1{ 1, 'c' };
    BaseClass* bc2 = new BaseClass(7);
    BaseClass bc3 = static_cast<BaseClass>(dc1);

    int a = 1;
    function<int()> func = [a](){  return a + 1; }; // a is direct initialized
    int n = func();
}

Inisialisasi daftar

Inisialisasi daftar terjadi ketika variabel diinisialisasi menggunakan daftar inisialisasi kurung. Daftar inisialisasi braced dapat digunakan dalam kasus berikut:

  • variabel diinisialisasi

  • kelas diinisialisasi dengan new kata kunci

  • objek dikembalikan dari fungsi

  • argumen yang diteruskan ke fungsi

  • salah satu argumen dalam inisialisasi langsung

  • dalam penginisialisasi anggota data non-statis

  • dalam daftar penginisialisasi konstruktor

Kode berikut menunjukkan beberapa contoh inisialisasi daftar:

class MyClass {
public:
    MyClass(int myInt, char myChar) {}
private:
    int m_int[]{ 3 };
    char m_char;
};
class MyClassConsumer{
public:
    void set_class(MyClass c) {}
    MyClass get_class() { return MyClass{ 0, '\0' }; }
};
struct MyStruct{
    int my_int;
    char my_char;
    MyClass my_class;
};
int main() {
    MyClass mc1{ 1, 'a' };
    MyClass* mc2 = new MyClass{ 2, 'b' };
    MyClass mc3 = { 3, 'c' };

    MyClassConsumer mcc;
    mcc.set_class(MyClass{ 3, 'c' });
    mcc.set_class({ 4, 'd' });

    MyStruct ms1{ 1, 'a', { 2, 'b' } };
}

Inisialisasi agregat

Inisialisasi agregat adalah bentuk inisialisasi daftar untuk array atau jenis kelas (sering kali struktur atau serikat) yang memiliki:

  • tidak ada anggota privat atau terlindungi

  • tidak ada konstruktor yang disediakan pengguna, kecuali untuk konstruktor default atau dihapus secara eksplisit

  • tidak ada kelas dasar

  • tidak ada fungsi anggota virtual

Catatan

Di Visual Studio 2015 dan yang lebih lama, agregat tidak diizinkan untuk memiliki inisialisasi kurung atau sama dengan untuk anggota non-statis. Pembatasan ini dihapus dalam standar C++14 dan diterapkan di Visual Studio 2017.

Inisialisasi agregat terdiri dari daftar inisialisasi kurung, dengan atau tanpa tanda sama dengan, seperti dalam contoh berikut:

#include <iostream>
using namespace std;

struct MyAggregate{
    int myInt;
    char myChar;
};

struct MyAggregate2{
    int myInt;
    char myChar = 'Z'; // member-initializer OK in C++14
};

int main() {
    MyAggregate agg1{ 1, 'c' };
    MyAggregate2 agg2{2};
    cout << "agg1: " << agg1.myChar << ": " << agg1.myInt << endl;
    cout << "agg2: " << agg2.myChar << ": " << agg2.myInt << endl;

    int myArr1[]{ 1, 2, 3, 4 };
    int myArr2[3] = { 5, 6, 7 };
    int myArr3[5] = { 8, 9, 10 };

    cout << "myArr1: ";
    for (int i : myArr1){
        cout << i << " ";
    }
    cout << endl;

    cout << "myArr3: ";
    for (auto const &i : myArr3) {
        cout << i << " ";
    }
    cout << endl;
}

Anda akan menemukan output berikut:

agg1: c: 1
agg2: Z: 2
myArr1: 1 2 3 4
myArr3: 8 9 10 0 0

Penting

Anggota array yang dinyatakan tetapi tidak diinisialisasi secara eksplisit selama inisialisasi agregat diinisialisasi nol, seperti di myArr3 atas.

Menginisialisasi serikat dan struktur

Jika serikat tidak memiliki konstruktor, Anda dapat menginisialisasinya dengan satu nilai (atau dengan instans lain dari serikat). Nilai digunakan untuk menginisialisasi bidang non-statis pertama. Ini berbeda dari inisialisasi struktur, di mana nilai pertama dalam penginisialisasi digunakan untuk menginisialisasi bidang pertama, yang kedua untuk menginisialisasi bidang kedua, dan sebagainya. Bandingkan inisialisasi serikat dan struktur dalam contoh berikut:

struct MyStruct {
    int myInt;
    char myChar;
};
union MyUnion {
    int my_int;
    char my_char;
    bool my_bool;
    MyStruct my_struct;
};

int main() {
    MyUnion mu1{ 'a' };  // my_int = 97, my_char = 'a', my_bool = true, {myInt = 97, myChar = '\0'}
    MyUnion mu2{ 1 };   // my_int = 1, my_char = 'x1', my_bool = true, {myInt = 1, myChar = '\0'}
    MyUnion mu3{};      // my_int = 0, my_char = '\0', my_bool = false, {myInt = 0, myChar = '\0'}
    MyUnion mu4 = mu3;  // my_int = 0, my_char = '\0', my_bool = false, {myInt = 0, myChar = '\0'}
    //MyUnion mu5{ 1, 'a', true };  // compiler error: C2078: too many initializers
    //MyUnion mu6 = 'a';            // compiler error: C2440: cannot convert from 'char' to 'MyUnion'
    //MyUnion mu7 = 1;              // compiler error: C2440: cannot convert from 'int' to 'MyUnion'

    MyStruct ms1{ 'a' };            // myInt = 97, myChar = '\0'
    MyStruct ms2{ 1 };              // myInt = 1, myChar = '\0'
    MyStruct ms3{};                 // myInt = 0, myChar = '\0'
    MyStruct ms4{1, 'a'};           // myInt = 1, myChar = 'a'
    MyStruct ms5 = { 2, 'b' };      // myInt = 2, myChar = 'b'
}

Menginisialisasi agregat yang berisi agregat

Jenis agregat dapat berisi jenis agregat lainnya, misalnya array array, array struktur, dan sebagainya. Jenis ini diinisialisasi dengan menggunakan sekumpulan kurung kurawal berlapis, misalnya:

struct MyStruct {
    int myInt;
    char myChar;
};
int main() {
    int intArr1[2][2]{{ 1, 2 }, { 3, 4 }};
    int intArr3[2][2] = {1, 2, 3, 4};
    MyStruct structArr[]{ { 1, 'a' }, { 2, 'b' }, {3, 'c'} };
}

Inisialisasi referensi

Variabel jenis referensi harus diinisialisasi dengan objek jenis dari mana jenis referensi berasal, atau dengan objek jenis yang dapat dikonversi ke jenis dari mana jenis referensi berasal. Contohnya:

// initializing_references.cpp
int iVar;
long lVar;
int main()
{
    long& LongRef1 = lVar;        // No conversion required.
    long& LongRef2 = iVar;        // Error C2440
    const long& LongRef3 = iVar;  // OK
    LongRef1 = 23L;               // Change lVar through a reference.
    LongRef2 = 11L;               // Change iVar through a reference.
    LongRef3 = 11L;               // Error C3892
}

Satu-satunya cara untuk menginisialisasi referensi dengan objek sementara adalah dengan menginisialisasi objek sementara yang konstan. Setelah diinisialisasi, variabel jenis referensi selalu menunjuk ke objek yang sama; tidak dapat dimodifikasi untuk menunjuk ke objek lain.

Meskipun sintaksnya bisa sama, inisialisasi variabel jenis referensi dan penugasan ke variabel jenis referensi secara semantik berbeda. Dalam contoh sebelumnya, tugas yang berubah iVar dan lVar terlihat mirip dengan inisialisasi, tetapi memiliki efek yang berbeda. Inisialisasi menentukan objek tempat variabel jenis referensi menunjuk; penugasan ditetapkan ke objek yang dirujuk melalui referensi.

Karena keduanya meneruskan argumen jenis referensi ke fungsi dan mengembalikan nilai jenis referensi dari fungsi adalah inisialisasi, argumen formal ke fungsi diinisialisasi dengan benar, seperti halnya referensi yang dikembalikan.

Variabel jenis referensi dapat dideklarasikan tanpa penginisialisasi hanya dalam hal berikut:

  • Deklarasi fungsi (prototipe). Contohnya:

    int func( int& );
    
  • Deklarasi jenis pengembalian fungsi. Contohnya:

    int& func( int& );
    
  • Deklarasi anggota kelas jenis referensi. Contohnya:

    class c {public:   int& i;};
    
  • Deklarasi variabel yang secara eksplisit ditentukan sebagai extern. Contohnya:

    extern int& iVal;
    

Saat menginisialisasi variabel jenis referensi, pengkompilasi menggunakan grafik keputusan yang ditunjukkan pada gambar berikut untuk memilih antara membuat referensi ke objek atau membuat objek sementara tempat referensi menunjuk:

Grafik keputusan untuk inisialisasi jenis referensi.

Grafik keputusan dimulai dengan: apakah inisialisasi merupakan lvalue dari jenis yang sama atau jenis yang berasal dari jenis referensi? Jika ya, referensi mengacu pada objek yang ditentukan dalam penginisialisasi. Jika tidak, keputusan berikutnya adalah apakah variabel jenis referensi adalah referensi const T yang diinisialisasi dan dapatkah penginisialisasi dikonversi secara implisit ke T? Jika ya, sementara dibuat dan variabel referensi menjadi nama untuk sementara tersebut. Jika tidak, itu adalah kesalahan.

Grafik keputusan untuk inisialisasi jenis referensi

Referensi ke volatile jenis (dinyatakan sebagai volatile typename& identifier) dapat diinisialisasi dengan objek dengan volatile jenis yang sama atau dengan objek yang belum dinyatakan sebagai volatile. Namun, mereka tidak dapat diinisialisasi dengan const objek jenis tersebut. Demikian pula, referensi ke const jenis (dinyatakan sebagai const typename& identifier) dapat diinisialisasi dengan objek dengan const jenis yang sama (atau apa pun yang memiliki konversi ke jenis tersebut atau dengan objek yang belum dinyatakan sebagai const). Namun, mereka tidak dapat diinisialisasi dengan volatile objek jenis tersebut.

Referensi yang tidak memenuhi syarat dengan kata const kunci atau volatile hanya dapat diinisialisasi dengan objek yang dideklarasikan sebagai tidak const atau volatile.

Inisialisasi variabel eksternal

Deklarasi variabel otomatis, statis, dan eksternal dapat berisi penginisialisasi. Namun, deklarasi variabel eksternal dapat berisi penginisialisasi hanya jika variabel tidak dinyatakan sebagai extern.