Bagaimana cara menginisialisasi variabel anggota kelas dasar di konstruktor kelas turunan?

123

Mengapa saya tidak bisa melakukan ini?

class A
{
public:
    int a, b;
};

class B : public A
{
    B() : A(), a(0), b(0)
    {
    }

};
amrhassan.dll
sumber
7
Apakah Anda bertanya mengapa Anda tidak bisa melakukan itu, yang merupakan pertanyaan desain bahasa, atau Anda bertanya bagaimana mengatasi keterbatasan bahasa itu?
Rob Kennedy
Saya berpikir bahwa ada cara khusus untuk melakukannya yang tidak saya sadari, tanpa harus menggunakan konstruktor dasar.
amrhassan
Anggota kelas dasar sudah diinisialisasi pada saat konstruktor kelas turunan Anda dijalankan. Anda dapat menetapkannya , jika Anda memiliki akses, atau memanggil setter untuk mereka, atau Anda dapat memberikan nilai untuk mereka ke konstruktor kelas dasar, jika ada yang sesuai. Satu hal yang tidak dapat Anda lakukan di kelas yang dirancang adalah menginisialisasi mereka.
Marquis dari Lorne

Jawaban:

143

Anda tidak dapat menginisialisasi adan bmasuk Bkarena mereka bukan anggota B. Mereka adalah anggota A, oleh karena itu hanya Adapat menginisialisasi mereka. Anda dapat membuatnya menjadi publik, lalu melakukan penugasan B, tetapi itu bukan opsi yang disarankan karena akan merusak enkapsulasi. Sebagai gantinya, buat konstruktor in Ato allow B(atau subclass apa pun dariA ) untuk menginisialisasinya:

class A 
{
protected:
    A(int a, int b) : a(a), b(b) {} // Accessible to derived classes
    // Change "protected" to "public" to allow others to instantiate A.
private:
    int a, b; // Keep these variables private in A
};

class B : public A 
{
public:
    B() : A(0, 0) // Calls A's constructor, initializing a and b in A to 0.
    {
    } 
};
In silico
sumber
32
sementara contoh Anda benar, penjelasan Anda menyesatkan. Ini bukan berarti bahwa Anda tidak dapat menginisialisasi a dan bdi B::B()karena mereka pribadi. Anda tidak dapat menginisialisasi mereka karena mereka bukan anggota class B. Jika Anda menjadikannya publik atau dilindungi, Anda dapat menetapkannya di badan B::B().
R Samuel Klatchko
2
Selain itu, solusi Anda membuat kelas A non-agregat, yang mungkin penting, jadi harus disebutkan.
Gene Bushuyev
1
@R Samuel Klatchko: Poin bagus. Ketika saya menulis jawaban, saya awalnya mengetik "Anda tidak dapat mengakses adan b..." dan mengubahnya menjadi "Anda tidak dapat menginisialisasi ..." tanpa memastikan sisa kalimat masuk akal. Posting telah diedit.
In silico
1
@ Gen Bushuyev: Kelas dalam kode asli dalam pertanyaan bukanlah agregat (ada anggota pribadi non-statis)
David Rodríguez - dribeas
@David - benar, yang merupakan kesalahan pengguna, dan saya mencoba memahami maksud pengguna, melewati dangkal.
Gene Bushuyev
26

Mengesampingkan fakta bahwa mereka private, karena adan bmerupakan anggota A, mereka dimaksudkan untuk diinisialisasi oleh Akonstruktor, bukan oleh konstruktor kelas lain (diturunkan atau tidak).

Mencoba:

class A
{
    int a, b;

protected: // or public:
    A(int a, int b): a(a), b(b) {}
};

class B : public A
{
    B() : A(0, 0) {}
};
NPE
sumber
7

Entah bagaimana, tidak ada yang mencantumkan cara paling sederhana:

class A
{
public:
    int a, b;
};

class B : public A
{
    B()
    {
        a = 0;
        b = 0;
    }

};

Anda tidak dapat mengakses anggota dasar dalam daftar penginisialisasi, tetapi konstruktor itu sendiri, seperti metode anggota lainnya, dapat mengakses publicdan protectedanggota kelas dasar.

Violet Giraffe
sumber
1
Bagus. Apakah ada kekurangan dalam melakukan cara ini?
Wander3r
2
@SaileshD: mungkin ada, jika Anda menginisialisasi objek dengan konstruktor yang mahal. Ini pertama-tama akan diinisialisasi secara default ketika instance Bdialokasikan, kemudian akan ditugaskan di dalam Bkonstruktor. Tetapi menurut saya kompiler juga masih bisa mengoptimalkan ini.
Violet Giraffe
1
Di dalam class Akita tidak bisa mengandalkan adan bdiinisialisasi. Penerapan apa pun class C : public A, misalnya, mungkin lupa untuk memanggil a=0;dan membiarkannya atidak dimulai.
Sparkofska
@Sparkofska, sangat benar. Yang terbaik adalah menginisialisasi bidang secara default baik di tempat saat mendeklarasikannya ( class A { int a = 0;};), atau di konstruktor kelas dasar. Subclass masih bisa menginisialisasi ulang mereka di konstruktornya sesuai kebutuhan.
Violet Giraffe
1
@ Wander3r Kelemahan lainnya adalah tidak semua kelas memiliki operator penugasan. Beberapa hanya dapat dibangun, tetapi tidak ditugaskan. Kemudian Anda selesai ...
Martin Pecka
2
# include<stdio.h>
# include<iostream>
# include<conio.h>

using namespace std;

class Base{
    public:
        Base(int i, float f, double d): i(i), f(f), d(d)
        {
        }
    virtual void Show()=0;
    protected:
        int i;
        float f;
        double d;
};


class Derived: public Base{
    public:
        Derived(int i, float f, double d): Base( i, f, d)
        {
        }
        void Show()
        {
            cout<< "int i = "<<i<<endl<<"float f = "<<f<<endl <<"double d = "<<d<<endl;
        }
};

int main(){
    Base * b = new Derived(10, 1.2, 3.89);
    b->Show();
    return 0;
}

Ini adalah contoh yang berfungsi jika Anda ingin menginisialisasi anggota data kelas Basis yang ada di objek kelas Derived, sedangkan Anda ingin mendorong antarmuka nilai ini melalui panggilan konstruktor kelas Derived.

manish srivastava
sumber
1

Meskipun ini berguna dalam kasus yang jarang terjadi (jika bukan itu masalahnya, bahasa akan mengizinkannya secara langsung), lihat Basis dari idiom Anggota . Ini bukan solusi bebas kode, Anda harus menambahkan lapisan tambahan warisan, tetapi itu menyelesaikan pekerjaan. Untuk menghindari kode boilerplate, Anda dapat menggunakan implementasi boost

Nikos Athanasiou
sumber
0

Kenapa kamu tidak bisa melakukannya? Karena bahasa tidak memungkinkan Anda untuk menginisialisasi daftar penginisialisasi kelas dasar 'anggota dalam kelas turunan'.

Bagaimana Anda bisa menyelesaikannya? Seperti ini:

class A
{
public:
    A(int a, int b) : a_(a), b_(b) {};
    int a_, b_;
};

class B : public A
{
public:
    B() : A(0,0) 
    {
    }
};
John Dibling
sumber
-1

Jika Anda tidak menentukan visibilitas untuk anggota kelas, defaultnya adalah "pribadi". Anda harus menjadikan anggota Anda pribadi atau dilindungi jika Anda ingin mengakses mereka di subkelas.

TotoroTotoro
sumber
-1

Kelas agregat, seperti A dalam contoh Anda (*), harus memiliki anggotanya publik, dan tidak memiliki konstruktor yang ditentukan pengguna. Mereka diinisialisasi dengan daftar penginisialisasi, misalnya A a {0,0};atau dalam kasus Anda B() : A({0,0}){}. Anggota kelas agregat dasar tidak dapat diinisialisasi secara individual dalam konstruktor dari kelas turunan.

(*) Tepatnya, seperti yang disebutkan dengan benar, asli class Abukanlah gabungan karena anggota pribadi non-statis

Gene Bushuyev
sumber