วิธีการเรียกฟังก์ชั่นระดับผู้ปกครองจากฟังก์ชั่นระดับที่ได้รับ?


603

ฉันจะเรียกใช้ฟังก์ชัน parent จากคลาสที่ได้รับโดยใช้ C ++ ได้อย่างไร ตัวอย่างเช่นฉันมีชั้นเรียนที่เรียกว่าparentและชั้นเรียนที่เรียกว่าchildมาจากผู้ปกครอง ภายในแต่ละชั้นมีprintฟังก์ชั่น ในคำจำกัดความของฟังก์ชั่นการพิมพ์ของเด็กฉันต้องการโทรไปยังฟังก์ชั่นการพิมพ์ของผู้ปกครอง ฉันจะทำสิ่งนี้อย่างไร


โซลูชันทั้งหมดข้างต้นนี้สมมติว่าฟังก์ชันการพิมพ์ของคุณเป็นวิธีการคงที่ เป็นกรณีนี้หรือไม่? หากวิธีการนี้ไม่คงที่การแก้ปัญหาข้างต้นจะไม่เกี่ยวข้อง
hhafez

14
ถึงตอนนี้, คุณเข้าใจผิดว่า base :: function () ไวยากรณ์ดูเหมือนว่าการเรียกเมธอดแบบคงที่ แต่มันใช้งานได้สำหรับวิธีการอินสแตนซ์ในบริบทนี้
Motti

2
ฉันจะไม่ใช้ MSVC __super เนื่องจากเป็นแพลตฟอร์มเฉพาะ แม้ว่ารหัสของคุณอาจไม่สามารถทำงานบนแพลตฟอร์มอื่น ๆ ได้ แต่ฉันใช้คำแนะนำอื่น ๆ เนื่องจากเป็นรหัสภาษาที่ตั้งใจไว้
ทีเซอร์


antipattern ที่คลาสที่ได้รับมาจำเป็นต้องเรียกใช้ฟังก์ชันคลาสพาเรนต์คือCall super
Rufus

คำตอบ:


775

ฉันจะเสี่ยงต่อการระบุที่ชัดเจน: คุณเรียกใช้ฟังก์ชันนี้ถ้ามันถูกกำหนดในคลาสพื้นฐานมันจะพร้อมใช้งานโดยอัตโนมัติในคลาสที่ได้รับ (นอกเสียจากว่าprivate)

base_class::foo(...)หากมีฟังก์ชั่นที่มีลายเซ็นเหมือนกันในชั้นเรียนมาคุณสามารถกระจ่างได้โดยการเพิ่มชื่อชั้นฐานที่ตามมาด้วยสองทวิภาค คุณควรทราบว่าแตกต่างจาก Java และ C #, C ++ ไม่ได้มีคำหลักสำหรับ "ชั้นฐาน" a ( superหรือbase) ตั้งแต่ c ++ สนับสนุนมรดกหลายซึ่งอาจนำไปสู่ความคลุมเครือ

class left {
public:
    void foo();
};

class right {
public:
    void foo();
};

class bottom : public left, public right {
public:
    void foo()
    {
        //base::foo();// ambiguous
        left::foo();
        right::foo();

        // and when foo() is not called for 'this':
        bottom b;
        b.left::foo();  // calls b.foo() from 'left'
        b.right::foo();  // call b.foo() from 'right'
    }
};

อนึ่งคุณไม่สามารถสืบทอดโดยตรงจากคลาสเดียวกันสองครั้งเนื่องจากจะไม่มีวิธีการอ้างถึงคลาสพื้นฐานใด ๆ เหนือคลาสอื่น

class bottom : public left, public left { // Illegal
};

31
ทำไมคุณต้องการรับช่วงชั้นเดียวกันสองครั้ง
Paul Brewczynski

65
@ bluesm: ใน OOP แบบคลาสสิกมันไม่สมเหตุสมผล แต่ในการเขียนโปรแกรมทั่วไปtemplate<class A, class B> class C: public A, public B {};อาจมีสองประเภทเหมือนกันด้วยเหตุผลขึ้นอยู่กับการใช้รหัสของคุณ (ที่ทำให้ A และ B เหมือนกัน) อาจเป็นสองหรือสาม วิธีเลเยอร์ที่เป็นนามธรรมจากบางคนไม่ทราบว่าคุณทำอะไร
Emilio Garavaglia

8
ฉันคิดว่ามันมีประโยชน์ที่จะเพิ่มว่าสิ่งนี้จะเรียกวิธีการเรียนระดับผู้ปกครองแม้ว่าจะไม่ได้ดำเนินการโดยตรงในระดับผู้ปกครอง แต่มีการใช้งานในหนึ่งในผู้ปกครองชั้นเรียนในห่วงโซ่การสืบทอด
Maxim Lavrov

4
บน sidenote มันทำให้ฉันเป็นบ้าเมื่อฉันพยายามใส่มันลงในไฟล์ cpp ฉันใช้ 'namespace std' 'left' ถูกกำหนดที่ไหนสักแห่งในเนมสเปซนั้น ตัวอย่างจะไม่รวบรวม - ทำให้ฉันเป็นบ้า :) จากนั้นฉันเปลี่ยน 'ซ้าย' เป็น 'ซ้าย' ตัวอย่างที่ดีโดยวิธีการ
Mathai

72
@Mathai using namespace stdและนั่นคือเหตุผลที่คุณไม่ควรที่จะใช้
JAB

193

เมื่อตั้งชื่อคลาสผู้ปกครองParentและชื่อระดับเด็กChildคุณสามารถทำสิ่งนี้:

class Parent {
public:
    virtual void print(int x);
}

class Child : public Parent {
    void print(int x) override;
}

void Parent::print(int x) {
    // some default behavior
}

void Child::print(int x) {
    // use Parent's print method; implicitly passes 'this' to Parent::print
    Parent::print(x);
}

โปรดทราบว่าParentเป็นชื่อจริงของชั้นเรียนไม่ใช่คำหลัก


แน่นอนว่านี่จะเป็นประโยชน์หากการเรียกฐานถูกสลับกับตรรกะอื่น ๆ มิฉะนั้นจะไม่มีจุดใด ๆ ในการเอาชนะฟังก์ชั่นดังนั้นบางทีมันอาจจะเกินไปไปยังจุด;)
underscore_d

1
@underscore_d อันที่จริงแล้วมันมีประโยชน์แม้ว่าการโทรพื้นฐานจะไม่สลับกับตรรกะอื่น ๆ สมมติว่าคลาสแม่นั้นทำทุกอย่างที่คุณต้องการ แต่จะเปิดเผยวิธีการ foo () ที่คุณไม่ต้องการให้ผู้ใช้เด็กใช้เพราะ foo () นั้นไม่มีความหมายในเด็กหรือผู้โทรภายนอกกับเด็กจะทำให้เด็ก ๆ การทำ ดังนั้นเด็กอาจใช้ parent :: foo () ในบางสถานการณ์ แต่ให้การใช้งาน foo เพื่อให้พวกเขาซ่อน foo ของผู้ปกครอง () จากการถูกเรียก
iheanyi

@iheanyi ฟังดูน่าสนใจ แต่ขอโทษฉันยังไม่เข้าใจเลย คือfoo()ที่นี่คล้ายกับprint()หรือฟังก์ชั่นแยกกันได้อย่างไร และที่คุณหมายถึงโดยใช้privateมรดกเพื่อซ่อนรายละเอียดสืบทอดมาจากฐานและให้publicฟังก์ชั่นสำหรับสิ่งที่คุณแชโดว์ไม่ต้องการเปิดเผย?
underscore_d

@underscore_d ใช่ก็คล้ายคลึงกับfoo() print()ให้ฉันกลับไปใช้print()เพราะฉันคิดว่ามันสมเหตุสมผลดีขึ้นในบริบทนี้ สมมติว่ามีคนสร้างคลาสที่ดำเนินการบางอย่างในประเภทข้อมูลเฉพาะเปิดเผยผู้เข้าถึงบางคนและมีprint(obj&)วิธี ฉันต้องการคลาสใหม่ที่ใช้งานได้array-of-objแต่ทุกอย่างอื่นเหมือนกัน ผลการประพันธ์ในรหัสซ้ำจำนวนมาก การสืบทอดลดขนาดที่ในการprint(array-of-obj&) โทรวนรอบprint(obj&)แต่ไม่ต้องการให้ลูกค้าโทรหาprint(obj&)เนื่องจากไม่เหมาะสมสำหรับพวกเขา
iheanyi

@underscore_d นี่เป็นการบอกกล่าวล่วงหน้าตามข้อสันนิษฐานที่ว่าฉันไม่สามารถกำหนดส่วนต่าง ๆ ของคลาสแม่ดั้งเดิมหรือการทำเช่นนั้นมีราคาแพงอย่างไม่น่าเชื่อ การรับมรดกแบบส่วนตัวสามารถใช้งานได้ แต่จากนั้นคุณสูญเสีย accessors สาธารณะที่คุณพึ่งพา - และดังนั้นจึงจำเป็นต้องทำซ้ำรหัส
iheanyi

32

ถ้าคลาสพื้นฐานของคุณถูกเรียกใช้Baseและฟังก์ชันของคุณถูกเรียกFooBar()คุณสามารถเรียกใช้ได้โดยตรงBase::FooBar()

void Base::FooBar()
{
   printf("in Base\n");
}

void ChildOfBase::FooBar()
{
  Base::FooBar();
}

28

ใน MSVC มีคำหลักเฉพาะของ Microsoft สำหรับสิ่งนั้น: __super


MSDN: ช่วยให้คุณระบุอย่างชัดเจนว่าคุณกำลังเรียกใช้การใช้งานระดับฐานสำหรับฟังก์ชันที่คุณกำลังเอาชนะ

// deriv_super.cpp
// compile with: /c
struct B1 {
   void mf(int) {}
};

struct B2 {
   void mf(short) {}

   void mf(char) {}
};

struct D : B1, B2 {
   void mf(short) {
      __super::mf(1);   // Calls B1::mf(int)
      __super::mf('s');   // Calls B2::mf(char)
   }
};


5
เอ๊ะฉันต้องการtypdefให้ผู้ปกครองเป็นเหมือนอย่างsuperนั้น
Thomas Eding

26
ฉันจะไม่พยายามที่จะแสดงให้เห็นถึงการใช้งานของ__super; ฉันพูดถึงที่นี่เป็นข้อเสนอแนะทางเลือก นักพัฒนาควรรู้คอมไพเลอร์และเข้าใจข้อดีข้อเสียของความสามารถ
Andrey

13
ฉันไม่แนะนำให้ใครใช้เพราะมันเป็นอุปสรรคต่อการพกพาของโค้ดอย่างรุนแรง
Erbureth พูดว่า Reinstate Monica

26
ฉันไม่เห็นด้วยกับ Andrey: นักพัฒนาควรทราบมาตรฐานและไม่จำเป็นต้องรำคาญกับคุณสมบัติของคอมไพเลอร์ถ้าเราพิจารณาการเขียนซอฟต์แวร์ซึ่งส่วนใหญ่เป็นอิสระซึ่งฉันคิดว่าเป็นความคิดที่ดีอย่างไรก็ตามไม่ช้าก็เร็วในโครงการขนาดใหญ่หลายคอมไพเลอร์ มีการใช้ต่อไป
Gabriel

7
"นักพัฒนาควรรู้จักคอมไพเลอร์"เหตุผลนี้และการรวมคุณสมบัติที่ไม่ได้มาตรฐานเป็นสิ่งที่นำไปสู่ ​​IE6 ...
e2-e4

5

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

#include<iostream>
using namespace std;

class Parent
{
  protected:
    virtual void fun(int i)
    {
      cout<<"Parent::fun functionality write here"<<endl;
    }
    void fun1(int i)
    {
      cout<<"Parent::fun1 functionality write here"<<endl;
    }
    void fun2()
    {

      cout<<"Parent::fun3 functionality write here"<<endl;
    }

};

class Child:public Parent
{
  public:
    virtual void fun(int i)
    {
      cout<<"Child::fun partial functionality write here"<<endl;
      Parent::fun(++i);
      Parent::fun2();
    }
    void fun1(int i)
    {
      cout<<"Child::fun1 partial functionality write here"<<endl;
      Parent::fun1(++i);
    }

};
int main()
{
   Child d1;
   d1.fun(1);
   d1.fun1(2);
   return 0;
}

เอาท์พุท:

$ g++ base_function_call_from_derived.cpp
$ ./a.out 
Child::fun partial functionality write here
Parent::fun functionality write here
Parent::fun3 functionality write here
Child::fun1 partial functionality write here
Parent::fun1 functionality write here

1
ขอบคุณที่นำตัวอย่างมาด้วยvirtual!
M.Ioan

5

เรียกใช้วิธีพาเรนต์ด้วยตัวดำเนินการแก้ไขขอบเขตพาเรนต์

ผู้ปกครอง :: วิธีการ ()

class Primate {
public:
    void whatAmI(){
        cout << "I am of Primate order";
    }
};

class Human : public Primate{
public:
    void whatAmI(){
        cout << "I am of Human species";
    }
    void whatIsMyOrder(){
        Primate::whatAmI(); // <-- SCOPE RESOLUTION OPERATOR
    }
};

-15
struct a{
 int x;

 struct son{
  a* _parent;
  void test(){
   _parent->x=1; //success
  }
 }_son;

 }_a;

int main(){
 _a._son._parent=&_a;
 _a._son.test();
}

ตัวอย่างอ้างอิง


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

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