จับโดยการอ้างอิงเมื่อไม่ได้กำหนดตัวแปรข้อยกเว้น

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

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

หากเรามีสถานการณ์ที่เราไม่ได้กำหนดชื่อข้อยกเว้นใน catch block ข้อกังวลเหล่านี้ (จริงๆ แล้ว 1. เนื่องจากการแบ่งส่วนจะไม่เป็นปัญหาหากเราไม่มีชื่อสำหรับตัวแปร) ยังคงใช้ได้อยู่หรือไม่

ตัวอย่างเช่น:

catch(my_exception)
{ ... }

or

catch(my_exception &)
{ ... }

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

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


person Christopher Howlin    schedule 13.09.2011    source แหล่งที่มา
comment
แม้ว่าจะเป็นแนวทางปฏิบัติมาตรฐานในการจับ my_exception& แต่ฉันพบว่าส่วนใหญ่แล้วข้อยกเว้นไม่ได้รับการแก้ไข ดังนั้น จึงควรจับ my_exception const& จะดีกว่า โปรดทราบว่า what คือ const   -  person Matthieu M.    schedule 13.09.2011
comment
คำถามนี้เป็นแรงบันดาลใจให้คำถามนี้เกี่ยวกับการคัดออกในกรณีเช่นนี้: stackoverflow.com/questions/7401521/   -  person Potatoswatter    schedule 13.09.2011


คำตอบ (3)


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

N3290 §15.3/16:
หาก การประกาศข้อยกเว้น ระบุชื่อ ก็จะประกาศตัวแปรซึ่งมีการเริ่มต้นการคัดลอก (8.5) จากอ็อบเจ็กต์ข้อยกเว้น หาก การประกาศข้อยกเว้น หมายถึงประเภทวัตถุ แต่ไม่ได้ระบุชื่อ ชั่วคราว (12.2) จะถูกคัดลอกเริ่มต้น (8.5) จากวัตถุข้อยกเว้น อายุการใช้งานของตัวแปรหรือสิ้นสุดชั่วคราวเมื่อตัวจัดการออก หลังจากการทำลายออบเจ็กต์อัตโนมัติใดๆ ที่เริ่มต้นภายในตัวจัดการ

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

อย่างไรก็ตาม ซึ่งมีความขัดแย้งในย่อหน้าถัดไป:

N3290 §15.3/17:
เมื่อตัวจัดการประกาศวัตถุที่ไม่คงที่ การเปลี่ยนแปลงใด ๆ ในวัตถุนั้นจะไม่ส่งผลกระทบต่อวัตถุชั่วคราวที่เริ่มต้นโดยการดำเนินการของ throw-expression . เมื่อตัวจัดการประกาศการอ้างอิงถึงวัตถุที่ไม่คงที่ การเปลี่ยนแปลงใด ๆ กับวัตถุที่อ้างอิงจะถูกเปลี่ยนไปยังวัตถุชั่วคราวที่เตรียมใช้งานเมื่อ throw-expression ถูกดำเนินการ และจะมีผลหากวัตถุนั้นถูกโยนทิ้งใหม่

ดังนั้น ประเภทที่ประกาศ T& (ด้วย T ไม่ใช่-const) จึงเป็นกรณีเดียวที่ C++11 ต้องการการอ้างอิงโดยตรงไปยังวัตถุที่ถูกโยนทิ้ง แทนที่จะคัดลอก และมันก็เป็นเช่นนั้นใน C++03 ยกเว้นว่า C++03 มีถ้อยคำเพิ่มเติมเกี่ยวกับการเพิ่มประสิทธิภาพเสมือนหนึ่ง ดังนั้น สำหรับการเป็นทางการ ควรมีการตั้งค่าไว้สำหรับ

    catch( T& name )

และ

    catch( T& )

อย่างไรก็ตาม ฉันมักจะพบข้อยกเว้นเช่น catch( T const& ) เสมอ จากมุมมองเชิงปฏิบัติอาจสันนิษฐานได้ว่าคอมไพเลอร์จะปรับให้เหมาะสมกับการอ้างอิงโดยตรงไปยังวัตถุที่ถูกโยนทิ้ง แม้ว่าจะเป็นไปได้ที่จะกำหนดกรณีที่ผลกระทบของโปรแกรมที่สังเกตได้นั้นไม่ได้มาตรฐานก็ตาม เช่น <evil grin>

#include <stdio.h>
#include <stdexcept>

struct Error
    : std::runtime_error
{
public:
    static Error* pThrown;

    char const* pMessage_;
    Error()
        : std::runtime_error( "Base class message" )
        , pMessage_( "Original message." )
    {
        printf( "Default-construction of Error object.\n" );
        pThrown = this;
    }

    Error( Error const& other )
        : std::runtime_error( other )
        , pMessage_( other.pMessage_ )
    {
        printf( "Copy-construction of Error obejct.\n" );
    }

    char const* what() const throw() { return pMessage_; }
};

Error*  Error::pThrown  = 0;

int main()
{
    printf( "Testing non-const ref:\n" );
    try
    {
        throw Error();
    }
    catch( Error& x )
    {
        Error::pThrown->pMessage_ = "Modified message.";
        printf( "%s\n", x.what() );
    }

    printf( "\n" );
    printf( "Testing const ref:\n" );
    try
    {
        throw Error();
    }
    catch( Error const& x )
    {
        Error::pThrown->pMessage_ = "Modified message";
        printf( "%s\n", x.what() );
    }
}

ด้วยทั้ง MinGW g++ 4.4.1 และ Visual C++ 10.0 ผลลัพธ์ก็คือ

Testing non-const ref:
Default-construction of Error object.
Modified message.

Testing const ref:
Default-construction of Error object.
Modified message

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

สรุป wrt. คำถามของ OP:

  • การจับโดยการอ้างอิงจะไม่เรียกตัวสร้างการคัดลอกซึ่งอาจยุติโปรแกรมได้

  • มาตรฐานรับประกันเฉพาะสิ่งนี้สำหรับการอ้างอิงถึง non-const

  • ในทางปฏิบัติ ดังที่แสดงไว้ มีการรับประกันสำหรับการอ้างอิงถึง const แม้ว่าผลลัพธ์ของโปรแกรมจะได้รับผลกระทบก็ตาม

ไชโย & hth.,

person Cheers and hth. - Alf    schedule 13.09.2011

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

person R. Martinho Fernandes    schedule 13.09.2011
comment
คำถาม: คอมไพลเลอร์สามารถปรับการคัดลอกให้เหมาะสมได้แม้ว่าตัวสร้างการคัดลอกของข้อยกเว้นจะมีผลข้างเคียงหรือไม่ - person R. Martinho Fernandes; 13.09.2011

อาจมีข้อมูลที่มองไม่เห็นซึ่งเชื่อมโยงกับข้อยกเว้นของคุณ เช่น vtable

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

person Sebastian Mach    schedule 13.09.2011
comment
คุณช่วยอธิบายรายละเอียดเกี่ยวกับสิ่งที่อาจหมายถึงในบริบทนี้ได้ไหม - person Christopher Howlin; 13.09.2011