การเริ่มต้นของโครงสร้าง/คลาสที่ไม่มีตัวสร้างในสแต็กและฮีป

ฉันต้องการทราบกฎสำหรับโครงสร้างที่เป็นศูนย์ (หรือคลาส) ที่ไม่มีตัวสร้างเริ่มต้นใน C ++

โดยเฉพาะอย่างยิ่ง ดูเหมือนว่าหากเก็บไว้ในสแต็ก (เช่น เป็นตัวแปรโลคัล) พวกมันจะไม่ได้เตรียมใช้งาน แต่หากจัดสรรบนฮีป พวกมันจะถูกเตรียมใช้งานเป็นศูนย์ (ทดสอบด้วย GCC 4.9.1) รับประกันว่าจะพกพาได้หรือไม่

โปรแกรมตัวอย่าง:

#include <iostream>
#include <map>
using namespace std;

struct X {
    int i, j, k;
    void show() { cout << i << " " << j << " " << k << endl; }
};

int fib(int i) {
    return (i > 1) ? fib(i-1) + fib(i-2) : 1;
}

int main() {
    map<int, X> m;            
    fib(10);                  // fills the stack with cruft
    X x1;                     // local
    X &x2 = m[1];             // heap-allocated within map
    X *x3 = new X();          // explicitly heap-allocated
    x1.show();  // --> outputs whatever was on the heap in those positions
    x2.show();  // --> outputs 0 0 0 
    x3->show(); // --> outputs 0 0 0     
    return 0;
}

แก้ไข: ลบ "หรือฉันควรใช้ตัวสร้าง" ในส่วนที่เป็นตัวหนา เพราะสิ่งที่ทำให้ฉันถามคือฉันต้องการทราบว่ามันเป็นพฤติกรรมที่รับประกันหรือไม่ - เราทุกคนต่างเห็นพ้องต้องกันว่าโค้ดที่อ่านได้ดีกว่าเมื่อมีตัวสร้างที่ชัดเจน


person tucuxi    schedule 06.05.2015    source แหล่งที่มา
comment
ฉันไม่รู้มากพอที่จะโพสต์คำตอบที่ชัดเจน แต่ new X() จะเริ่มต้น X ในขณะที่ new X จะไม่ (ในกรณีปัจจุบันที่ X เป็นผลรวม)   -  person Quentin    schedule 06.05.2015
comment
คุณควรเขียน Constructor และตั้งค่าตัวแปรภายในของคลาสเสมอ ไม่ว่าคอมไพเลอร์จะตั้งค่าไว้แล้วก็ตาม   -  person maja    schedule 06.05.2015
comment
@Quentin สองข้อความนี้เหมือนกัน แต่ควรเลือกข้อความที่สอง (new X)   -  person maja    schedule 06.05.2015
comment
@maja พวกเขาไม่เหมือนกันและฉันต้องการ new X() ทำไมคุณถึงชอบ new X?   -  person TartanLlama    schedule 06.05.2015
comment
@maja พวกเขาไม่เหมือนกัน ดูที่นี่ ที่นี่ และ ที่นี่ ในกรณีนี้ X มีการกำหนดค่าเริ่มต้น (เป็นศูนย์) หรือไม่ทำอะไรเลย (ตัวสร้างค่าเริ่มต้นเล็กน้อย) อย่างไรก็ตาม กฎการเริ่มต้นถือเป็นฝันร้าย ดังนั้นเพียงใช้ Constructor ที่ชัดเจนและจัดการกับมันให้เสร็จสิ้น...   -  person Quentin    schedule 06.05.2015
comment
@TartanLlama เพราะคุณอาจประสบปัญหาในสถานการณ์ที่มีฟังก์ชันชื่อ X() อยู่ ในกรณีนี้ คอมไพลเลอร์จะตีความ X() เป็นการเรียกใช้ฟังก์ชัน ในขณะที่ new X จะไม่คลุมเครือเสมอ   -  person maja    schedule 06.05.2015
comment
ผิดคน คุณตอบ @TartanLlama ;)   -  person Quentin    schedule 06.05.2015
comment
@เควนติน: งั้นฝากไว้กับคนที่ ไม่ รู้จักมากพอ! ไม่จำเป็นต้องตอบ/คาดเดาในความคิดเห็น นั่นไม่ใช่สิ่งที่พวกเขาทำ ไชโย   -  person Lightness Races in Orbit    schedule 06.05.2015
comment
@maja: เรื่องไร้สาระโดยสิ้นเชิง การทิ้ง () ไม่ได้ช่วยคุณจากความคลุมเครือนั้น!   -  person Lightness Races in Orbit    schedule 06.05.2015
comment
@Quentin เรากำลังพูดถึงสิ่งที่แตกต่างกันหรือไม่? รหัส A* a = new A(); เทียบเท่ากับ A* a = new A; ใช่ไหม หากไม่มี Constructor ระบบจะใช้ค่าดีฟอลต์ ไม่สำคัญว่าคุณจะละเว้นวงเล็บเหลี่ยมหรือไม่ก็ตาม   -  person maja    schedule 06.05.2015
comment
@maja: คุณกำลังพูดถึงเรื่องอะไร? นั่นไม่ใช่ความคลุมเครือในการแยกวิเคราะห์ใน C ++   -  person Puppy    schedule 06.05.2015
comment
@maja: ไม่ ไม่ มันไม่เหมือนกัน จึงเกิดคำถามและคำตอบ   -  person Puppy    schedule 06.05.2015
comment
@maja: ไม่ มันไม่เทียบเท่ากัน ดังที่หลายๆ คนได้อธิบายไปหลายครั้งแล้ว   -  person Lightness Races in Orbit    schedule 06.05.2015
comment
@LightnessRacesinOrbit ในกรณีที่มีฟังก์ชันที่มีชื่อนั้นอยู่ คอมไพเลอร์จะยกเลิกโดยมีข้อผิดพลาด   -  person maja    schedule 06.05.2015
comment
@maja: คลิกที่ลิงค์ที่ฉันให้ไว้ จากนั้นอ่านข้อความของมัน คุณยืนยันว่าการทิ้ง () จะช่วยหลีกเลี่ยงความคลุมเครือ ฉันแสดงให้เห็นด้วยตัวอย่างสดว่าไม่ได้เป็นเช่นนั้น   -  person Lightness Races in Orbit    schedule 06.05.2015
comment
@LightnessRacesinOrbit ฉันรู้ว่ามีอะไรอยู่ในความคิดเห็นของฉันและไม่มีอะไรเพิ่มเติม;)   -  person Quentin    schedule 06.05.2015
comment
@LightnessRacesinOrbit อืม... ฉันต้องตรวจสอบเมื่อฉันอยู่ที่บ้าน... ดูเหมือนว่าฉันจะผสมปนเปกัน...   -  person maja    schedule 06.05.2015


คำตอบ (3)


ไม่ใช่การจัดสรรแบบไดนามิกที่เริ่มต้นศูนย์สมาชิก struct ของคุณ มันเป็นไวยากรณ์ของคุณ:

X* ptr = new X();
//            ^^
// 
// as opposed to just:
X* ptr = new X;

หากคุณต้องการรับประกัน เพียงแค่เขียนต่อไป :)

อีกทางเลือกหนึ่งซึ่งเข้ากันได้ดีกับระยะเวลาการจัดเก็บอัตโนมัติคือการใช้ไวยากรณ์ {} ที่ใหม่กว่า:

X* ptr = new X{};   // dynamic
X  obj{};           // automatic

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

person Lightness Races in Orbit    schedule 06.05.2015
comment
คำตอบที่ดี; ฉันไม่เคยคิดที่จะโทรหาใหม่โดยไม่มีวงเล็บเพื่อเปรียบเทียบ มีแนวคิดใดบ้างเกี่ยวกับสาเหตุที่ std::map คือ () - กำลังเริ่มต้น struct ของฉัน - person tucuxi; 06.05.2015
comment
@tucuxi: เพราะนั่นคือสิ่งที่มันทำ! [C++11: 23.4.4.3/1]: ผลกระทบ: หากไม่มีคีย์ที่เทียบเท่ากับ x ในแผนที่ ให้แทรก value_type(x, T()) ลงในแผนที่ มาตรฐาน อาจ พยายามหลีกเลี่ยงการกำหนดค่าเริ่มต้นนี้ และใช้ค่าเริ่มต้นแทน แต่จะเพิ่มความซับซ้อนและอาจลดประโยชน์ลง ฉันหมายความว่า ในกรณีส่วนใหญ่ การมีค่าที่อ่านได้อย่างปลอดภัยจะมีประโยชน์มากกว่าการหลีกเลี่ยงการเขียนเพียงครั้งเดียวที่อาจซ้ำซ้อน - person Lightness Races in Orbit; 06.05.2015
comment
@tucuxi: เพราะการไม่ทำเช่นนั้นถือเป็นเรื่องโง่ คุณเพียงแค่จบลงด้วยคุณค่าไร้ค่ามากมายที่คุณต้องกำหนดในภายหลัง - person Puppy; 06.05.2015
comment
@tucuxi ฉันไม่พบคำที่แน่ชัดเกี่ยวกับ [insert Standard guru invocation here] แต่ IINM เพียงการอ้างอิงถึงตัวแปรที่ไม่ได้กำหนดค่าเริ่มต้นคือ UB และ std::map ต้องส่งคืนการอ้างอิงดังกล่าว - person Quentin; 06.05.2015
comment
@เควนติน: ไม่สามารถพูดได้ว่าฉันเคยได้ยินเรื่องนั้นและฉันไม่สามารถหาหลักฐานใด ๆ เกี่ยวกับเรื่องนี้ในการสแกนข้อความมาตรฐานที่รวดเร็ว [มาก] ไม่ใช่ว่ามันจะทำให้ฉันประหลาดใจมาก - person Lightness Races in Orbit; 06.05.2015

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

person Puppy    schedule 06.05.2015
comment
ดูเหมือนเป็นคำแนะนำที่หนักหน่วงโดยไม่มีคุณสมบัติ การเพิ่ม Constructor ที่ผู้ใช้กำหนดจะเปลี่ยนคุณสมบัติของประเภทในลักษณะที่คุณ อาจ ไม่ต้องการในบางสถานการณ์ - person Lightness Races in Orbit; 06.05.2015
comment
ขออภัย ฉันแก้ไขหรือควรใช้ Constructor ออกไปแล้ว เพราะฉันอยากรู้จริงๆ ว่าเหตุใดสิ่งต่างๆ จึงเกิดขึ้นในขณะนั้น ใช่ ฉันยอมรับว่าโค้ดจะดีกว่าหากใช้ Constructor แต่ฉันอยากจะเข้าใจว่าเหตุใดจึงทำงานในลักษณะนี้ - person tucuxi; 06.05.2015
comment
@Lightness: ใครก็ตามที่พบกับสถานการณ์อย่างใดอย่างหนึ่งจริง ๆ และเข้าใจว่าคุณสมบัติใดบ้างที่มีการเปลี่ยนแปลงและเหตุใดจึงไม่ต้องการคำแนะนำของคำตอบนี้เพื่อทราบว่าต้องทำอย่างไร การไม่เขียน Constructor เมื่อคุณต้องการด้วยเหตุผลไร้สาระนั้นเป็นเรื่องธรรมดาและแย่กว่าการทำให้ประเภทของคุณไม่ใช่ POD หรืออะไรทำนองนั้นโดยไม่ได้ตั้งใจ - person Puppy; 06.05.2015
comment
อ๋อ ฉันเข้าใจแล้ว: คำตอบนี้จงใจละเว้นรายละเอียดที่สำคัญเพราะใครก็ตามที่อ่านจะไม่เข้าใจรายละเอียดเหล่านั้น หัวสูงวิชาการที่ดีที่สุด! - person Lightness Races in Orbit; 06.05.2015

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

หากต้องการรับการกำหนดค่าเริ่มต้นเป็นศูนย์แบบพกพา เพียงใช้ X x = {}; ซึ่งดำเนินการกำหนดค่าเริ่มต้นเป็นศูนย์

นี่คือคำพูดมาตรฐานเกี่ยวกับผลรวม (8.5.1):

การรวมคืออาร์เรย์หรือคลาส (ข้อ 9) ที่ไม่มีตัวสร้างที่ผู้ใช้จัดเตรียมไว้ (12.1) ไม่มีตัวเริ่มต้นปีกกาหรือเท่ากับสำหรับสมาชิกข้อมูลที่ไม่คงที่ (9.2) ไม่มีสมาชิกข้อมูลส่วนตัวหรือการป้องกันที่ไม่คงที่ ( ข้อ 11) ไม่มีคลาสพื้นฐาน (ข้อ 10) และไม่มีฟังก์ชันเสมือน (10.3)

การอ้างอิง: เหตุใดฉันจึงไม่สามารถเริ่มต้นวงเล็บปีกกาได้ โครงสร้างที่ได้มาจากโครงสร้างอื่น?

person timato    schedule 06.05.2015