คำว่า "อินเตอร์เฟส" ใน C ++


11

Java ทำให้ความแตกต่างที่ชัดเจนระหว่างและclass interface(ฉันเชื่อว่า C # ทำเช่นนั้น แต่ฉันไม่มีประสบการณ์ด้วย) เมื่อเขียน C ++ อย่างไรก็ตามไม่มีภาษาบังคับให้แยกความแตกต่างระหว่างคลาสและส่วนต่อประสาน

ดังนั้นฉันมักจะดูอินเตอร์เฟซเป็นวิธีแก้ปัญหาสำหรับการขาดการสืบทอดหลายใน Java การทำให้ความแตกต่างดังกล่าวให้ความรู้สึกโดยพลการและไร้ความหมายใน C ++

ฉันมักจะไปกับวิธีการ "เขียนสิ่งต่าง ๆ ในวิธีที่ชัดเจนที่สุด" ดังนั้นใน C ++ ฉันมีสิ่งที่เรียกว่าส่วนติดต่อใน Java เช่น:

class Foo {
public:
  virtual void doStuff() = 0;
  ~Foo() = 0;
};

และฉันก็ตัดสินใจว่าผู้ดำเนินการส่วนใหญ่Fooต้องการแบ่งปันฟังก์ชั่นทั่วไปบางอย่างที่ฉันอาจจะเขียน:

class Foo {
public:
  virtual void doStuff() = 0;
  ~Foo() {}
protected:
  // If it needs this to do its thing:
  int internalHelperThing(int);
  // Or if it doesn't need the this pointer:
  static int someOtherHelper(int);
};

ซึ่งทำให้สิ่งนี้ไม่ใช่ส่วนติดต่อในความหมายของ Java อีกต่อไป

C ++ มีสองแนวคิดที่สำคัญซึ่งเกี่ยวข้องกับปัญหาการสืบทอดพื้นฐาน:

  1. virtual inhertiance
  2. คลาสที่ไม่มีตัวแปรสมาชิกสามารถครอบครองพื้นที่พิเศษเมื่อใช้เป็นฐาน

    "subobjects คลาสพื้นฐานอาจมีขนาดเป็นศูนย์"

    การอ้างอิง

ในบรรดาที่ฉันพยายามหลีกเลี่ยง # 1 เป็นไปได้ - มันยากที่จะเผชิญหน้ากับสถานการณ์ที่จริง ๆ คือการออกแบบที่ "สะอาด" แต่ความแตกต่างที่สำคัญระหว่างความเข้าใจของฉันเกี่ยวกับคำว่า "อินเทอร์เฟซ" และคุณลักษณะภาษา C ++ ด้วยเหตุนี้ในปัจจุบันฉัน (เกือบ) ไม่เคยอ้างถึงสิ่งต่าง ๆ ว่า "อินเทอร์เฟซ" ใน C ++ และพูดในแง่ของคลาสพื้นฐานและขนาดของมัน ฉันจะบอกว่าในบริบทของ C ++ "interface" เป็นชื่อเรียกผิด

มันมาถึงความสนใจของฉันแม้ว่าจะมีไม่กี่คนที่ทำให้ความแตกต่างดังกล่าว

  1. ฉันทนต่อการสูญเสียสิ่งใดโดยอนุญาตให้ (เช่นprotected) virtualฟังก์ชันที่ไม่ใช่อยู่ใน "ส่วนต่อประสาน" ใน C ++ หรือไม่ (ความรู้สึกของฉันเป็นสิ่งที่ตรงกันข้าม - เป็นตำแหน่งที่เป็นธรรมชาติมากขึ้นสำหรับรหัสที่แชร์)
  2. คือ "อินเตอร์เฟซ" คำที่มีความหมายในภาษา C ++ - ไม่ได้บ่งบอกถึงเพียงบริสุทธิ์virtualหรือมันจะยุติธรรมโทรเรียน c ++ ไม่มีตัวแปรสมาชิกอินเตอร์เฟซที่ยัง?

C # มีหลายอินเทอร์เฟซที่สืบทอดเหมือนกัน, สืบทอดเดี่ยวของการใช้งานเป็น Java, แต่ผ่านการใช้วิธีการขยายทั่วไป , internalHelperThingสามารถจำลองในเกือบทุกกรณี
Sjoerd

โปรดทราบว่าเป็นการปฏิบัติที่ไม่ถูกต้องในการเปิดเผยสมาชิกเสมือน
Klaim

สาธารณะ~Foo() {}ในระดับนามธรรมเป็นข้อผิดพลาดภายใต้ (เกือบ) ทุกสถานการณ์
Wolf

คำตอบ:


11

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

เกี่ยวกับตัวอย่างของคุณ: ใน Java (และคล้ายกันใน C #) รหัสของคุณอาจบ่งบอกถึงความกังวล:

interface IFoo {/*  ... */} // here is your interface

class FooBase implements IFoo 
{
     // make default implementations for interface methods
}

class Foo extends FooBase
{
}

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


3
ความหมายที่เป็นไปได้อีกอย่างหนึ่งของ "อินเตอร์เฟส" สำหรับโปรแกรมเมอร์ C ++ คือpublicส่วนต่าง ๆ ของ.hไฟล์
David Thornley

ตัวอย่างรหัสของคุณเป็นภาษาอะไร ถ้านั่นคือความพยายามในการเป็น C ++ ฉันกำลังจะร้องไห้ ...
Qix - MONICA WISTREATED

@Qix: ทำให้ง่ายต่อการอ่านการโพสต์ของฉันอีกครั้ง (มันระบุไว้อย่างชัดเจนว่ารหัสอยู่ใน Java) และในกรณีที่คุณได้> 2000 คะแนนคุณสามารถแก้ไขการโพสต์ของฉันเพื่อเพิ่มรหัสตัวอย่าง C ++ ที่เทียบเท่าได้ ชัดเจน.
Doc Brown

หากเป็นจาวานั่นก็ยังผิดและฉันไม่สามารถแก้ไขได้ อักขระไม่เพียงพอที่จะเปลี่ยน Java ไม่ได้ใช้ colons ในการนำไปใช้ / ยืดออกซึ่งเป็นสาเหตุที่ฉันสงสัยว่ามันเป็นความพยายามที่ C ++ ...
Qix - MONICA WAS MISTREATED

@Qix: หากคุณพบปัญหาเกี่ยวกับการสร้างประโยคเพิ่มเติมคุณสามารถทำให้พวกเขาเป็นของขวัญคริสมาสต์ :-)
Doc Brown Brown

7

ดูเหมือนว่าคุณอาจตกอยู่ในกับดักของความสับสนในความหมายของสิ่งที่มันเป็นอินเทอร์เฟซเป็นแนวความคิดเป็นทั้งการใช้งาน (อินเทอร์เฟซ - ตัวพิมพ์เล็ก 'i') และนามธรรม (อินเทอร์เฟซ )

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

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

ฉันจะสูญเสียสิ่งใดหรือไม่ด้วยการอนุญาตให้ (เช่นการป้องกัน) ฟังก์ชั่นที่ไม่ใช่เสมือนอยู่ภายใน "ส่วนต่อประสาน" ใน C ++? (ความรู้สึกของฉันเป็นสิ่งที่ตรงกันข้าม - เป็นตำแหน่งที่เป็นธรรมชาติมากขึ้นสำหรับรหัสที่แชร์)

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

คำว่า "อินเทอร์เฟซ" มีความหมายใน C ++ - มันบอกเป็นนัย ๆ ว่าเป็นเวอร์ชวลเสมือนจริงหรือมันจะยุติธรรมที่จะเรียกคลาส C ++ ที่ไม่มีตัวแปรสมาชิกที่อินเตอร์เฟสยังคงอยู่?

ยกโทษให้ฉัน แต่มันนานมากแล้วที่ฉันได้เขียนใบสมัครอย่างจริงจังใน C ++ ฉันจำได้ว่าอินเทอร์เฟซเป็นคำสำคัญสำหรับจุดประสงค์ของสิ่งที่เป็นนามธรรมดังที่ฉันได้อธิบายไว้ที่นี่ ฉันจะไม่เรียกคลาส C ++ ของการเรียงลำดับใด ๆ ที่เป็นอินเทอร์เฟซฉันจะบอกว่าคลาสนั้นมีอินเทอร์เฟซภายในความหมายที่ฉันได้กล่าวไว้ข้างต้น ในแง่นั้นคำนั้นมีความหมาย แต่ขึ้นอยู่กับบริบท


1
+1 สำหรับความแตกต่างระหว่าง "มี" และ "เป็น" อินเทอร์เฟซ
Doc Brown

6

อินเทอร์เฟซ Java ไม่ใช่ "วิธีแก้ปัญหา" พวกมันเป็นการตัดสินใจออกแบบโดยเจตนาเพื่อหลีกเลี่ยงปัญหาบางอย่างกับการสืบทอดหลายอย่างเช่นการสืบทอดเพชรและเพื่อสนับสนุนการออกแบบที่ลดการมีเพศสัมพันธ์

อินเทอร์เฟซของคุณด้วยวิธีการที่ได้รับการป้องกันเป็นกรณีของตำราเรียนที่ต้องการ "ชอบการแต่งเพลงมากกว่าการรับมรดก" หากต้องการอ้างอิงจาก Sutter และ Alexandrescu ในส่วนของชื่อนั้นจากมาตรฐานการเข้ารหัส C ++ ที่ยอดเยี่ยม:

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

ด้วยการรวมฟังก์ชั่นตัวช่วยของคุณในอินเทอร์เฟซของคุณคุณอาจประหยัดการพิมพ์เพียงเล็กน้อยในตอนนี้ Foo*มันมักจะยาวที่ดีกว่าในระยะที่จะทำให้การทำงานของคุณผู้ช่วยแยกและผ่านใน

STL เป็นตัวอย่างที่ดีในเรื่องนี้ ฟังก์ชันตัวช่วยมากเท่าที่จะทำได้จะถูกดึงออกมา<algorithm>แทนที่จะอยู่ในคลาสของคอนเทนเนอร์ ตัวอย่างเช่นเนื่องจากsort()ใช้ public container API เพื่อทำงานคุณจึงรู้ว่าคุณสามารถใช้อัลกอริทึมการเรียงลำดับของคุณเองโดยไม่ต้องเปลี่ยนรหัส STL ใด ๆ การออกแบบนี้ช่วยให้ห้องสมุดอย่างบูสต์ซึ่งเพิ่มประสิทธิภาพ STL แทนที่จะแทนที่โดยไม่จำเป็นต้องรู้อะไรเกี่ยวกับบูสต์


2

ฉันจะสูญเสียสิ่งใดหรือไม่ด้วยการอนุญาตให้ (เช่นการป้องกัน) ฟังก์ชั่นที่ไม่ใช่เสมือนอยู่ภายใน "ส่วนต่อประสาน" ใน C ++? (ความรู้สึกของฉันเป็นสิ่งที่ตรงกันข้าม - เป็นตำแหน่งที่เป็นธรรมชาติมากขึ้นสำหรับรหัสที่แชร์)

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

นี่เป็นหนึ่งในอินสแตนซ์ที่ไม่มีคำตอบที่เหมาะกับทุกขนาดและคุณต้องใช้ประสบการณ์และการตัดสินใจของคุณในการตัดสินใจ หากคุณสามารถพูดด้วยความมั่นใจ 100% ว่าทุกประเภทรองเป็นไปได้ของระดับมิฉะนั้นอย่างเต็มที่เสมือนของคุณFooมักจะต้องมีการดำเนินการตามวิธีการป้องกันbar()นั้นFooเป็นสถานที่ที่เหมาะสมสำหรับมัน เมื่อคุณมีคลาสย่อยBazที่ไม่ต้องการbar()คุณต้องอยู่กับความจริงที่ว่าBazจะมีการเข้าถึงโค้ดมันไม่ควรหรือผ่านการฝึกการเรียงลำดับชั้นของคลาสใหม่ อดีตไม่ใช่การฝึกฝนที่ดีและหลังอาจใช้เวลานานกว่าสองสามนาทีมันจะต้องใช้เวลาในการจัดการสิ่งต่าง ๆ ให้ถูกต้องตั้งแต่แรก

คำว่า "อินเทอร์เฟซ" มีความหมายใน C ++ - มันบอกเป็นนัย ๆ ว่าเป็นเวอร์ชวลเสมือนจริงหรือมันจะยุติธรรมที่จะเรียกคลาส C ++ ที่ไม่มีตัวแปรสมาชิกที่อินเตอร์เฟสยังคงอยู่?

ส่วนที่ 10.4 ของมาตรฐาน C ++ ทำให้มีการกล่าวถึงการใช้คลาสนามธรรมเพื่อนำอินเตอร์เฟสมาใช้ แต่ไม่ได้กำหนดอย่างเป็นทางการ คำนี้มีความหมายในบริบทของวิทยาศาสตร์คอมพิวเตอร์ทั่วไปและใครก็ตามที่มีอำนาจควรเข้าใจว่า " Fooเป็นอินเทอร์เฟซสำหรับ (อะไรก็ตาม)" แสดงถึงรูปแบบของสิ่งที่เป็นนามธรรม ผู้ที่สัมผัสกับภาษาที่มีการสร้างส่วนต่อประสานที่กำหนดไว้อาจคิดว่าเป็นเวอร์ชวลเพียว แต่ใครก็ตามที่ต้องการใช้งานด้วยFooจะได้ดูคำจำกัดความของมันก่อนดำเนินการต่อ


2
อะไรคือความแตกต่างระหว่างการให้การประกาศเสมือนแท้และการประกาศบวกกับการใช้งาน ในทั้งสองกรณีจะต้องมีการดำเนินการและbazสามารถมีการดำเนินการเช่นreturn false;หรืออะไรก็ตาม หากวิธีนี้ใช้ไม่ได้กับคลาสย่อยทั้งหมดจะไม่อยู่ในคลาสนามธรรมพื้นฐานในรูปแบบใด ๆ
David Thornley

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