Bagikan melalui


Konstruktor (C++)

Untuk menyesuaikan cara kelas menginisialisasi anggotanya, atau untuk memanggil fungsi saat objek kelas Anda dibuat, tentukan konstruktor. Konstruktor memiliki nama yang sama dengan kelas dan tanpa nilai pengembalian. Anda dapat menentukan sebanyak mungkin konstruktor yang kelebihan beban sesuai kebutuhan untuk menyesuaikan inisialisasi dengan berbagai cara. Biasanya, konstruktor memiliki aksesibilitas publik sehingga kode di luar definisi kelas atau hierarki warisan dapat membuat objek kelas. Tetapi Anda juga dapat mendeklarasikan konstruktor sebagai protected atau private.

Konstruktor dapat secara opsional mengambil daftar inisialisasi anggota. Ini adalah cara yang lebih efisien untuk menginisialisasi anggota kelas daripada menetapkan nilai dalam isi konstruktor. Contoh berikut menunjukkan kelas Box dengan tiga konstruktor yang kelebihan beban. Dua terakhir menggunakan daftar init anggota:

class Box {
public:
    // Default constructor
    Box() {}

    // Initialize a Box with equal dimensions (i.e. a cube)
    explicit Box(int i) : m_width(i), m_length(i), m_height(i) // member init list
    {}

    // Initialize a Box with custom dimensions
    Box(int width, int length, int height)
        : m_width(width), m_length(length), m_height(height)
    {}

    int Volume() { return m_width * m_length * m_height; }

private:
    // Will have value of 0 when default constructor is called.
    // If we didn't zero-init here, default constructor would
    // leave them uninitialized with garbage values.
    int m_width{ 0 };
    int m_length{ 0 };
    int m_height{ 0 };
};

Saat Anda mendeklarasikan instans kelas, pengkompilasi memilih konstruktor mana yang akan dipanggil berdasarkan aturan resolusi kelebihan beban:

int main()
{
    Box b; // Calls Box()

    // Using uniform initialization (preferred):
    Box b2 {5}; // Calls Box(int)
    Box b3 {5, 8, 12}; // Calls Box(int, int, int)

    // Using function-style notation:
    Box b4(2, 4, 6); // Calls Box(int, int, int)
}
  • Konstruktor dapat dinyatakan sebagai inline, , explicit, friendatau constexpr.
  • Konstruktor dapat menginisialisasi objek yang telah dideklarasikan sebagai const, volatile atau const volatile. Objek menjadi const setelah konstruktor selesai.
  • Untuk menentukan konstruktor dalam file implementasi, berikan nama yang memenuhi syarat seperti fungsi anggota lainnya: Box::Box(){...}.

Daftar penginisialisasi anggota

Konstruktor dapat secara opsional memiliki daftar inisialisasi anggota, yang menginisialisasi anggota kelas sebelum isi konstruktor berjalan. (Daftar inisialisasi anggota bukan hal yang sama dengan daftar inisialisasi tipe std::initializer_list<T>.)

Lebih suka daftar penginisialisasi anggota daripada menetapkan nilai dalam isi konstruktor. Daftar inisialisasi anggota secara langsung menginisialisasi anggota. Contoh berikut menunjukkan daftar penginisialisasi anggota, yang terdiri dari semua identifier(argument) ekspresi setelah titik dua:

    Box(int width, int length, int height)
        : m_width(width), m_length(length), m_height(height)
    {}

Pengidentifikasi harus merujuk ke anggota kelas; ini diinisialisasi dengan nilai argumen. Argumen dapat menjadi salah satu parameter konstruktor, panggilan fungsi atau std::initializer_list<T>.

const anggota dan anggota jenis referensi harus diinisialisasi dalam daftar penginisialisasi anggota.

Untuk memastikan kelas dasar sepenuhnya diinisialisasi sebelum konstruktor turunan berjalan, panggil konstruktor kelas dasar berparameter apa pun dalam daftar inisialisasi.

Konstruktor default

Konstruktor default biasanya tidak memiliki parameter, tetapi mereka dapat memiliki parameter dengan nilai default.

class Box {
public:
    Box() { /*perform any required default initialization steps*/}

    // All params have default values
    Box (int w = 1, int l = 1, int h = 1): m_width(w), m_height(h), m_length(l){}
...
}

Konstruktor default adalah salah satu fungsi anggota khusus. Jika tidak ada konstruktor yang dideklarasikan dalam kelas, kompilator menyediakan konstruktor default implisit inline .

#include <iostream>
using namespace std;

class Box {
public:
    int Volume() {return m_width * m_height * m_length;}
private:
    int m_width { 0 };
    int m_height { 0 };
    int m_length { 0 };
};

int main() {
    Box box1; // Invoke compiler-generated constructor
    cout << "box1.Volume: " << box1.Volume() << endl; // Outputs 0
}

Jika Anda mengandalkan konstruktor default implisit, pastikan untuk menginisialisasi anggota dalam definisi kelas, seperti yang ditunjukkan pada contoh sebelumnya. Tanpa inisialisasi tersebut, anggota tidak akan diinisialisasi dan panggilan Volume() akan menghasilkan nilai sampah. Secara umum, adalah praktik yang baik untuk menginisialisasi anggota dengan cara ini bahkan ketika tidak mengandalkan konstruktor default implisit.

Anda dapat mencegah pengkompilasi menghasilkan konstruktor default implisit dengan menentukannya sebagai dihapus:

    // Default constructor
    Box() = delete;

Konstruktor default yang dihasilkan kompilator akan didefinisikan sebagai dihapus jika ada anggota kelas yang tidak dapat dibangun secara default. Misalnya, semua anggota jenis kelas, dan anggota jenis kelasnya, harus memiliki konstruktor dan destruktor default yang dapat diakses. Semua anggota data dari jenis referensi dan semua const anggota harus memiliki penginisialisasi anggota default.

Saat Anda memanggil konstruktor default yang dihasilkan kompilator dan mencoba menggunakan tanda kurung, peringatan dikeluarkan:

class myclass{};
int main(){
myclass mc();     // warning C4930: prototyped function not called (was a variable definition intended?)
}

Pernyataan ini adalah contoh masalah "Most Vexing Parse". Anda dapat menafsirkan myclass md(); baik sebagai deklarasi fungsi atau sebagai pemanggilan konstruktor default. Karena pengurai C++ mendukung deklarasi atas hal-hal lain, ekspresi diperlakukan sebagai deklarasi fungsi. Untuk informasi selengkapnya, lihat Sebagian Besar Parse Vexing.

Jika ada konstruktor non-default yang dideklarasikan, pengkompilasi tidak menyediakan konstruktor default:

class Box {
public:
    Box(int width, int length, int height)
        : m_width(width), m_length(length), m_height(height){}
private:
    int m_width;
    int m_length;
    int m_height;

};

int main(){

    Box box1(1, 2, 3);
    Box box2{ 2, 3, 4 };
    Box box3; // C2512: no appropriate default constructor available
}

Jika kelas tidak memiliki konstruktor default, array objek kelas tersebut tidak dapat dibangun dengan menggunakan sintaks kurung siku saja. Misalnya, mengingat blok kode sebelumnya, array Boxes tidak dapat dinyatakan seperti ini:

Box boxes[3]; // C2512: no appropriate default constructor available

Namun, Anda dapat menggunakan sekumpulan daftar penginisialisasi untuk menginisialisasi array objek Box:

Box boxes[3]{ { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };

Untuk informasi selengkapnya, lihat Penginisialisasi.

Menyalin konstruktor

Konstruktor salinan menginisialisasi objek dengan menyalin nilai anggota dari objek dengan jenis yang sama. Jika semua anggota kelas Anda adalah jenis sederhana seperti nilai skalar, konstruktor salinan yang dihasilkan kompilator sudah cukup dan Anda tidak perlu menentukan sendiri. Jika kelas Anda memerlukan inisialisasi yang lebih kompleks, maka Anda perlu menerapkan konstruktor salinan kustom. Misalnya, jika anggota kelas adalah penunjuk maka Anda perlu menentukan konstruktor salinan untuk mengalokasikan memori baru dan menyalin nilai dari objek yang ditunjuk-ke lainnya. Konstruktor salinan yang dihasilkan kompilator hanya menyalin penunjuk, sehingga pointer baru masih menunjuk ke lokasi memori lainnya.

Konstruktor salinan mungkin memiliki salah satu tanda tangan ini:

    Box(Box& other); // Avoid if possible--allows modification of other.
    Box(const Box& other);
    Box(volatile Box& other);
    Box(volatile const Box& other);

    // Additional parameters OK if they have default values
    Box(Box& other, int i = 42, string label = "Box");

Saat menentukan konstruktor salinan, Anda juga harus menentukan operator penugasan salinan (=). Untuk informasi selengkapnya, lihat Penugasan dan Menyalin konstruktor dan menyalin operator penugasan.

Anda dapat mencegah objek Anda disalin dengan menentukan konstruktor salinan sebagai dihapus:

    Box (const Box& other) = delete;

Mencoba menyalin objek menghasilkan kesalahan C2280: mencoba mereferensikan fungsi yang dihapus.

Pindahkan konstruktor

Konstruktor pemindahan adalah fungsi anggota khusus yang memindahkan kepemilikan data objek yang ada ke variabel baru tanpa menyalin data asli. Dibutuhkan referensi rvalue sebagai parameter pertamanya, dan parameter selanjutnya harus memiliki nilai default. Memindahkan konstruktor dapat secara signifikan meningkatkan efisiensi program Anda saat melewati objek besar.

Box(Box&& other);

Pengkompilasi memilih konstruktor pemindahan ketika objek diinisialisasi oleh objek lain dengan jenis yang sama, jika objek lain akan dihancurkan dan tidak lagi membutuhkan sumber dayanya. Contoh berikut menunjukkan satu kasus ketika konstruktor pemindahan dipilih oleh resolusi kelebihan beban. Dalam konstruktor yang memanggil get_Box(), nilai yang dikembalikan adalah xvalue (nilai eXpiring). Ini tidak ditetapkan ke variabel apa pun dan oleh karena itu akan keluar dari cakupan. Untuk memberikan motivasi untuk contoh ini, mari kita beri Box vektor string besar yang mewakili kontennya. Daripada menyalin vektor dan stringnya, konstruktor pemindahan "mencuri" itu dari nilai "kotak" yang kedaluwarsa sehingga vektor sekarang milik objek baru. Panggilan ke std::move adalah semua yang diperlukan karena kedua vector kelas mengimplementasikan string konstruktor pemindahan mereka sendiri.

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;

class Box {
public:
    Box() { std::cout << "default" << std::endl; }
    Box(int width, int height, int length)
       : m_width(width), m_height(height), m_length(length)
    {
        std::cout << "int,int,int" << std::endl;
    }
    Box(Box& other)
       : m_width(other.m_width), m_height(other.m_height), m_length(other.m_length)
    {
        std::cout << "copy" << std::endl;
    }
    Box(Box&& other) : m_width(other.m_width), m_height(other.m_height), m_length(other.m_length)
    {
        m_contents = std::move(other.m_contents);
        std::cout << "move" << std::endl;
    }
    int Volume() { return m_width * m_height * m_length; }
    void Add_Item(string item) { m_contents.push_back(item); }
    void Print_Contents()
    {
        for (const auto& item : m_contents)
        {
            cout << item << " ";
        }
    }
private:
    int m_width{ 0 };
    int m_height{ 0 };
    int m_length{ 0 };
    vector<string> m_contents;
};

Box get_Box()
{
    Box b(5, 10, 18); // "int,int,int"
    b.Add_Item("Toupee");
    b.Add_Item("Megaphone");
    b.Add_Item("Suit");

    return b;
}

int main()
{
    Box b; // "default"
    Box b1(b); // "copy"
    Box b2(get_Box()); // "move"
    cout << "b2 contents: ";
    b2.Print_Contents(); // Prove that we have all the values

    char ch;
    cin >> ch; // keep window open
    return 0;
}

Jika kelas tidak menentukan konstruktor pemindahan, pengkompilasi menghasilkan yang implisit jika tidak ada konstruktor salinan yang dideklarasikan pengguna, operator penugasan salinan, operator penetapan pemindahan, atau destruktor. Jika tidak ada konstruktor pemindahan eksplisit atau implisit yang ditentukan, operasi yang jika tidak akan menggunakan konstruktor pemindahan, gunakan konstruktor salinan sebagai gantinya. Jika kelas mendeklarasikan konstruktor pemindahan atau operator penetapan pemindahan, konstruktor salinan yang dinyatakan secara implisit didefinisikan sebagai dihapus.

Konstruktor pemindahan yang dideklarasikan secara implisit didefinisikan sebagai dihapus jika anggota yang merupakan jenis kelas tidak memiliki destruktor atau jika kompilator tidak dapat menentukan konstruktor mana yang akan digunakan untuk operasi pemindahan.

Untuk informasi selengkapnya tentang cara menulis konstruktor pemindahan non-sepele, lihat Memindahkan Konstruktor dan Memindahkan Operator Penugasan (C++).

Konstruktor default dan dihapus secara eksplisit

Anda dapat secara eksplisit default menyalin konstruktor, konstruktor default, memindahkan konstruktor, menyalin operator penugasan, memindahkan operator penugasan, dan destruktor. Anda dapat secara eksplisit menghapus semua fungsi anggota khusus.

class Box2
{
public:
    Box2() = delete;
    Box2(const Box2& other) = default;
    Box2& operator=(const Box2& other) = default;
    Box2(Box2&& other) = default;
    Box2& operator=(Box2&& other) = default;
    //...
};

Untuk informasi selengkapnya, lihat Fungsi Default dan Dihapus Secara Eksplisit.

konstruktor constexpr

Konstruktor dapat dinyatakan sebagai constexpr jika

  • ini dinyatakan sebagai default atau memenuhi semua kondisi untuk fungsi constexpr secara umum;
  • kelas tidak memiliki kelas dasar virtual;
  • masing-masing parameter adalah jenis harfiah;
  • isi bukan fungsi try-block;
  • semua anggota data non-statis dan subobjek kelas dasar diinisialisasi;
  • jika kelas adalah (a) serikat yang memiliki anggota varian, atau (b) memiliki serikat anonim, hanya salah satu anggota serikat yang diinisialisasi;
  • setiap anggota data non-statis dari jenis kelas, dan semua subobjek kelas dasar memiliki konstruktor constexpr

Konstruktor daftar penginisialisasi

Jika konstruktor mengambil std::initializer_list<T> sebagai parameternya, dan parameter lain memiliki argumen default, konstruktor tersebut dipilih dalam resolusi kelebihan beban saat kelas dibuat melalui inisialisasi langsung. Anda dapat menggunakan initializer_list untuk menginisialisasi anggota mana pun yang dapat menerimanya. Misalnya, asumsikan kelas Box (ditunjukkan sebelumnya) memiliki std::vector<string> anggota m_contents. Anda dapat menyediakan konstruktor seperti ini:

    Box(initializer_list<string> list, int w = 0, int h = 0, int l = 0)
        : m_contents(list), m_width(w), m_height(h), m_length(l)
{}

Lalu buat objek Box seperti ini:

    Box b{ "apples", "oranges", "pears" }; // or ...
    Box b2(initializer_list<string> { "bread", "cheese", "wine" }, 2, 4, 6);

Konstruktor eksplisit

Jika kelas memiliki konstruktor dengan satu parameter, atau jika semua parameter kecuali memiliki nilai default, jenis parameter dapat dikonversi secara implisit ke jenis kelas. Misalnya, jika Box kelas memiliki konstruktor seperti ini:

Box(int size): m_width(size), m_length(size), m_height(size){}

Dimungkinkan untuk menginisialisasi Kotak seperti ini:

Box b = 42;

Atau teruskan int ke fungsi yang mengambil Box:

class ShippingOrder
{
public:
    ShippingOrder(Box b, double postage) : m_box(b), m_postage(postage){}

private:
    Box m_box;
    double m_postage;
}
//elsewhere...
    ShippingOrder so(42, 10.8);

Konversi tersebut dapat berguna dalam beberapa kasus, tetapi lebih sering dapat menyebabkan kesalahan halus tetapi serius dalam kode Anda. Sebagai aturan umum, Anda harus menggunakan explicit kata kunci pada konstruktor (dan operator yang ditentukan pengguna) untuk mencegah konversi jenis implisit semacam ini:

explicit Box(int size): m_width(size), m_length(size), m_height(size){}

Ketika konstruktor eksplisit, baris ini menyebabkan kesalahan kompilator: ShippingOrder so(42, 10.8);. Untuk informasi selengkapnya, lihat Konversi Jenis yang Ditentukan Pengguna.

Urutan konstruksi

Konstruktor melakukan pekerjaannya dalam urutan ini:

  1. Ini memanggil kelas dasar dan konstruktor anggota dalam urutan deklarasi.

  2. Jika kelas berasal dari kelas dasar virtual, kelas ini menginisialisasi pointer dasar virtual objek.

  3. Jika kelas memiliki atau mewarisi fungsi virtual, kelas akan menginisialisasi penunjuk fungsi virtual objek. Penunjuk fungsi virtual menunjuk ke tabel fungsi virtual kelas untuk mengaktifkan pengikatan panggilan fungsi virtual yang benar ke kode.

  4. Ini menjalankan kode apa pun dalam isi fungsinya.

Contoh berikut menunjukkan urutan di mana kelas dasar dan konstruktor anggota dipanggil di konstruktor untuk kelas turunan. Pertama, konstruktor dasar dipanggil. Kemudian, anggota kelas dasar diinisialisasi dalam urutan muncul dalam deklarasi kelas. Akhirnya, konstruktor turunan dipanggil.

#include <iostream>

using namespace std;

class Contained1 {
public:
    Contained1() { cout << "Contained1 ctor\n"; }
};

class Contained2 {
public:
    Contained2() { cout << "Contained2 ctor\n"; }
};

class Contained3 {
public:
    Contained3() { cout << "Contained3 ctor\n"; }
};

class BaseContainer {
public:
    BaseContainer() { cout << "BaseContainer ctor\n"; }
private:
    Contained1 c1;
    Contained2 c2;
};

class DerivedContainer : public BaseContainer {
public:
    DerivedContainer() : BaseContainer() { cout << "DerivedContainer ctor\n"; }
private:
    Contained3 c3;
};

int main() {
    DerivedContainer dc;
}

Berikut output-nya:

Contained1 ctor
Contained2 ctor
BaseContainer ctor
Contained3 ctor
DerivedContainer ctor

Konstruktor kelas turunan selalu memanggil konstruktor kelas dasar, sehingga dapat mengandalkan kelas dasar yang sepenuhnya dibangun sebelum pekerjaan tambahan selesai. Konstruktor kelas dasar dipanggil dalam urutan derivasi—misalnya, jika ClassA berasal dari ClassB, yang berasal dari ClassC, ClassC konstruktor dipanggil terlebih dahulu, lalu ClassB konstruktor, kemudian ClassA konstruktor.

Jika kelas dasar tidak memiliki konstruktor default, Anda harus menyediakan parameter konstruktor kelas dasar di konstruktor kelas turunan:

class Box {
public:
    Box(int width, int length, int height){
       m_width = width;
       m_length = length;
       m_height = height;
    }

private:
    int m_width;
    int m_length;
    int m_height;
};

class StorageBox : public Box {
public:
    StorageBox(int width, int length, int height, const string label&) : Box(width, length, height){
        m_label = label;
    }
private:
    string m_label;
};

int main(){

    const string aLabel = "aLabel";
    StorageBox sb(1, 2, 3, aLabel);
}

Jika konstruktor melempar pengecualian, urutan penghancuran adalah kebalikan dari urutan konstruksi:

  1. Kode dalam isi fungsi konstruktor adalah unwound.

  2. Objek kelas dasar dan anggota dihancurkan, dalam urutan deklarasi terbalik.

  3. Jika konstruktor tidak mendelegasikan, semua objek dan anggota kelas dasar yang sepenuhnya dibangun akan dihancurkan. Namun, karena objek itu sendiri tidak sepenuhnya dibangun, destruktor tidak dijalankan.

Konstruktor turunan dan inisialisasi agregat yang diperluas

Jika konstruktor kelas dasar bersifat non-publik, tetapi dapat diakses oleh kelas turunan, maka Anda tidak dapat menggunakan kurung kurawal kosong untuk menginisialisasi objek jenis turunan dalam /std:c++17 mode dan yang lebih baru di Visual Studio 2017 dan yang lebih baru.

Contoh berikut menunjukkan perilaku sesuai C++14:

struct Derived;

struct Base {
    friend struct Derived;
private:
    Base() {}
};

struct Derived : Base {};

Derived d1; // OK. No aggregate init involved.
Derived d2 {}; // OK in C++14: Calls Derived::Derived()
               // which can call Base ctor.

Di C++17, Derived sekarang dianggap sebagai jenis agregat. Ini berarti bahwa inisialisasi Base melalui konstruktor default privat terjadi secara langsung, sebagai bagian dari aturan inisialisasi agregat yang diperluas. Sebelumnya, Base konstruktor privat dipanggil melalui Derived konstruktor, dan berhasil karena friend deklarasi.

Contoh berikut menunjukkan perilaku C++17 di Visual Studio 2017 dan yang lebih baru dalam /std:c++17 mode:

struct Derived;

struct Base {
    friend struct Derived;
private:
    Base() {}
};

struct Derived : Base {
    Derived() {} // add user-defined constructor
                 // to call with {} initialization
};

Derived d1; // OK. No aggregate init involved.

Derived d2 {}; // error C2248: 'Base::Base': can't access
               // private member declared in class 'Base'

Konstruktor untuk kelas yang memiliki beberapa warisan

Jika kelas berasal dari beberapa kelas dasar, konstruktor kelas dasar dipanggil dalam urutan di mana mereka tercantum dalam deklarasi kelas turunan:

#include <iostream>
using namespace std;

class BaseClass1 {
public:
    BaseClass1() { cout << "BaseClass1 ctor\n"; }
};
class BaseClass2 {
public:
    BaseClass2() { cout << "BaseClass2 ctor\n"; }
};
class BaseClass3 {
public:
    BaseClass3() { cout << "BaseClass3 ctor\n"; }
};
class DerivedClass : public BaseClass1,
                     public BaseClass2,
                     public BaseClass3
                     {
public:
    DerivedClass() { cout << "DerivedClass ctor\n"; }
};

int main() {
    DerivedClass dc;
}

Anda harus mengharapkan output berikut:

BaseClass1 ctor
BaseClass2 ctor
BaseClass3 ctor
DerivedClass ctor

Mendelegasikan konstruktor

Konstruktor yang mendelegasikan memanggil konstruktor yang berbeda di kelas yang sama untuk melakukan beberapa pekerjaan inisialisasi. Fitur ini berguna ketika Anda memiliki beberapa konstruktor yang semuanya harus melakukan pekerjaan serupa. Anda dapat menulis logika utama dalam satu konstruktor dan memanggilnya dari orang lain. Dalam contoh sepele berikut, Box(int) mendelegasikan pekerjaannya ke Box(int,int,int,int):

class Box {
public:
    // Default constructor
    Box() {}

    // Initialize a Box with equal dimensions (i.e. a cube)
    Box(int i) :  Box(i, i, i)  // delegating constructor
    {}

    // Initialize a Box with custom dimensions
    Box(int width, int length, int height)
        : m_width(width), m_length(length), m_height(height)
    {}
    //... rest of class as before
};

Objek yang dibuat oleh konstruktor sepenuhnya diinisialisasi segera setelah konstruktor selesai. Untuk informasi selengkapnya, lihat Mendelegasikan Konstruktor.

Mewarisi konstruktor (C++11)

Kelas turunan dapat mewarisi konstruktor dari kelas dasar langsung dengan menggunakan deklarasi seperti yang using ditunjukkan dalam contoh berikut:

#include <iostream>
using namespace std;

class Base
{
public:
    Base() { cout << "Base()" << endl; }
    Base(const Base& other) { cout << "Base(Base&)" << endl; }
    explicit Base(int i) : num(i) { cout << "Base(int)" << endl; }
    explicit Base(char c) : letter(c) { cout << "Base(char)" << endl; }

private:
    int num;
    char letter;
};

class Derived : Base
{
public:
    // Inherit all constructors from Base
    using Base::Base;

private:
    // Can't initialize newMember from Base constructors.
    int newMember{ 0 };
};

int main()
{
    cout << "Derived d1(5) calls: ";
    Derived d1(5);
    cout << "Derived d1('c') calls: ";
    Derived d2('c');
    cout << "Derived d3 = d2 calls: " ;
    Derived d3 = d2;
    cout << "Derived d4 calls: ";
    Derived d4;
}

/* Output:
Derived d1(5) calls: Base(int)
Derived d1('c') calls: Base(char)
Derived d3 = d2 calls: Base(Base&)
Derived d4 calls: Base()*/

Visual Studio 2017 dan yang lebih baru: using Pernyataan dalam /std:c++17 mode dan yang lebih baru membawa ke dalam cakupan semua konstruktor dari kelas dasar kecuali yang memiliki tanda tangan yang identik dengan konstruktor di kelas turunan. Secara umum, yang terbaik adalah menggunakan konstruktor warisan ketika kelas turunan menyatakan tidak ada anggota data atau konstruktor baru.

Templat kelas dapat mewarisi semua konstruktor dari argumen jenis jika jenis tersebut menentukan kelas dasar:

template< typename T >
class Derived : T {
    using T::T;   // declare the constructors from T
    // ...
};

Kelas turunan tidak dapat mewarisi dari beberapa kelas dasar jika kelas dasar tersebut memiliki konstruktor yang memiliki tanda tangan yang identik.

Konstruktor dan kelas komposit

Kelas yang berisi anggota jenis kelas dikenal sebagai kelas komposit. Saat anggota jenis kelas dari kelas komposit dibuat, konstruktor dipanggil sebelum konstruktor kelas itu sendiri. Ketika kelas yang terkandung tidak memiliki konstruktor default, Anda harus menggunakan daftar inisialisasi di konstruktor kelas komposit. Dalam contoh sebelumnya StorageBox , jika Anda mengubah jenis m_label variabel anggota ke kelas baru Label , Anda harus memanggil konstruktor kelas dasar dan menginisialisasi m_label variabel di StorageBox konstruktor:

class Label {
public:
    Label(const string& name, const string& address) { m_name = name; m_address = address; }
    string m_name;
    string m_address;
};

class StorageBox : public Box {
public:
    StorageBox(int width, int length, int height, Label label)
        : Box(width, length, height), m_label(label){}
private:
    Label m_label;
};

int main(){
// passing a named Label
    Label label1{ "some_name", "some_address" };
    StorageBox sb1(1, 2, 3, label1);

    // passing a temporary label
    StorageBox sb2(3, 4, 5, Label{ "another name", "another address" });

    // passing a temporary label as an initializer list
    StorageBox sb3(1, 2, 3, {"myname", "myaddress"});
}

Di bagian ini

Lihat juga

Kelas dan struktur