ฉันจะเริ่มต้นตัวแปรสมาชิกคลาสพื้นฐานในตัวสร้างคลาสที่ได้รับมาได้อย่างไร


123

ทำไมฉันทำไม่ได้

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

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

};

7
คุณกำลังถามว่าทำไมคุณทำไม่ได้ซึ่งเป็นคำถามเกี่ยวกับการออกแบบภาษาหรือคุณกำลังถามว่าจะแก้ไขข้อ จำกัด ด้านภาษานั้นอย่างไร
Rob Kennedy

ฉันคิดว่ามีวิธีพิเศษบางอย่างที่จะทำโดยที่ฉันไม่รู้โดยไม่ต้องใช้ตัวสร้างฐาน
amrhassan

สมาชิกคลาสพื้นฐานถูกเตรียมใช้งานแล้วตามเวลาที่คอนสตรัคเตอร์คลาสที่ได้รับของคุณเริ่มทำงาน คุณสามารถกำหนดได้ถ้าคุณมีสิทธิ์เข้าถึงหรือเรียกตัวตั้งค่าสำหรับพวกเขาหรือคุณสามารถกำหนดค่าให้กับตัวสร้างคลาสพื้นฐานได้หากมีค่าที่เหมาะสม สิ่งหนึ่งที่คุณไม่สามารถทำได้ในชั้นเรียนที่วางแผนไว้คือเริ่มต้นสิ่งเหล่านี้
Marquis of Lorne

คำตอบ:


143

คุณไม่สามารถเริ่มต้นaและbในเพราะพวกเขาไม่ได้เป็นสมาชิกของB Bพวกเขาเป็นสมาชิกAดังนั้นจึงAสามารถเริ่มต้นได้เท่านั้น คุณสามารถทำให้เป็นสาธารณะจากนั้นทำการมอบหมายได้Bแต่นั่นไม่ใช่ตัวเลือกที่แนะนำเนื่องจากจะทำลายการห่อหุ้ม ให้สร้างคอนสตรัคเตอร์Aเพื่ออนุญาตB(หรือคลาสย่อยใด ๆ ของA) เพื่อเตรียมใช้งาน:

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

32
ในขณะที่ตัวอย่างของคุณถูกต้อง แต่คำอธิบายของคุณทำให้เข้าใจผิด ไม่ใช่ว่าคุณไม่สามารถเริ่มต้น aและbเข้าได้B::B()เนื่องจากเป็นแบบส่วนตัว class Bคุณไม่สามารถเริ่มต้นพวกเขาเพราะพวกเขาไม่ได้เป็นสมาชิกของ ถ้าคุณทำให้พวกเขาได้รับการป้องกันสาธารณะหรือคุณอาจจะกำหนดB::B()ไว้ในร่างกายของ
R Samuel Klatchko

2
นอกจากนี้โซลูชันของคุณยังทำให้คลาส A ไม่รวมซึ่งอาจมีความสำคัญดังนั้นจึงต้องกล่าวถึง
Gene Bushuyev

1
@R Samuel Klatchko: จุดดี ตอนที่เขียนคำตอบตอนแรกพิมพ์ว่า "You can't access aand b... " และเปลี่ยนเป็น "You can't initialize ... " โดยไม่แน่ใจว่าประโยคที่เหลือนั้นสมเหตุสมผล แก้ไขโพสต์แล้ว
ในซิลิโค

1
@Gene Bushuyev: คลาสในรหัสเดิมในคำถามไม่ใช่การรวม (มีสมาชิกส่วนตัวที่ไม่คงที่)
David Rodríguez - dribeas

@David - ถูกต้องซึ่งเป็นข้อผิดพลาดของผู้ใช้และฉันพยายามทำตามความตั้งใจของผู้ใช้โดยข้ามเพียงผิวเผิน
Gene Bushuyev

26

ทิ้งความจริงที่ว่าพวกเขาเป็นprivateตั้งแต่aและbเป็นสมาชิกของAพวกเขาถูกกำหนดให้เริ่มต้นโดยAผู้สร้างไม่ใช่โดยผู้สร้างของคลาสอื่น ๆ (ได้มาหรือไม่)

ลอง:

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

7

อย่างไรก็ตามไม่มีใครระบุวิธีที่ง่ายที่สุด:

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

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

};

คุณไม่สามารถเข้าถึงสมาชิกพื้นฐานในรายการตัวเริ่มต้นได้ แต่ตัวสร้างเองเช่นเดียวกับเมธอดสมาชิกอื่น ๆ สามารถเข้าถึงpublicและprotectedสมาชิกของคลาสพื้นฐานได้


1
ดี มีข้อเสียเปรียบในการทำเช่นนี้หรือไม่?
Wander3r

2
@SaileshD: อาจมีถ้าคุณเริ่มต้นวัตถุด้วยตัวสร้างที่มีราคาแพง ขั้นแรกจะเริ่มต้นโดยค่าเริ่มต้นเมื่อBมีการจัดสรรอินสแตนซ์จากนั้นจะถูกกำหนดไว้ภายในตัวBสร้าง แต่ฉันก็คิดว่าคอมไพเลอร์ยังคงสามารถเพิ่มประสิทธิภาพนี้ได้
ยีราฟสีม่วง

1
ภายในclass Aเราไม่สามารถพึ่งพาaและbเริ่มต้นได้ class C : public Aตัวอย่างเช่นการดำเนินการใด ๆอาจลืมโทรa=0;และปล่อยไว้โดยaไม่ได้เริ่มต้น
Sparkofska

@Sparkofska จริงมาก. วิธีที่ดีที่สุดคือการเริ่มต้นฟิลด์เริ่มต้นโดยปริยายเมื่อประกาศ ( class A { int a = 0;};) หรือในตัวสร้างของคลาสฐาน คลาสย่อยยังคงสามารถกำหนดค่าเริ่มต้นใหม่ในตัวสร้างได้ตามต้องการ
Violet Giraffe

1
@ Wander3r ข้อเสียเปรียบอีกประการหนึ่งคือไม่ใช่ทุกชั้นเรียนที่มีตัวดำเนินการกำหนด บางส่วนสามารถสร้างได้เท่านั้น แต่ไม่สามารถกำหนดให้ เสร็จแล้ว ...
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;
}

เป็นตัวอย่างการทำงานในกรณีที่คุณต้องการเริ่มต้นสมาชิกข้อมูลคลาสฐานที่มีอยู่ในออบเจ็กต์คลาสที่ได้รับมาในขณะที่คุณต้องการส่งค่าเหล่านี้เชื่อมต่อผ่านการเรียกตัวสร้างคลาสที่ได้รับ


1

ขณะนี้เป็นประโยชน์ในกรณีที่หายาก (ถ้าที่ไม่ได้กรณีภาษาที่จะได้รับอนุญาตได้โดยตรง) ใช้เวลาดูที่ฐานจากสมาชิกสำนวน ไม่ใช่วิธีการแก้ปัญหาโดยไม่ต้องใช้รหัสคุณต้องเพิ่มการสืบทอดอีกชั้นหนึ่ง แต่จะทำให้งานสำเร็จ เพื่อหลีกเลี่ยงรหัสสำเร็จรูปคุณสามารถใช้การเพิ่มประสิทธิภาพ


0

ทำไมถึงทำไม่ได้ เนื่องจากภาษาไม่อนุญาตให้คุณเริ่มต้นคลาสพื้นฐาน 'สมาชิกในรายการเริ่มต้นของคลาสที่ได้รับ'

คุณจะทำสิ่งนี้ได้อย่างไร? แบบนี้:

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

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

-1

หากคุณไม่ระบุการเปิดเผยสำหรับสมาชิกชั้นเรียนค่าเริ่มต้นจะเป็น "ส่วนตัว" คุณควรกำหนดให้สมาชิกของคุณเป็นส่วนตัวหรือได้รับการปกป้องหากคุณต้องการเข้าถึงสมาชิกในคลาสย่อย


-1

คลาสรวมเช่น A ในตัวอย่างของคุณ (*) ต้องให้สมาชิกเป็นสาธารณะและไม่มีตัวสร้างที่ผู้ใช้กำหนดเอง พวกเขาจะ intialized กับรายการ initializer เช่นหรือในกรณีของคุณA a {0,0}; B() : A({0,0}){}สมาชิกของคลาสการรวมฐานไม่สามารถเริ่มต้นทีละตัวในตัวสร้างของคลาสที่ได้รับ

(*) เพื่อให้ถูกต้องตามที่กล่าวไว้อย่างถูกต้องต้นฉบับclass Aไม่ได้เป็นการรวมเนื่องจากสมาชิกส่วนตัวที่ไม่คงที่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.