ความแตกต่างระหว่างคืออะไร
public
,private
และprotected
มรดกในc ++?
คำถามทั้งหมดที่ฉันพบในการจัดการกับกรณีเฉพาะ
ความแตกต่างระหว่างคืออะไร
public
,private
และprotected
มรดกในc ++?
คำถามทั้งหมดที่ฉันพบในการจัดการกับกรณีเฉพาะ
คำตอบ:
เพื่อตอบคำถามนั้นฉันต้องการอธิบาย accessors ของสมาชิกก่อนด้วยคำพูดของตัวเอง หากคุณรู้สิ่งนี้แล้วให้ข้ามไปที่หัวข้อ "ถัดไป:"
มีสาม accessors ว่าฉันรู้คือpublic
, protected
และprivate
และ
ปล่อย:
class Base {
public:
int publicMember;
protected:
int protectedMember;
private:
int privateMember;
};
Base
ก็เป็นสิ่งที่Base
มีpublicMember
อยู่เช่นกันBase
protectedMember
Base
รู้privateMember
นอกจากโดย "ตระหนักถึง" ฉันหมายถึง "ยอมรับการมีอยู่ของและสามารถเข้าถึงได้"
สิ่งเดียวกันนี้เกิดขึ้นกับการสืบทอดมรดกที่เป็นสาธารณะและเป็นส่วนตัว ลองพิจารณาชั้นBase
และชั้นเรียนที่สืบทอดจากChild
Base
public
ทุกอย่างที่ตระหนักถึงBase
และChild
ยังเป็นที่ทราบว่าสืบทอดมาจากChild
Base
protected
เท่านั้นChild
และลูกของมันจะรู้ว่าพวกเขาสืบทอดมาจากBase
และเด็กที่มีความตระหนักว่าพวกเขาได้รับมรดกจากprivate
ไม่มีใครอื่นนอกจากการChild
รับรู้ถึงมรดกSomeBase
SomeBase
สิ่งนี้ก็เหมือนกับสมาชิกคนอื่น ๆ ที่มีตัวระบุการเข้าถึงซึ่งใช้ตัวควบคุมเดียวกันกับการเข้าถึงภายนอก
class A
{
public:
int x;
protected:
int y;
private:
int z;
};
class B : public A
{
// x is public
// y is protected
// z is not accessible from B
};
class C : protected A
{
// x is protected
// y is protected
// z is not accessible from C
};
class D : private A // 'private' is default for classes
{
// x is private
// y is private
// z is not accessible from D
};
หมายเหตุสำคัญ: คลาส B, C และ D ทั้งหมดมีตัวแปร x, y และ z มันเป็นเพียงคำถามของการเข้าถึง
การใช้งานเกี่ยวกับการคุ้มครองและมรดกส่วนตัวที่คุณสามารถอ่านได้ที่นี่
การ จำกัด การเปิดเผยของการสืบทอดจะทำให้โค้ดไม่สามารถมองเห็นได้ว่าบางคลาสสืบทอดคลาสอื่น: การแปลงโดยนัยจากที่ได้รับไปยังฐานจะไม่ทำงานและ static_cast
จากฐานไปยังที่ได้รับมาจะไม่ทำงาน
มีเพียงสมาชิก / เพื่อนของคลาสเท่านั้นที่สามารถเห็นการสืบทอดส่วนตัวและเฉพาะสมาชิก / เพื่อนและคลาสที่ได้รับเท่านั้นที่สามารถดูการสืบทอดที่ได้รับการป้องกันได้
มรดกสาธารณะ
เป็นมรดก ปุ่มคือหน้าต่างและที่ใดก็ได้ที่ต้องการหน้าต่างก็สามารถส่งปุ่มได้เช่นกัน
class button : public window { };
การป้องกันการสืบทอด
มีการป้องกันการใช้งานในแง่ของ มีประโยชน์น้อยมาก ใช้ในboost::compressed_pair
การรับมาจากคลาสที่ว่างเปล่าและบันทึกหน่วยความจำโดยใช้การเพิ่มประสิทธิภาพคลาสฐานที่ว่างเปล่า (ตัวอย่างด้านล่างไม่ได้ใช้แม่แบบเพื่อให้เป็นจุด):
struct empty_pair_impl : protected empty_class_1
{ non_empty_class_2 second; };
struct pair : private empty_pair_impl {
non_empty_class_2 &second() {
return this->second;
}
empty_class_1 &first() {
return *this; // notice we return *this!
}
};
มรดกส่วนตัว
ดำเนินการในแง่ของ การใช้คลาสพื้นฐานนั้นใช้สำหรับการใช้คลาสที่ได้รับเท่านั้น มีประโยชน์กับคุณลักษณะและหากขนาดมีความสำคัญ (ลักษณะว่างเปล่าที่มีเฉพาะฟังก์ชันจะใช้ประโยชน์จากการเพิ่มประสิทธิภาพคลาสฐานที่ว่างเปล่า) บ่อยครั้งที่การกักกันเป็นทางออกที่ดีกว่า ขนาดของสายอักขระมีความสำคัญอย่างยิ่งดังนั้นจึงเป็นการใช้งานที่เห็นได้บ่อยที่นี่
template<typename StorageModel>
struct string : private StorageModel {
public:
void realloc() {
// uses inherited function
StorageModel::realloc();
}
};
สมาชิกสาธารณะ
สรุป
class pair {
public:
First first;
Second second;
};
accessors
class window {
public:
int getWidth() const;
};
สมาชิกที่ได้รับการป้องกัน
ให้การเข้าถึงที่ปรับปรุงสำหรับคลาสที่ได้รับ
class stack {
protected:
vector<element> c;
};
class window {
protected:
void registerClass(window_descriptor w);
};
สมาชิกส่วนตัว
เก็บรายละเอียดการใช้งาน
class window {
private:
int width;
};
โปรดทราบว่า C-style casts อนุญาตให้ทำการแคสต์คลาสที่ได้รับไปยังคลาสฐานที่ได้รับการปกป้องหรือเป็นส่วนตัวในลักษณะที่กำหนดและปลอดภัย สิ่งนี้ควรหลีกเลี่ยงค่าใช้จ่ายทั้งหมดเนื่องจากสามารถทำให้รหัสขึ้นอยู่กับรายละเอียดการใช้งาน - แต่ถ้าจำเป็นคุณสามารถใช้เทคนิคนี้ได้
คำหลักสามคำนี้ยังใช้ในบริบทที่แตกต่างอย่างสิ้นเชิงเพื่อระบุรูปแบบการสืบทอดการมองเห็นรูปแบบการมองเห็นการรับมรดก
ตารางนี้รวบรวมชุดค่าผสมที่เป็นไปได้ทั้งหมดของการประกาศส่วนประกอบและโมเดลการสืบทอดที่แสดงการเข้าถึงผลลัพธ์ไปยังคอมโพเนนต์เมื่อกำหนดคลาสย่อยอย่างสมบูรณ์
ตารางข้างต้นถูกตีความด้วยวิธีดังต่อไปนี้ (ดูที่แถวแรก):
ถ้าเป็นส่วนประกอบจะมีการประกาศให้เป็นสาธารณะและระดับนี้จะได้รับมรดกในฐานะประชาชนที่เกิดการเข้าถึงเป็นสาธารณะ
ตัวอย่าง:
class Super {
public: int p;
private: int q;
protected: int r;
};
class Sub : private Super {};
class Subsub : public Sub {};
ส่งผลให้การเข้าถึงสำหรับตัวแปรp
, q
, r
ในชั้นเรียนSubsubเป็นใคร
ตัวอย่างอื่น:
class Super {
private: int x;
protected: int y;
public: int z;
};
class Sub : protected Super {};
ส่งผลให้การเข้าถึงสำหรับตัวแปรy
, z
ในระดับตำบลจะได้รับการคุ้มครองและตัวแปรx
คือใคร
ตัวอย่างที่มีรายละเอียดเพิ่มเติม:
class Super {
private:
int storage;
public:
void put(int val) { storage = val; }
int get(void) { return storage; }
};
int main(void) {
Super object;
object.put(100);
object.put(object.get());
cout << object.get() << endl;
return 0;
}
ตอนนี้ให้กำหนดคลาสย่อย:
class Sub : Super { };
int main(void) {
Sub object;
object.put(100);
object.put(object.get());
cout << object.get() << endl;
return 0;
}
คลาสที่กำหนดชื่อ Sub ซึ่งเป็นคลาสย่อยของคลาสที่มีชื่อSuper
หรือSub
คลาสนั้นได้มาจากSuper
คลาส Sub
แนะนำระดับค่าตัวแปรใหม่หรือฟังก์ชั่นใหม่ มันหมายความว่าวัตถุใด ๆ ของSub
คลาสสืบทอดลักษณะทั้งหมดหลังจากที่Super
อยู่ในความเป็นจริงสำเนาของSuper
วัตถุคลาส?
ไม่ไม่มันไม่ได้
หากเรารวบรวมรหัสต่อไปนี้เราจะไม่ได้รับอะไรนอกจากการรวบรวมข้อผิดพลาดที่บอกว่าput
และget
วิธีการนั้นไม่สามารถเข้าถึงได้ ทำไม?
เมื่อเราละเว้นระบุการมองเห็นคอมไพเลอร์สันนิษฐานว่าเราจะใช้สิ่งที่เรียกว่ามรดกส่วนตัว หมายความว่าส่วนประกอบซูเปอร์คลาสสาธารณะทั้งหมดเปลี่ยนเป็นการเข้าถึงแบบส่วนตัวส่วนประกอบซูเปอร์คลาสส่วนตัวจะไม่สามารถเข้าถึงได้เลย หมายความว่าคุณไม่ได้รับอนุญาตให้ใช้งานส่วนหลังภายในคลาสย่อย
เราต้องแจ้งคอมไพเลอร์ว่าเราต้องการรักษานโยบายการเข้าถึงที่ใช้ก่อนหน้านี้
class Sub : public Super { };
อย่าหลงผิด : ไม่ได้หมายความว่าส่วนประกอบส่วนตัวของคลาส Super (เช่นตัวแปรหน่วยเก็บข้อมูล) จะเปลี่ยนเป็นสาธารณะในลักษณะที่น่าอัศจรรย์ เอกชนส่วนประกอบจะยังคงเอกชน , ประชาชน จะยังคงอยู่ของประชาชน
วัตถุในSub
ชั้นเรียนอาจทำสิ่งเดียวกันกับพี่น้องที่สร้างขึ้นจากSuper
ชั้นเรียน "เกือบ"เพราะความเป็นจริงของการเป็นประเภทรองยังหมายถึงว่าการเข้าถึงชั้นหายไปส่วนประกอบส่วนตัวของซับคลาส เราไม่สามารถเขียนฟังก์ชันสมาชิกของSub
คลาสซึ่งจะสามารถจัดการตัวแปรหน่วยเก็บข้อมูลได้โดยตรง
นี่เป็นข้อ จำกัด ที่ร้ายแรงมาก มีวิธีแก้ปัญหาหรือไม่?
ใช่แล้ว
ระดับการเข้าถึงที่สามเรียกว่าได้รับการคุ้มครอง คำหลักที่มีการป้องกันหมายความว่าองค์ประกอบที่มีเครื่องหมายมันทำงานเหมือนประชาชนคนหนึ่งเมื่อนำมาใช้โดยการใด ๆ ของ subclasses และรูปลักษณ์ที่เหมือนส่วนตัวกับส่วนที่เหลือของโลก - สิ่งนี้เป็นจริงสำหรับคลาสที่สืบทอดสู่สาธารณะเท่านั้น (เช่นคลาส Super ในตัวอย่างของเรา) -
class Super {
protected:
int storage;
public:
void put(int val) { storage = val; }
int get(void) { return storage; }
};
class Sub : public Super {
public:
void print(void) {cout << "storage = " << storage;}
};
int main(void) {
Sub object;
object.put(100);
object.put(object.get() + 1);
object.print();
return 0;
}
ในขณะที่คุณเห็นในตัวอย่างโค้ดที่เราฟังก์ชั่นใหม่ให้กับSub
การเรียนและมันไม่สิ่งหนึ่งที่สำคัญ: มันเข้าถึงตัวแปรจัดเก็บจากชั้นซูเปอร์
มันจะเป็นไปไม่ได้ถ้าประกาศตัวแปรเป็นส่วนตัว ในขอบเขตฟังก์ชั่นหลักตัวแปรยังคงซ่อนอยู่ดังนั้นถ้าคุณเขียนอะไรเช่น:
object.storage = 0;
error: 'int Super::storage' is protected
คอมไพเลอร์จะแจ้งให้ทราบว่ามันเป็น
ในที่สุดโปรแกรมสุดท้ายจะสร้างผลลัพธ์ต่อไปนี้:
storage = 101
มันเกี่ยวข้องกับวิธีการที่สมาชิกสาธารณะของคลาสฐานนั้นถูกเปิดเผยจากคลาสที่ได้รับมา
ตามที่ litb ชี้ให้เห็นการสืบทอดสาธารณะเป็นมรดกดั้งเดิมที่คุณจะเห็นในภาษาการเขียนโปรแกรมส่วนใหญ่ นั่นคือแบบจำลองความสัมพันธ์ "IS-A" การรับมรดกส่วนตัวสิ่งที่แปลกประหลาดกับ C ++ คือความสัมพันธ์ "ดำเนินการในข้อกำหนดของ" นั่นคือคุณต้องการใช้ส่วนต่อประสานสาธารณะในคลาสที่ได้รับ แต่ไม่ต้องการให้ผู้ใช้ของคลาสที่ได้รับนั้นสามารถเข้าถึงส่วนต่อประสานนั้นได้ หลายคนแย้งว่าในกรณีนี้คุณควรรวมคลาสพื้นฐานนั่นคือแทนที่จะมีคลาสฐานเป็นฐานส่วนตัวให้เป็นสมาชิกของมาเพื่อนำกลับมาใช้ฟังก์ชั่นของชั้นฐาน
Member in base class : Private Protected Public
ประเภทมรดก : วัตถุที่รับช่วงเป็น :
Private : Inaccessible Private Private
Protected : Inaccessible Protected Protected
Public : Inaccessible Protected Public
1) มรดกสาธารณะ :
สมาชิกส่วนตัวของคลาสฐานไม่สามารถเข้าถึงได้ในคลาสที่ได้รับ
ข สมาชิกที่ได้รับการป้องกันของคลาสฐานจะได้รับการป้องกันในคลาสที่ได้รับมา
ค. สมาชิกสาธารณะของคลาสฐานยังคงเป็นสาธารณะในคลาสที่ได้รับมา
ดังนั้นคลาสอื่น ๆ สามารถใช้สมาชิกสาธารณะของคลาสฐานผ่านคลาสอ็อบเจ็กต์ที่ได้รับ
2) การป้องกันมรดก :
สมาชิกส่วนตัวของคลาสฐานไม่สามารถเข้าถึงได้ในคลาสที่ได้รับ
ข สมาชิกที่ได้รับการป้องกันของคลาสฐานจะได้รับการป้องกันในคลาสที่ได้รับมา
ค. สมาชิกสาธารณะของคลาสฐานกลายเป็นสมาชิกที่ได้รับการป้องกันของคลาสที่ได้รับมาด้วย
ดังนั้นคลาสอื่นไม่สามารถใช้สมาชิกสาธารณะของคลาสฐานผ่านคลาสอ็อบเจ็กต์ที่ได้รับ แต่จะพร้อมใช้งานกับคลาสย่อยของ Derived
3) มรดกส่วนตัว :
สมาชิกส่วนตัวของคลาสฐานไม่สามารถเข้าถึงได้ในคลาสที่ได้รับ
ข สมาชิกที่ได้รับการป้องกันและสาธารณะของคลาสพื้นฐานจะกลายเป็นสมาชิกส่วนตัวของคลาสที่ได้รับมา
ดังนั้นไม่มีสมาชิกของคลาสฐานที่สามารถเข้าถึงได้โดยคลาสอื่นผ่านวัตถุคลาสที่ได้รับเนื่องจากพวกเขาเป็นส่วนตัวในคลาสที่ได้รับมา ดังนั้นแม้แต่คลาสย่อยของคลาส Derived ก็ไม่สามารถเข้าถึงได้
การสืบทอดสาธารณะเป็นแบบจำลองความสัมพันธ์ IS-A กับ
class B {};
class D : public B {};
ทุกเป็นD
B
การรับมรดกส่วนบุคคลเป็นแบบจำลองความสัมพันธ์แบบ IS-IMPLEMENTED-USING (หรือสิ่งที่เรียกว่า) กับ
class B {};
class D : private B {};
D
คือไม่B
แต่ทุกD
ใช้มันB
ในการดำเนินงานของ การสืบทอดส่วนตัวสามารถกำจัดได้โดยใช้การกักกันแทน:
class B {};
class D {
private:
B b_;
};
นี้D
ก็สามารถจะดำเนินการโดยใช้ในกรณีนี้ใช้มันB
b_
การบรรจุเป็นข้อต่อที่แน่นน้อยกว่าระหว่างชนิดมากกว่าการสืบทอดดังนั้นโดยทั่วไปควรเลือกใช้ บางครั้งการใช้การกักกันแทนการสืบทอดส่วนตัวไม่สะดวกเท่ากับการรับมรดกส่วนตัว บ่อยครั้งที่ข้อแก้ตัวอ่อนแอสำหรับการขี้เกียจ
ฉันไม่คิดว่าใครจะรู้ว่าprotected
แบบจำลองการรับมรดกคืออะไร อย่างน้อยฉันก็ยังไม่เห็นคำอธิบายที่น่าเชื่อถือใด ๆ
D
บุคลากรเอกชนจากมันสามารถแทนที่การทำงานเสมือนจริงของD
B
(ถ้ายกตัวอย่างเช่นB
เป็นอินเตอร์เฟซผู้สังเกตการณ์แล้วD
สามารถใช้มันและผ่านthis
ฟังก์ชั่นที่กำหนดให้ auch อินเตอร์เฟซโดยไม่ต้องทุกคนจะสามารถใช้D
ในฐานะผู้สังเกตการณ์.) นอกจากนี้D
การคัดเลือกจะทำให้สมาชิกของที่มีอยู่ในอินเตอร์เฟซของมันด้วยการทำB
using B::member
ทั้งสองมีความไม่สะดวกในการใช้ syntactically เมื่อB
เป็นสมาชิก
protected
มรดกที่ผมเคยพบว่ามีประโยชน์กับvirtual
ชั้นฐานและprotected
ctor:struct CommonStuff { CommonStuff(Stuff*) {/* assert !=0 */ } }; struct HandlerMixin1 : protected virtual CommonStuff { protected: HandlerMixin1() : CommonStuff(nullptr) {} /*...*/ }; struct Handler : HandlerMixin1, ... { Handler(Stuff& stuff) : CommonStuff(&stuff) {} };
ถ้าคุณสืบทอดต่อสาธารณะจากคลาสอื่นทุกคนรู้ว่าคุณกำลังสืบทอดและคุณสามารถใช้ polymorphically โดยใครก็ได้ผ่านตัวชี้คลาสพื้นฐาน
หากคุณสืบทอดการป้องกันเพียงชั้นเรียนลูกของคุณจะสามารถใช้คุณ polymorphically
หากคุณสืบทอดโดยส่วนตัวเพียงอย่างเดียวคุณจะสามารถดำเนินการวิธีการเรียนระดับผู้ปกครอง
ซึ่งโดยทั่วไปจะเป็นสัญลักษณ์ของความรู้ที่เหลือในชั้นเรียนมีเกี่ยวกับความสัมพันธ์ของคุณกับชั้นผู้ปกครองของคุณ
สมาชิกข้อมูลที่ได้รับความคุ้มครองสามารถเข้าถึงได้โดยคลาสใด ๆ ที่สืบทอดจากคลาสของคุณ อย่างไรก็ตามข้อมูลส่วนตัวสมาชิกไม่สามารถ สมมติว่าเรามีดังต่อไปนี้:
class MyClass {
private:
int myPrivateMember; // lol
protected:
int myProtectedMember;
};
จากภายในส่วนขยายของคุณไปยังคลาสนี้การอ้างอิงthis.myPrivateMember
จะไม่ทำงาน อย่างไรก็ตามthis.myProtectedMember
จะ ค่านี้จะยังคงห่อหุ้มดังนั้นหากเรามีการเริ่มของชั้นนี้ที่เรียกว่าmyObj
แล้วmyObj.myProtectedMember
จะไม่ทำงานจึงเป็นสิ่งที่คล้ายกันในการทำงานเพื่อเป็นสมาชิกข้อมูลส่วนตัว
Accessors | Base Class | Derived Class | World
—————————————+————————————+———————————————+———————
public | y | y | y
—————————————+————————————+———————————————+———————
protected | y | y | n
—————————————+————————————+———————————————+———————
private | | |
or | y | n | n
no accessor | | |
y: accessible
n: not accessible
จากตัวอย่างนี้สำหรับ java ... ฉันคิดว่าตารางเล็ก ๆ น่าจะคุ้มค่าพันคำ :)
สรุป:
เมื่อรับช่วงคุณสามารถ (ในบางภาษา) เปลี่ยนประเภทการป้องกันของข้อมูลสมาชิกในทิศทางที่แน่นอนเช่นจากการป้องกันสู่สาธารณะ
สมาชิกส่วนตัวของคลาสฐานสามารถเข้าถึงได้โดยสมาชิกของคลาสฐานนั้นเท่านั้น
สมาชิกสาธารณะของคลาสฐานสามารถเข้าถึงได้โดยสมาชิกของคลาสฐานนั้นสมาชิกของคลาสที่ได้รับรวมถึงสมาชิกที่อยู่นอกคลาสพื้นฐานและคลาสที่ได้รับ
สมาชิกที่ได้รับความคุ้มครองของคลาสพื้นฐานสามารถเข้าถึงได้โดยสมาชิกของคลาสพื้นฐานเช่นเดียวกับสมาชิกของคลาสที่ได้รับ
ส่วนตัว : ฐาน
ได้รับการป้องกัน : ฐาน + มา
สาธารณะ : ฐาน + มา + สมาชิกคนอื่น ๆ
ฉันพบคำตอบง่ายๆและคิดว่าจะโพสต์ไว้เพื่ออ้างอิงในอนาคตของฉันด้วย
มันมาจากลิงค์ http://www.learncpp.com/cpp-tutorial/115-inheritance-and-access-specifiers/
class Base
{
public:
int m_nPublic; // can be accessed by anybody
private:
int m_nPrivate; // can only be accessed by Base member functions (but not derived classes)
protected:
int m_nProtected; // can be accessed by Base member functions, or derived classes.
};
class Derived: public Base
{
public:
Derived()
{
// Derived's access to Base members is not influenced by the type of inheritance used,
// so the following is always true:
m_nPublic = 1; // allowed: can access public base members from derived class
m_nPrivate = 2; // not allowed: can not access private base members from derived class
m_nProtected = 3; // allowed: can access protected base members from derived class
}
};
int main()
{
Base cBase;
cBase.m_nPublic = 1; // allowed: can access public members from outside class
cBase.m_nPrivate = 2; // not allowed: can not access private members from outside class
cBase.m_nProtected = 3; // not allowed: can not access protected members from outside class
}
มันคือการป้องกันการเข้าถึงของสาธารณะและสมาชิกที่ได้รับการป้องกันของคลาสฐานในคลาสที่ได้รับ ด้วยการสืบทอดสาธารณะคลาสที่ได้รับสามารถเห็นสาธารณะและสมาชิกที่ได้รับการป้องกันของฐาน ด้วยการสืบทอดส่วนตัวไม่สามารถทำได้ ด้วยการป้องกันคลาสที่ได้รับและคลาสใด ๆ ที่ได้รับจากสิ่งนั้นสามารถมองเห็นได้