ทำไมต้องเลือกวางโครงสร้างบนคลาส?


476

การเล่นกับ Swift ที่มาจากพื้นหลัง Java ทำไมคุณต้องการเลือก Struct แทนที่จะเป็น Class ดูเหมือนว่าพวกเขาจะเป็นสิ่งเดียวกันโดยมีโครงสร้างที่เสนอฟังก์ชันการทำงานน้อยลง ทำไมต้องเลือกมัน?


11
โครงสร้างจะถูกคัดลอกเสมอเมื่อมันถูกส่งผ่านไปในรหัสของคุณและไม่ใช้การนับการอ้างอิง แหล่งที่มา: developer.apple.com/library/prerelease/ios/documentation/swift/…
holex

4
ฉันจะบอกว่า structs มีความเหมาะสมมากขึ้นในการเก็บข้อมูลไม่ใช่ตรรกะ หากต้องการพูดในแง่ของจาวาลองนึกภาพ structs ว่า "Value Objects"
Vincent Guerci

6
ฉันประหลาดใจในการสนทนาทั้งหมดนี้ไม่มีการเอ่ยถึงการทำสำเนาแบบขี้เกียจที่เรียกว่าcopy-on-writeโดยตรง ความกังวลใด ๆ เกี่ยวกับประสิทธิภาพการคัดลอก struct เป็นส่วนใหญ่สงสัยในการออกแบบนี้
เดวิดเจมส์

3
การเลือกโครงสร้างของคลาสไม่ใช่เรื่องของความคิดเห็น มีเหตุผลเฉพาะในการเลือกข้อใดข้อหนึ่ง
David James

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

คำตอบ:


548

จากการที่ WWDC 2015 ได้รับความนิยมอย่างมากพูดคุยเกี่ยวกับการเขียนโปรแกรมเชิงโพรโทคอลใน Swift ( วิดีโอ , การถอดเสียง ) Swift มีคุณสมบัติหลายอย่างที่ทำให้โครงสร้างดีกว่าคลาสในหลาย ๆ สถานการณ์

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

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

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

การพูดวางโครงเรื่องเหล่านี้ในกรณีที่ต้องการเรียน:

  • การคัดลอกหรือการเปรียบเทียบอินสแตนซ์ไม่สมเหตุสมผล (เช่นหน้าต่าง)
  • อายุการใช้งานอินสแตนซ์เชื่อมโยงกับผลกระทบภายนอก (เช่น TemporaryFile)
  • อินสแตนซ์เป็นเพียง "sinks" - conduits แบบเขียนอย่างเดียวไปยังสถานะภายนอก (เช่น CGGContext)

มันบอกเป็นนัยว่า structs ควรเป็นค่าเริ่มต้นและคลาสควรเป็นทางเลือก

ในทางตรงกันข้ามเอกสารภาษาการเขียนโปรแกรม Swiftค่อนข้างขัดแย้ง:

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

ตามแนวทางทั่วไปให้พิจารณาการสร้างโครงสร้างเมื่อมีเงื่อนไขเหล่านี้อย่างน้อยหนึ่งข้อ:

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

ตัวอย่างของผู้สมัครที่ดีสำหรับโครงสร้างรวมถึง:

  • ขนาดของรูปทรงเรขาคณิตซึ่งอาจห่อหุ้มคุณสมบัติความกว้างและคุณสมบัติความสูงทั้งสองประเภทเป็นคู่
  • วิธีการอ้างถึงช่วงภายในชุดข้อมูลอาจจะห่อหุ้มคุณสมบัติเริ่มต้นและคุณสมบัติความยาวทั้งสองประเภทเป็น Int
  • จุดหนึ่งในระบบพิกัด 3 มิติซึ่งอาจจะห่อหุ้มคุณสมบัติ x, y และ z ซึ่งเป็นประเภท Double แต่ละประเภท

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

นี่คือการอ้างว่าเราควรเริ่มต้นการใช้คลาสและโครงสร้างการใช้งานเฉพาะในสถานการณ์ที่เฉพาะเจาะจง ในที่สุดคุณต้องเข้าใจความหมายของประเภทค่าเทียบกับประเภทอ้างอิงแล้วคุณสามารถทำการตัดสินใจอย่างชาญฉลาดเกี่ยวกับเวลาที่จะใช้ structs หรือคลาส นอกจากนี้โปรดทราบว่าแนวคิดเหล่านี้มีการพัฒนาอยู่ตลอดเวลาและมีการเขียนเอกสารภาษา Swift Programming Language ก่อนที่จะมีการพูดคุยเกี่ยวกับ Protocol Oriented Programming


12
@ElgsQianChen จุดทั้งหมดของการเขียนนี้คือ struct ควรจะเลือกโดยค่าเริ่มต้นและควรใช้คลาสเมื่อจำเป็นเท่านั้น โครงสร้างมีความปลอดภัยและปราศจากข้อบกพร่องมากโดยเฉพาะในสภาพแวดล้อมแบบมัลติเธรด ใช่คุณสามารถใช้คลาสแทน struct ได้ แต่ structs จะดีกว่า
drewag

16
@drewag นั่นน่าจะตรงกันข้ามกับสิ่งที่พูด มันบอกว่าคลาสควรเป็นค่าเริ่มต้นที่คุณใช้ไม่ใช่โครงสร้างIn practice, this means that most custom data constructs should be classes, not structures.คุณสามารถอธิบายให้ฉันฟังได้อย่างไรหลังจากอ่านแล้วคุณจะได้รับชุดข้อมูลส่วนใหญ่ที่ควรเป็นโครงสร้างและไม่ใช่คลาส พวกเขาให้ชุดของกฎเฉพาะเมื่อสิ่งที่ควรจะเป็นโครงสร้างและสวยมากพูดว่า "สถานการณ์อื่น ๆ ทั้งหมดชั้นดีกว่า"
แมตต์

42
บรรทัดสุดท้ายควรพูดว่า "คำแนะนำส่วนตัวของฉันตรงข้ามกับเอกสาร:" ... แล้วมันเป็นคำตอบที่ยอดเยี่ยม!
Dan Rosenstark

5
หนังสือ Swift 2.2 ยังบอกว่าใช้คลาสในสถานการณ์ส่วนใหญ่
David James

6
โครงสร้างผ่าน Class นั้นลดความซับซ้อนลงอย่างแน่นอน แต่ความหมายของการใช้หน่วยความจำคืออะไรเมื่อ structs กลายเป็นตัวเลือกเริ่มต้น เมื่อสิ่งต่าง ๆ ถูกคัดลอกไปทุกที่แทนการอ้างอิงมันควรเพิ่มการใช้หน่วยความจำโดยแอพ ไม่ควรหรือ
MadNik

164

เนื่องจากอินสแตนซ์ของโครงสร้างถูกจัดสรรบนสแต็กและอินสแตนซ์ของคลาสนั้นถูกจัดสรรบนฮีปบางครั้งโครงสร้างจึงสามารถเร็วขึ้นอย่างมาก

อย่างไรก็ตามคุณควรวัดด้วยตัวคุณเองและตัดสินใจตามกรณีการใช้งานเฉพาะของคุณ

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

class Int10Class {
    let value1, value2, value3, value4, value5, value6, value7, value8, value9, value10: Int
    init(_ val: Int) {
        self.value1 = val
        self.value2 = val
        self.value3 = val
        self.value4 = val
        self.value5 = val
        self.value6 = val
        self.value7 = val
        self.value8 = val
        self.value9 = val
        self.value10 = val
    }
}

struct Int10Struct {
    let value1, value2, value3, value4, value5, value6, value7, value8, value9, value10: Int
    init(_ val: Int) {
        self.value1 = val
        self.value2 = val
        self.value3 = val
        self.value4 = val
        self.value5 = val
        self.value6 = val
        self.value7 = val
        self.value8 = val
        self.value9 = val
        self.value10 = val
    }
}

func + (x: Int10Class, y: Int10Class) -> Int10Class {
    return IntClass(x.value + y.value)
}

func + (x: Int10Struct, y: Int10Struct) -> Int10Struct {
    return IntStruct(x.value + y.value)
}

ประสิทธิภาพถูกวัดโดยใช้

// Measure Int10Class
measure("class (10 fields)") {
    var x = Int10Class(0)
    for _ in 1...10000000 {
        x = x + Int10Class(1)
    }
}

// Measure Int10Struct
measure("struct (10 fields)") {
    var y = Int10Struct(0)
    for _ in 1...10000000 {
        y = y + Int10Struct(1)
    }
}

func measure(name: String, @noescape block: () -> ()) {
    let t0 = CACurrentMediaTime()

    block()

    let dt = CACurrentMediaTime() - t0
    print("\(name) -> \(dt)")
}

สามารถดูรหัสได้ที่https://github.com/knguyen2708/StructVsClassPerformance

อัปเดต (27 มีนาคม 2018) :

ตั้งแต่ Swift 4.0, Xcode 9.2, กำลังรัน Release build บน iPhone 6S, iOS 11.2.6, การตั้งค่า Swift Compiler คือ-O -whole-module-optimization:

  • class รุ่นใช้เวลา 2.06 วินาที
  • struct เวอร์ชั่นใช้เวลา 4.17e-08 วินาที (เร็วกว่า 50,000,000 ครั้ง)

(ฉันไม่ได้ใช้การวิ่งหลายครั้งโดยเฉลี่ยอีกต่อไปเนื่องจากความต่างน้อยมากต่ำกว่า 5%)

หมายเหตุ : ความแตกต่างนั้นมีความสำคัญน้อยกว่ามากหากไม่มีการปรับแต่งโมดูลทั้งหมด ฉันจะดีใจถ้ามีคนชี้ให้เห็นว่าธงทำอะไรได้จริง


อัพเดท (7 พฤษภาคม 2559) :

ตั้งแต่ Swift 2.2.1, Xcode 7.3, รัน build build บน iPhone 6S, iOS 9.3.1, เฉลี่ยมากกว่า 5 run, การตั้งค่า Swift Compiler คือ-O -whole-module-optimization:

  • class รุ่นใช้เวลา 2.159942142 วินาที
  • struct รุ่นใช้เวลา 5.83E-08s (เร็วขึ้น 37,000,000 เท่า)

หมายเหตุ : เนื่องจากมีคนกล่าวว่าในสถานการณ์จริงจะมีมากกว่า 1 ฟิลด์ใน struct ฉันได้เพิ่มการทดสอบสำหรับ structs / คลาสที่มี 10 ฟิลด์แทนที่จะเป็น 1 น่าแปลกที่ผลลัพธ์ไม่ได้แตกต่างกันมากนัก


ผลลัพธ์ดั้งเดิม (1 มิถุนายน 2014):

(วิ่งบน struct / คลาสที่มี 1 ฟิลด์ไม่ใช่ 10)

ตั้งแต่ Swift 1.2, Xcode 6.3.2, กำลังรัน Release build บน iPhone 5S, iOS 8.3, เฉลี่ยมากกว่า 5 run

  • class รุ่นเอา 9.788332333s
  • struct รุ่นใช้เวลา 0.010532942s (เร็วขึ้น 900 เท่า)

ผลลัพธ์เก่า (จากเวลาที่ไม่รู้จัก)

(วิ่งบน struct / คลาสที่มี 1 ฟิลด์ไม่ใช่ 10)

ด้วยรุ่นวางจำหน่ายใน MacBook Pro ของฉัน:

  • classรุ่นเอา 1.10082 วินาที
  • structรุ่นเอา 0.02324 วินาที (50 ครั้งเร็วกว่า)

27
จริง แต่ดูเหมือนว่าการคัดลอกชุดของโครงสร้างรอบ ๆ จะช้ากว่าการคัดลอกการอ้างอิงไปยังวัตถุเดียว กล่าวอีกอย่างหนึ่งก็คือเร็วกว่าการคัดลอกตัวชี้เดียวรอบกว่าเพื่อคัดลอกบล็อกหน่วยความจำขนาดใหญ่โดยพลการ
Tylerc230

14
-1 การทดสอบนี้ไม่ได้เป็นตัวอย่างที่ดีเพราะมีเพียง var เดียวใน struct โปรดทราบว่าถ้าคุณเพิ่มค่าหลายค่าและวัตถุหนึ่งหรือสองรุ่น struct จะเทียบเคียงกับรุ่นคลาส ยิ่งคุณเพิ่ม vars ยิ่งเวอร์ชัน struct ช้าลงเท่าไหร่
joshrl

6
@joshrl ได้คะแนนของคุณ แต่ตัวอย่างคือ "ดี" หรือไม่ขึ้นอยู่กับสถานการณ์ที่เฉพาะเจาะจง รหัสนี้ถูกดึงมาจากแอพของฉันเองดังนั้นมันจึงเป็นกรณีการใช้งานที่ถูกต้องและการใช้ structs ได้ปรับปรุงประสิทธิภาพการทำงานของแอพของฉันอย่างหนาแน่น มันอาจไม่ใช่กรณีการใช้งานทั่วไป (เช่นกันกรณีการใช้งานทั่วไปคือสำหรับแอพส่วนใหญ่ไม่มีใครสนใจว่าข้อมูลจะถูกส่งผ่านไปได้เร็วแค่ไหนเนื่องจากคอขวดเกิดขึ้นที่อื่นเช่นการเชื่อมต่อเครือข่าย สำคัญเมื่อคุณมีอุปกรณ์ GHz ที่มี GB หรือ RAM)
Khanh Nguyen

26
เท่าที่ฉันเข้าใจการคัดลอกใน swift ถูกปรับให้เกิดขึ้นในเวลา WRITE ซึ่งหมายความว่าไม่มีการทำสำเนาหน่วยความจำทางกายภาพเว้นแต่ว่าจะมีการแก้ไขสำเนาใหม่
Matjan

6
คำตอบนี้แสดงตัวอย่างที่ไม่สำคัญอย่างยิ่งจนถึงจุดที่ไม่สมจริงดังนั้นจึงไม่ถูกต้องสำหรับหลาย ๆ กรณี คำตอบที่ดีกว่าคือ "ขึ้นอยู่กับ"
iwasrobbed

60

ความคล้ายคลึงกันระหว่าง structs และคลาส

ฉันสร้างส่วนสำคัญสำหรับสิ่งนี้ด้วยตัวอย่างง่ายๆ https://github.com/objc-swift/swift-classes-vs-structures

และความแตกต่าง

1. การสืบทอด

โครงสร้างไม่สามารถสืบทอดได้อย่างรวดเร็ว ถ้าคุณต้องการ

class Vehicle{
}

class Car : Vehicle{
}

ไปเรียน

2. ผ่าน

โครงสร้าง Swift ส่งผ่านค่าและอินสแตนซ์ของคลาสส่งผ่านโดยอ้างอิง

ความแตกต่างตามบริบท

โครงสร้างคงที่และตัวแปร

ตัวอย่าง (ใช้ที่ WWDC 2014)

struct Point{

   var x = 0.0;
   var y = 0.0;

} 

กำหนด struct ชื่อ Point

var point = Point(x:0.0,y:2.0)

ทีนี้ถ้าฉันลองเปลี่ยน x มันเป็นการแสดงออกที่ถูกต้อง

point.x = 5

แต่ถ้าฉันกำหนดจุดเป็นค่าคงที่

let point = Point(x:0.0,y:2.0)
point.x = 5 //This will give compile time error.

ในกรณีนี้จุดทั้งหมดคงที่ไม่เปลี่ยนแปลง

ถ้าฉันใช้คลาสพอยต์แทนนี่เป็นนิพจน์ที่ถูกต้อง เพราะในค่าคงที่ไม่เปลี่ยนชั้นของคลาสเป็นการอ้างอิงถึงคลาสเองไม่ใช่ตัวแปรอินสแตนซ์ (ยกเว้นตัวแปรที่กำหนดเป็นค่าคงที่)


คุณสามารถสืบทอด structs ใน Swift gist.github.com/AliSoftware/9e4946c8b6038572d678
thatguy

12
สรุปสาระสำคัญข้างต้นคือเกี่ยวกับวิธีการที่เราสามารถบรรลุรสชาติการสืบทอดสำหรับ struct คุณจะเห็นไวยากรณ์เช่น ตอบ: B. มันเป็นโครงสร้างที่เรียกว่า A ใช้โพรโทคอลที่เรียกว่า B. เอกสารประกอบของ Apple อย่างชัดเจนว่าโครงสร้างไม่รองรับการถ่ายทอดทางพันธุกรรมที่บริสุทธิ์และไม่ได้
MadNik

2
ผู้ชายที่ย่อหน้าสุดท้ายของคุณยอดเยี่ยมมาก ฉันรู้อยู่เสมอว่าคุณสามารถเปลี่ยนค่าคงที่ได้ ... แต่เมื่อเวลาผ่านไปฉันเห็นว่าคุณทำไม่ได้ดังนั้นฉันจึงงุนงง ความแตกต่างนี้ทำให้มองเห็นได้
ฮันนี่

28

ต่อไปนี้เป็นเหตุผลอื่นที่ควรพิจารณา:

  1. structs รับ initializer อัตโนมัติที่คุณไม่จำเป็นต้องบำรุงรักษาในโค้ดเลย

    struct MorphProperty {
       var type : MorphPropertyValueType
       var key : String
       var value : AnyObject
    
       enum MorphPropertyValueType {
           case String, Int, Double
       }
     }
    
     var m = MorphProperty(type: .Int, key: "what", value: "blah")

ในการรับสิ่งนี้ในคลาสคุณจะต้องเพิ่ม initializer และบำรุงรักษา intializer ...

  1. ประเภทคอลเลกชันพื้นฐานเช่นArraystructs ยิ่งคุณใช้มันในรหัสของคุณเองมากเท่าไหร่คุณก็ยิ่งคุ้นเคยกับการส่งผ่านค่าโดยไม่ต้องอ้างอิง ตัวอย่างเช่น

    func removeLast(var array:[String]) {
       array.removeLast()
       println(array) // [one, two]
    }
    
    var someArray = ["one", "two", "three"]
    removeLast(someArray)
    println(someArray) // [one, two, three]
  2. เห็นได้ชัดว่าไม่สามารถเปลี่ยนแปลงได้และความไม่แน่นอนเป็นหัวข้อใหญ่ แต่คนฉลาดจำนวนมากคิดว่าการเปลี่ยนแปลงไม่ได้ - โครงสร้างในกรณีนี้ - เป็นที่นิยมมากกว่า วัตถุที่เปลี่ยนแปลงไม่ได้และไม่เปลี่ยนรูป


4
เป็นเรื่องจริงที่คุณจะได้รับการเริ่มต้นอัตโนมัติ นอกจากนี้คุณยังจะได้รับการเริ่มต้นว่างเปล่าเมื่อคุณสมบัติทั้งหมดเป็นตัวเลือก แต่ถ้าคุณมีโครงสร้างใน Framework คุณจำเป็นต้องเขียน initialiser ด้วยตนเองถ้าคุณต้องการให้มันพร้อมใช้งานนอกinternalขอบเขต
Abizern

2
@Abizern ยืนยันแล้ว - stackoverflow.com/a/26224873/8047 - และคนที่น่ารำคาญ
Dan Rosenstark

2
@Abizern มีเหตุผลที่ยอดเยี่ยมสำหรับทุกสิ่งใน Swift แต่ทุกครั้งที่มีบางสิ่งที่เป็นจริงในที่เดียวและไม่ใช่ที่อื่นผู้พัฒนาต้องรู้สิ่งเพิ่มเติม ฉันเดาว่าที่นี่ฉันควรจะพูดว่า "มันน่าตื่นเต้นที่ได้ทำงานในภาษาที่ท้าทาย!"
Dan Rosenstark

4
ฉันยังสามารถเพิ่มได้หรือไม่ว่ามันไม่สามารถเปลี่ยนแปลงได้ของโครงสร้างที่มีประโยชน์ (แม้ว่ามันจะเป็นสิ่งที่ดีมาก) คุณสามารถกลายพันธุ์ structs แต่คุณต้องทำเครื่องหมายวิธีการmutatingเพื่อให้คุณชัดเจนเกี่ยวกับฟังก์ชั่นการเปลี่ยนแปลงสถานะของพวกเขา แต่ธรรมชาติของพวกเขาในฐานะประเภทคุณค่าคือสิ่งที่สำคัญ หากคุณประกาศโครงสร้างด้วยletคุณจะไม่สามารถเรียกใช้ฟังก์ชันการกลายพันธุ์ใด ๆ ได้ วิดีโอ WWDC 15 เกี่ยวกับการตั้งโปรแกรมที่ดีขึ้นผ่านประเภทค่าเป็นทรัพยากรที่ยอดเยี่ยมสำหรับสิ่งนี้
Abizern

1
ขอบคุณ @Abizern ฉันไม่เคยเข้าใจเรื่องนี้มาก่อนอ่านความคิดเห็นของคุณ สำหรับวัตถุให้เทียบกับ var ไม่แตกต่างกันมาก แต่สำหรับ structs มันใหญ่มาก ขอบคุณที่ชี้นำสิ่งนี้
Dan Rosenstark

27

สมมติว่าเรารู้ว่าโครงสร้างเป็นประเภทค่าและชั้นเป็นชนิดการอ้างอิง

หากคุณไม่ทราบว่าประเภทค่าและประเภทอ้างอิงคืออะไรให้ดูความแตกต่างระหว่างการส่งต่อโดยอ้างอิงกับการผ่านตามค่าคืออะไร

ตามโพสต์ของ mikeash :

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

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

ตัวควบคุมมุมมองและหน้าต่างเป็นตัวอย่างที่คล้ายกัน พวกเขาอาจจะคัดลอกเป็นไปได้ แต่แทบจะไม่เคยทำสิ่งที่คุณต้องการ ควรเป็นประเภทอ้างอิง

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

struct User {}
class UserController {
    var users: [User]

    func add(user: User) { ... }
    func remove(userNamed: String) { ... }
    func ...
}

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

ภาษาส่วนใหญ่พูดว่า "ไม่" สำหรับสิ่งนี้และสร้างประเภทอ้างอิงคอลเลกชัน สิ่งนี้เป็นจริงใน Objective-C และ Java และ Python และ JavaScript และเกือบทุกภาษาอื่น ๆ ที่ฉันนึกออก (ข้อยกเว้นหนึ่งที่สำคัญคือ C ++ ที่มีคอลเลกชันประเภท STL แต่ C ++ เป็นภาษาที่เพ้อคลั่งของโลกภาษาซึ่งทำทุกอย่างแปลก ๆ )

Swift กล่าวว่า "ใช่" ซึ่งหมายความว่าประเภทเช่น Array และ Dictionary และ String เป็น struct มากกว่าคลาส พวกเขาได้รับการคัดลอกในการมอบหมายและผ่านพวกเขาเป็นพารามิเตอร์ นี่เป็นตัวเลือกที่สมเหตุสมผลอย่างสมบูรณ์ตราบใดที่สำเนามีราคาถูกซึ่ง Swift พยายามอย่างหนักเพื่อให้บรรลุผลสำเร็จ ...

โดยส่วนตัวฉันไม่ตั้งชื่อชั้นเรียนของฉันแบบนั้น ฉันมักจะตั้งชื่อ Mine UserManagerแทนUserControllerแต่ความคิดนั้นเหมือนกัน

นอกจากนี้อย่าใช้คลาสเมื่อคุณต้องแทนที่แต่ละอินสแตนซ์ของฟังก์ชันเช่นพวกเขาไม่มีฟังก์ชั่นที่ใช้ร่วมกัน

ดังนั้นแทนที่จะมีคลาสย่อยหลายคลาส ใช้โครงสร้างหลายอย่างที่สอดคล้องกับโปรโตคอล


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


1
คำอธิบายที่ฉันกำลังมองหา เขียนดี :)
androCoder-BD

ตัวอย่างคอนโทรลเลอร์ที่มีประโยชน์มาก
ถาม P

1
@AskP ฉันส่งอีเมลไมค์ตัวเองและได้ว่าชิ้นส่วนพิเศษของรหัส :)
น้ำผึ้ง

18

ข้อดีบางประการ:

  • threadsafe อัตโนมัติเนื่องจากไม่สามารถแบ่งปันได้
  • ใช้หน่วยความจำน้อยลงเนื่องจากไม่มี isa และ refcount (และในความเป็นจริงคือการจัดสรรสแต็กโดยทั่วไป)
  • วิธีการจะถูกส่งแบบคงที่เสมอดังนั้นสามารถ inline (แม้ว่า @final สามารถทำเช่นนี้สำหรับการเรียน)
  • เหตุผลง่ายกว่า (ไม่จำเป็นต้อง "คัดลอกการป้องกัน" ตามปกติกับ NSArray, NSString ฯลฯ ... ) ด้วยเหตุผลเดียวกันกับความปลอดภัยของเธรด

ไม่แน่ใจว่ามันอยู่นอกขอบเขตของคำตอบนี้หรือไม่ แต่คุณสามารถอธิบาย (หรือลิงก์ฉันเดา) จุด "วิธีการส่งแบบคงที่เสมอ"?
Dan Rosenstark

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

1
ขอบคุณคำอธิบายที่ดี ดังนั้นเราคาดหวังความเร็วรันไทม์ที่มากขึ้นหรือความคลุมเครือน้อยลงจากมุมมอง IDE หรือทั้งสองอย่าง?
Dan Rosenstark

1
อดีตส่วนใหญ่
Catfish_Man

เพียงทราบว่าวิธีการจะไม่ถูกส่งแบบคงที่ถ้าคุณอ้างถึงโครงสร้างผ่านโปรโตคอล
Cristik

12

โครงสร้างนั้นเร็วกว่าคลาสมาก นอกจากนี้หากคุณต้องการรับมรดกคุณต้องใช้คลาส จุดที่สำคัญที่สุดคือ Class นั้นเป็นประเภทอ้างอิงในขณะที่โครงสร้างเป็นประเภทค่า ตัวอย่างเช่น,

class Flight {
    var id:Int?
    var description:String?
    var destination:String?
    var airlines:String?
    init(){
        id = 100
        description = "first ever flight of Virgin Airlines"
        destination = "london"
        airlines = "Virgin Airlines"
    } 
}

struct Flight2 {
    var id:Int
    var description:String
    var destination:String
    var airlines:String  
}

ตอนนี้ให้สร้างตัวอย่างของทั้งคู่

var flightA = Flight()

var flightB = Flight2.init(id: 100, description:"first ever flight of Virgin Airlines", destination:"london" , airlines:"Virgin Airlines" )

ตอนนี้ให้ส่งผ่านอินสแตนซ์เหล่านี้ไปยังสองฟังก์ชันที่ปรับเปลี่ยน id คำอธิบายปลายทาง ฯลฯ

func modifyFlight(flight:Flight) -> Void {
    flight.id = 200
    flight.description = "second flight of Virgin Airlines"
    flight.destination = "new york"
    flight.airlines = "Virgin Airlines"
}

นอกจากนี้ยังมี

func modifyFlight2(flight2: Flight2) -> Void {
    var passedFlight = flight2
    passedFlight.id = 200
    passedFlight.description = "second flight from virgin airlines" 
}

ดังนั้น,

modifyFlight(flight: flightA)
modifyFlight2(flight2: flightB)

ตอนนี้ถ้าเราพิมพ์รหัสและคำอธิบายของ flightA เราก็จะได้

id = 200
description = "second flight of Virgin Airlines"

ที่นี่เราสามารถเห็น id และคำอธิบายของ FlightA มีการเปลี่ยนแปลงเนื่องจากพารามิเตอร์ที่ส่งผ่านไปยังวิธีการแก้ไขจริง ๆ แล้วชี้ไปยังที่อยู่หน่วยความจำของวัตถุ FlightA (ชนิดอ้างอิง)

ตอนนี้ถ้าเราพิมพ์ id และคำอธิบายของอินสแตนซ์ของ FLightB ที่เราได้รับ

id = 100
description = "first ever flight of Virgin Airlines"

ที่นี่เราจะเห็นได้ว่าอินสแตนซ์ของ FlightB จะไม่เปลี่ยนแปลงเพราะในเมธอด alterFlight2 อินสแตนซ์ที่แท้จริงของ Flight2 จะผ่านไปแทนที่จะอ้างอิง (ประเภทค่า)


2
คุณไม่เคยสร้างตัวอย่างของ FLightB
David Seek

1
ถ้าอย่างนั้นทำไมคุณถึงพูดถึง FlightB ครับ Here we can see that the FlightB instance is not changed
David Seek

@ManojKarki คำตอบที่ดี แค่ต้องการชี้ให้เห็นว่าคุณประกาศ FlightA สองครั้งเมื่อฉันคิดว่าคุณต้องการประกาศ FlightA แล้ว FlightB
ScottyBlades

11

Structsเป็นvalue typeและClassesเป็นreference type

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

ใช้valueประเภทเมื่อ:

  • คุณต้องการให้สำเนามีสถานะเป็นอิสระข้อมูลจะถูกใช้ในโค้ดข้ามหลายเธรด

ใช้referenceประเภทเมื่อ:

  • คุณต้องการสร้างสถานะที่แชร์และไม่แน่นอน

สามารถดูข้อมูลเพิ่มเติมได้ในเอกสารประกอบของ Apple

https://docs.swift.org/swift-book/LanguageGuide/ClassesAndStructures.html


ข้อมูลเพิ่มเติม

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

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

เพื่อเป็นการเตือนความจำนี่คือรายการของ Swift

ประเภทค่า:

  • โครงสร้าง
  • enum
  • tuple
  • Primitives (Int, Double, Bool เป็นต้น)
  • คอลเลกชัน (Array, String, Dictionary, Set)

ประเภทอ้างอิง:

  • ชั้น
  • อะไรก็ตามที่มาจาก NSObject
  • ฟังก์ชัน
  • การปิด

5

ตอบคำถามจากมุมมองของประเภทค่าเทียบกับประเภทอ้างอิงจากโพสต์บล็อกของ Apple นี้มันจะง่ายมาก:

ใช้ประเภทค่า [เช่น struct, enum] เมื่อ:

  • การเปรียบเทียบข้อมูลอินสแตนซ์กับ == สมเหตุสมผล
  • คุณต้องการให้สำเนามีสถานะเป็นอิสระ
  • ข้อมูลจะถูกใช้ในรหัสในหลายกระทู้

ใช้ประเภทการอ้างอิง [เช่นคลาส] เมื่อ:

  • เปรียบเทียบตัวตนของอินสแตนซ์กับ === สมเหตุสมผลแล้ว
  • คุณต้องการสร้างสถานะที่แชร์และไม่แน่นอน

ดังที่ได้กล่าวไว้ในบทความนั้นคลาสที่ไม่มีคุณสมบัติที่เขียนได้จะทำงานเหมือนกับ struct ด้วย (ฉันจะเพิ่ม) หนึ่ง caveat: structs ดีที่สุดสำหรับโมเดลที่ปลอดภัยสำหรับเธรด - ข้อกำหนดที่ใกล้เข้ามามากขึ้นในสถาปัตยกรรมแอปสมัยใหม่


3

ด้วยคลาสที่คุณได้รับการสืบทอดและถูกส่งผ่านโดยการอ้างอิง structs ไม่มีการสืบทอดและถูกส่งผ่านโดยค่า

มีเซสชัน WWDC ที่ยอดเยี่ยมสำหรับ Swift คำถามเฉพาะนี้ได้รับคำตอบโดยละเอียดในหนึ่งในนั้น ตรวจสอบให้แน่ใจว่าคุณได้ดูสิ่งเหล่านี้เพราะมันจะช่วยให้คุณเพิ่มความเร็วได้เร็วขึ้นกว่าเดิมจากคู่มือภาษาหรือ iBook


คุณช่วยระบุลิงก์จากสิ่งที่คุณพูดถึงได้ไหม สาเหตุที่ WWDC มีให้เลือกไม่กี่ตัวฉันอยากจะดูหัวข้อที่พูดถึงเรื่องนี้โดยเฉพาะ
MMachinegun

สำหรับฉันนี่เป็นการเริ่มต้นที่ดีที่นี่: github.com/raywenderlich/ …
MMachinegun

2
เขาอาจพูดถึงเซสชั่นที่ยอดเยี่ยมนี้: การเขียนโปรแกรมเชิงโปรโตคอลในสวิฟท์ (ลิงก์: วิดีโอ , การถอดเสียง )
zekel

2

ฉันจะไม่พูดว่า structs มีฟังก์ชั่นน้อยลง

แน่นอนว่าตนเองนั้นไม่สามารถเปลี่ยนแปลงได้ยกเว้นในฟังก์ชั่นการกลายพันธุ์ แต่มันเกี่ยวกับมัน

การรับมรดกทำงานได้ดีตราบใดที่คุณยึดติดกับแนวคิดเก่าที่ดีว่าทุกชั้นควรเป็นนามธรรมหรือสุดท้าย

ใช้คลาสนามธรรมเป็นโปรโตคอลและคลาสสุดท้ายเป็นโครงสร้าง

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

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

ตัวอย่างโครงสร้างการสืบทอดที่มีการใช้งานที่สกปรกและตรงไปตรงมาที่ด้านล่างในฟังก์ชันชื่อ "ตัวอย่าง":

protocol EventVisitor
{
    func visit(event: TimeEvent)
    func visit(event: StatusEvent)
}

protocol Event
{
    var ts: Int64 { get set }

    func accept(visitor: EventVisitor)
}

struct TimeEvent : Event
{
    var ts: Int64
    var time: Int64

    func accept(visitor: EventVisitor)
    {
        visitor.visit(self)
    }
}

protocol StatusEventVisitor
{
    func visit(event: StatusLostStatusEvent)
    func visit(event: StatusChangedStatusEvent)
}

protocol StatusEvent : Event
{
    var deviceId: Int64 { get set }

    func accept(visitor: StatusEventVisitor)
}

struct StatusLostStatusEvent : StatusEvent
{
    var ts: Int64
    var deviceId: Int64
    var reason: String

    func accept(visitor: EventVisitor)
    {
        visitor.visit(self)
    }

    func accept(visitor: StatusEventVisitor)
    {
        visitor.visit(self)
    }
}

struct StatusChangedStatusEvent : StatusEvent
{
    var ts: Int64
    var deviceId: Int64
    var newStatus: UInt32
    var oldStatus: UInt32

    func accept(visitor: EventVisitor)
    {
        visitor.visit(self)
    }

    func accept(visitor: StatusEventVisitor)
    {
        visitor.visit(self)
    }
}

func readEvent(fd: Int) -> Event
{
    return TimeEvent(ts: 123, time: 56789)
}

func example()
{
    class Visitor : EventVisitor
    {
        var status: UInt32 = 3;

        func visit(event: TimeEvent)
        {
            print("A time event: \(event)")
        }

        func visit(event: StatusEvent)
        {
            print("A status event: \(event)")

            if let change = event as? StatusChangedStatusEvent
            {
                status = change.newStatus
            }
        }
    }

    let visitor = Visitor()

    readEvent(1).accept(visitor)

    print("status: \(visitor.status)")
}

2

ใน Swift รูปแบบการเขียนโปรแกรมใหม่ได้รับการแนะนำให้รู้จักกับการเขียนโปรแกรมเชิงโพรโทคอล

รูปแบบ Creational:

ใน swift, Struct เป็นชนิดของค่าที่ถูกโคลนโดยอัตโนมัติ ดังนั้นเราจึงได้รับพฤติกรรมที่จำเป็นในการใช้รูปแบบต้นแบบได้ฟรี

ในขณะที่คลาสเป็นประเภทอ้างอิงซึ่งไม่ได้ถูกโคลนโดยอัตโนมัติระหว่างการกำหนด ในการนำรูปแบบต้นแบบมาใช้คลาสต้องนำNSCopyingโปรโตคอลมาใช้


ตื้นคัดลอกซ้ำการอ้างอิงเท่านั้นที่ชี้ไปที่วัตถุเหล่านั้นในขณะที่การคัดลอกลึกทำซ้ำการอ้างอิงของวัตถุ


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

class Contact{
  var firstName:String
  var lastName:String
  var workAddress:Address // Reference type
}

class Address{
   var street:String
   ...
} 

ด้วยการใช้structs และ enumsเราทำให้โค้ดของเราง่ายขึ้นเนื่องจากเราไม่ต้องใช้ตรรกะในการคัดลอก


1

Cocoa APIs จำนวนมากต้องการคลาสย่อย NSObject ซึ่งบังคับให้คุณใช้คลาส แต่นอกเหนือจากนั้นคุณสามารถใช้กรณีต่อไปนี้จากบล็อก Swift ของ Apple เพื่อตัดสินใจว่าจะใช้ประเภทค่า struct / enum หรือประเภทการอ้างอิงระดับ

https://developer.apple.com/swift/blog/?id=10


0

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

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


0

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

ข้อเสียของโครงสร้างคือไม่สามารถสืบทอดได้


-7
  • โครงสร้างและคลาสเป็นประเภทข้อมูลที่ผู้ใช้ระบุ

  • โดยค่าเริ่มต้นโครงสร้างเป็นสาธารณะในขณะที่คลาสเป็นส่วนตัว

  • ชั้นเรียนดำเนินการหลักของการห่อหุ้ม

  • วัตถุของคลาสที่ถูกสร้างขึ้นในหน่วยความจำฮีป

  • Class ใช้สำหรับการใช้งานอีกครั้งในขณะที่โครงสร้างใช้สำหรับจัดกลุ่มข้อมูลในโครงสร้างเดียวกัน

  • ข้อมูลโครงสร้างสมาชิกไม่สามารถเริ่มต้นได้โดยตรง แต่สามารถกำหนดโดยโครงสร้างภายนอกได้

  • สมาชิกของคลาสข้อมูลสามารถเริ่มต้นได้โดยตรงโดยตัวสร้างพารามิเตอร์น้อยกว่าและกำหนดโดยตัวสร้างพารามิเตอร์


2
คำตอบที่แย่ที่สุดที่เคยมีมา!
J. Doe

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