การฝึกในการส่งคืนตัวแปรอ้างอิง C ++ นั้นชั่วร้ายหรือไม่?


341

ฉันคิดว่ามันเป็นเรื่องส่วนตัว ฉันไม่แน่ใจว่าความคิดเห็นจะเป็นเอกฉันท์หรือไม่ (ฉันเห็นตัวอย่างโค้ดจำนวนมากซึ่งส่งคืนการอ้างอิง)

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

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

ฉันรู้สึกว่าเนื่องจากพอยน์เตอร์และการอ้างอิงแบบผสมของฉันรวมกับความจริงที่ว่าฉันใหม่กับ C ++ และความสับสนอย่างสมบูรณ์เกี่ยวกับสิ่งที่จะใช้เมื่อแอปพลิเคชันของฉันต้องเป็นนรกหน่วยความจำรั่วไหล ...

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


มันไม่ได้เลวร้ายถ้าคุณกำลังเขียนฟังก์ชั่น / วิธีเหมือนทะเยอทะยาน
John Z. Li

คำตอบ:


411

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

ถ้าคุณหมายถึง:

int& getInt() {
    int i;
    return i;  // DON'T DO THIS.
}

นั่นคือความชั่วร้ายทุกประเภท การจัดสรรสแต็กiจะหายไปและคุณไม่ได้อ้างถึงอะไร นี่คือความชั่วร้าย:

int& getInt() {
    int* i = new int;
    return *i;  // DON'T DO THIS.
}

เพราะตอนนี้ลูกค้าต้องทำสิ่งที่แปลกประหลาด:

int& myInt = getInt(); // note the &, we cannot lose this reference!
delete &myInt;         // must delete...totally weird and  evil

int oops = getInt(); 
delete &oops; // undefined behavior, we're wrongly deleting a copy, not the original

โปรดทราบว่าการอ้างอิง rvalue ยังคงเป็นเพียงการอ้างอิงดังนั้นแอปพลิเคชันที่ชั่วร้ายทั้งหมดจึงยังคงเหมือนเดิม

หากคุณต้องการจัดสรรบางสิ่งที่อยู่นอกขอบเขตของฟังก์ชันให้ใช้ตัวชี้สมาร์ท (หรือโดยทั่วไปคือคอนเทนเนอร์):

std::unique_ptr<int> getInt() {
    return std::make_unique<int>(0);
}

และตอนนี้ไคลเอนต์เก็บตัวชี้อัจฉริยะ:

std::unique_ptr<int> x = getInt();

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

struct immutableint {
    immutableint(int i) : i_(i) {}

    const int& get() const { return i_; }
private:
    int i_;
};

ที่นี่เรารู้ว่ามันโอเคที่จะส่งคืนการอ้างอิงถึงi_เพราะสิ่งที่เรียกเราจัดการชีวิตของอินสแตนซ์ของชั้นเรียนดังนั้นi_จะมีชีวิตอยู่อย่างน้อยในระยะเวลานั้น

และแน่นอนไม่มีอะไรผิดปกติเพียง:

int getInt() {
   return 0;
}

หากอายุการใช้งานที่เหลืออยู่กับผู้โทรและคุณแค่คำนวณค่า

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


21
นี่เป็นตัวอย่างที่ไม่ดีทั้งหมด ตัวอย่างที่ดีที่สุดของการใช้งานที่เหมาะสมคือเมื่อคุณส่งคืนการอ้างอิงไปยังวัตถุที่ถูกส่งผ่านมาตัวดำเนินการ ala <<
Arelius

171
เพื่อประโยชน์ของลูกหลานและสำหรับการเขียนโปรแกรมใด ๆ ที่ใหม่กว่า chancing เมื่อนี้ชี้จะไม่เลวร้าย ไม่มีตัวชี้ไปยังหน่วยความจำแบบไดนามิกที่ไม่ดี พวกเขาทั้งคู่มีสถานที่ที่ถูกต้องตามกฎหมายใน C ++ ตัวชี้สมาร์ทควรเป็นค่าเริ่มต้นของคุณอย่างแน่นอนเมื่อพูดถึงการจัดการหน่วยความจำแบบไดนามิก แต่ตัวชี้สมาร์ทเริ่มต้นของคุณควรเป็น unique_ptr ไม่ใช่ shared_ptr
Jamin Gray

12
แก้ไขผู้อนุมัติ: อย่าอนุมัติการแก้ไขหากคุณไม่สามารถรับรองความถูกต้องได้ ฉันย้อนกลับการแก้ไขที่ไม่ถูกต้อง
GManNickG

7
เพื่อประโยชน์ของลูกหลานและสำหรับการเขียนโปรแกรมใด ๆ ที่ใหม่กว่า chancing เมื่อนี้ไม่ได้เขียน return new int
Lightness Races ในวงโคจร

3
เพื่อประโยชน์ของลูกหลานและสำหรับโปรแกรมเมอร์รุ่นใหม่ ๆ ที่สนใจสิ่งนี้เพียงแค่คืนค่า T จากฟังก์ชัน RVO จะดูแลทุกอย่าง
รองเท้า

64

ไม่ไม่ไม่ไม่กี่ครั้ง

ความชั่วคือการอ้างอิงวัตถุที่จัดสรรแบบไดนามิกและสูญเสียตัวชี้เดิม เมื่อคุณวัตถุคุณถือว่าภาระผูกพันที่จะมีการรับประกันnewdelete

แต่ดูที่ตัวอย่างเช่นoperator<<: ที่จะต้องส่งคืนการอ้างอิงหรือ

cout << "foo" << "bar" << "bletch" << endl ;

จะไม่ทำงาน


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

4
การฝึกคืนวัตถุอ้างอิงนั้นไม่ใช่ความชั่วร้าย หมายเลข Ergo ความกลัวที่เขาแสดงออกนั้นเป็นความกลัวที่ถูกต้องขณะที่ฉันชี้ให้เห็นในกราฟที่สอง
Charlie Martin

คุณไม่ได้จริง แต่นี่ไม่คุ้มกับเวลาของฉัน

2
Iraimbilanja @ เกี่ยวกับ "ไม่" - ฉันไม่สนใจ แต่โพสต์นี้ชี้ให้เห็นข้อมูลสำคัญที่หายไปจากจีเอ็ม
Kobor42

48

คุณควรส่งคืนการอ้างอิงไปยังวัตถุที่มีอยู่ซึ่งไม่ได้หายไปทันทีและคุณไม่ต้องการโอนกรรมสิทธิ์

อย่าส่งคืนการอ้างอิงไปยังตัวแปรโลคอลหรือบางรายการเช่นนั้นเพราะจะไม่มีการอ้างอิง

คุณสามารถส่งคืนการอ้างอิงถึงสิ่งที่เป็นอิสระจากฟังก์ชั่นซึ่งคุณไม่คาดหวังว่าฟังก์ชั่นการโทรจะรับผิดชอบในการลบ นี่เป็นกรณีของoperator[]ฟังก์ชั่นทั่วไป

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


1
คำตอบที่ยอดเยี่ยม แต่สำหรับ "คุณสามารถส่งคืนชั่วคราวเป็นการอ้างอิง const" รหัสต่อไปนี้จะรวบรวม แต่อาจมีปัญหาเพราะชั่วคราวถูกทำลายในตอนท้ายของคำสั่งส่งคืน: "int const & f () {return 42;} โมฆะ main () {int const & r = f (); ++ r;} "
j_random_hacker

@j_random_hacker: C ++ มีกฎแปลก ๆ บางอย่างสำหรับการอ้างอิงถึงขมับซึ่งอาจยืดอายุการใช้งานชั่วคราว ฉันขอโทษฉันไม่เข้าใจดีพอที่จะรู้ว่ามันครอบคลุมกรณีของคุณหรือไม่
Mark Ransom

3
@ Mark: ใช่มันมีกฎแปลก ๆ อายุการใช้งานชั่วคราวของสามารถขยายได้โดยการเริ่มต้นการอ้างอิง const (นั่นไม่ใช่สมาชิกชั้น) ด้วย; มันจะมีชีวิตอยู่จนกว่าผู้อ้างอิงจะออกนอกขอบเขต น่าเศร้าที่การคืนค่าอ้างอิงจะไม่ครอบคลุม อย่างไรก็ตามการคืนค่า temp ด้วยค่านั้นปลอดภัยอย่างไรก็ตาม
j_random_hacker

ดู c ++ มาตรฐาน 12.2 วรรค 5. ยังเห็นสมุนไพรซัทเทอจรจัดคุรุของสัปดาห์ที่herbsutter.wordpress.com/2008/01/01/...
David Thornley

4
@David: เมื่อประเภทการคืนค่าของฟังก์ชันคือ "T const &" สิ่งที่เกิดขึ้นจริงก็คือคำสั่ง return จะแปลง temp ซึ่งเป็นประเภท T โดยปริยายเพื่อพิมพ์ "T const &" ตาม 6.6.3.2 (เป็นการแปลงทางกฎหมาย แต่อย่างใดอย่างหนึ่ง ซึ่งไม่ขยายอายุการใช้งาน) และจากนั้นรหัสโทรศัพท์จะเริ่มต้นการอ้างอิงประเภท "T const &" พร้อมกับผลลัพธ์ของฟังก์ชันเช่นเดียวกับประเภท "T const &" - ​​อีกครั้งกระบวนการทางกฎหมาย แต่ไม่ใช่การยืดอายุการใช้งาน ผลลัพธ์ที่ได้: ไม่ยืดอายุการใช้งานและสับสนมาก :(
j_random_hacker

26

ฉันค้นหาคำตอบที่ไม่พอใจดังนั้นฉันจะเพิ่มสองเซ็นต์ของฉัน

ลองวิเคราะห์กรณีต่อไปนี้:

การใช้งานผิดพลาด

int& getInt()
{
    int x = 4;
    return x;
}

นี่เป็นข้อผิดพลาดที่เห็นได้ชัด

int& x = getInt(); // will refer to garbage

การใช้งานกับตัวแปรคงที่

int& getInt()
{
   static int x = 4;
   return x;
}

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

int& x = getInt(); // valid reference, x = 4

นี่เป็นเรื่องธรรมดาเมื่อใช้รูปแบบซิงเกิล

Class Singleton
{
    public:
        static Singleton& instance()
        {
            static Singleton instance;
            return instance;
        };

        void printHello()
        {
             printf("Hello");
        };

}

การใช้งาน:

 Singleton& my_sing = Singleton::instance(); // Valid Singleton instance
 my_sing.printHello();  // "Hello"

ผู้ประกอบการ

คอนเทนเนอร์ไลบรารีมาตรฐานขึ้นอยู่กับการใช้งานของตัวดำเนินการที่ส่งคืนการอ้างอิงตัวอย่างเช่น

T & operator*();

อาจใช้ในต่อไปนี้

std::vector<int> x = {1, 2, 3}; // create vector with 3 elements
std::vector<int>::iterator iter = x.begin(); // iterator points to first element (1)
*iter = 2; // modify first element, x = {2, 2, 3} now

การเข้าถึงข้อมูลภายในอย่างรวดเร็ว

มีบางครั้งที่ & อาจถูกใช้เพื่อเข้าถึงข้อมูลภายในได้อย่างรวดเร็ว

Class Container
{
    private:
        std::vector<int> m_data;

    public:
        std::vector<int>& data()
        {
             return m_data;
        }
}

ด้วยการใช้งาน:

Container cont;
cont.data().push_back(1); // appends element to std::vector<int>
cont.data()[0] // 1

อย่างไรก็ตามสิ่งนี้อาจนำไปสู่อันตรายเช่นนี้:

Container* cont = new Container;
std::vector<int>& cont_data = cont->data();
cont_data.push_back(1);
delete cont; // This is bad, because we still have a dangling reference to its internal data!
cont_data[0]; // dangling reference!

การคืนค่าการอ้างอิงไปยังตัวแปรแบบคงที่สามารถนำไปสู่พฤติกรรมที่ไม่พึงประสงค์เช่นพิจารณาตัวดำเนินการคูณซึ่งส่งกลับการอ้างอิงถึงสมาชิกแบบคงที่แล้วต่อไปนี้จะส่งผลให้true:If((a*b) == (c*d))
SebNag

Container::data()การใช้งานควรอ่านreturn m_data;
Xeaz

มันมีประโยชน์มากขอบคุณ! @ Xeaz นั้นจะไม่ทำให้เกิดปัญหากับการต่อสาย?
แอนดรู

@SebTu ทำไมคุณถึงอยากทำเช่นนั้น?
thorhunter

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

14

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

มีสิ่งดีๆที่สามารถทำได้ (เช่น map [name] = "hello world")


1
ฉันแค่อยากรู้อยากเห็นสิ่งที่ดีเกี่ยวกับmap[name] = "hello world"?
เหมาะสม

5
@wrongusername ไวยากรณ์ใช้งานง่าย เคยพยายามเพิ่มจำนวนของค่าที่เก็บไว้ใน a HashMap<String,Integer>Java หรือไม่? : P
Mehrdad Afshari

ฮ่า ๆ ยังไม่ได้ แต่ดูที่ตัวอย่าง HashMap มันดูค่อนข้างน่า
กลัว

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

10

"การส่งคืนการอ้างอิงเป็นสิ่งที่เลวร้ายเพราะเพียง [อย่างที่ฉันเข้าใจ] มันทำให้พลาดการลบได้ง่ายขึ้น"

ไม่จริง. การส่งคืนการอ้างอิงไม่ได้หมายความถึงความหมายของการเป็นเจ้าของ นั่นเป็นเพียงเพราะคุณทำสิ่งนี้:

Value& v = thing->getTheValue();

... ไม่ได้หมายความว่าคุณเป็นเจ้าของหน่วยความจำที่อ้างถึงโดย v;

อย่างไรก็ตามนี่เป็นรหัสที่น่ากลัว:

int& getTheValue()
{
   return *new int;
}

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


7

มีสองกรณี:

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

  • การอ้างอิงที่ไม่ใช่แบบคอนทราสต์ - ความคิดที่ดีบางครั้งแบ่งการห่อหุ้ม

ทั้งสองแบ่งปันปัญหาเดียวกัน - อาจชี้ไปที่วัตถุที่ถูกทำลาย ...

ฉันอยากจะแนะนำให้ใช้สมาร์ทพอยน์เตอร์สำหรับหลาย ๆ สถานการณ์ที่คุณต้องการส่งคืนการอ้างอิง / ตัวชี้

นอกจากนี้ให้สังเกตสิ่งต่อไปนี้:

มีกฎอย่างเป็นทางการ - มาตรฐาน C ++ (ส่วน 13.3.3.1.4 หากคุณสนใจ) ระบุว่าชั่วคราวสามารถผูกกับการอ้างอิง const - ถ้าคุณพยายามใช้การอ้างอิงที่ไม่ใช่ const คอมไพเลอร์ต้องตั้งค่าสถานะนี้เป็น ข้อผิดพลาด


1
การอ้างอิงที่ไม่ใช่ const ไม่จำเป็นต้องทำลายการห่อหุ้ม พิจารณา vector :: โอเปอเรเตอร์ []

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

ดังนั้นคุณกำลังบอกว่าตัวดำเนินการตัวห้อยปกติใช้งานความชั่วร้ายที่จำเป็นหรือไม่ ฉันไม่เห็นด้วยหรือไม่เห็นด้วยกับสิ่งนี้ อย่างที่ฉันไม่ฉลาด
Nick Bolton

ผมไม่ได้พูดว่า แต่มันอาจเป็นความชั่วร้ายถ้า misused :))) :: เวกเตอร์ที่ควรจะใช้เมื่อใดก็ตามที่เป็นไปได้ ....

ใช่มั้ย? เวกเตอร์ :: ที่ยังส่งกลับการอ้างอิงที่ไม่เป็นอันตราย

4

ไม่เพียง แต่ไม่ใช่ความชั่วร้ายบางครั้งก็เป็นสิ่งจำเป็น ตัวอย่างเช่นจะไม่สามารถใช้ตัวดำเนินการ [] ของ std :: vector ได้โดยไม่ต้องใช้ค่าส่งคืนอ้างอิง


ใช่แน่นอน ฉันคิดว่านี่เป็นเหตุผลที่ฉันเริ่มใช้มัน; เหมือนครั้งแรกที่ฉันใช้ตัวดำเนินการตัวห้อย [] ฉันตระหนักถึงการใช้การอ้างอิง ฉันเชื่อว่านี่คือพฤตินัย
Nick Bolton

คุณสามารถนำไปใช้operator[]กับคอนเทนเนอร์ได้โดยไม่ต้องใช้การอ้างอิง ... และstd::vector<bool>ทำได้ (และสร้างความยุ่งเหยิงอย่างแท้จริงในกระบวนการนี้)
Ben Voigt

@ BenVoigt mmm ทำไมต้องเป็นระเบียบ? การส่งคืนพร็อกซีเป็นสถานการณ์ที่ถูกต้องสำหรับคอนเทนเนอร์ที่มีที่เก็บข้อมูลที่ซับซ้อนซึ่งไม่ได้จับคู่กับประเภทด้านนอกโดยตรง (เช่นที่::std::vector<bool>คุณพูดถึง)
Sergey.quixoticaxis.Ivanov

1
@ Sergey.quixoticaxis.Ivanov: ระเบียบคือการใช้std::vector<T>ในรหัสแม่แบบเสียถ้าTอาจเป็นboolเพราะstd::vector<bool>มีพฤติกรรมที่แตกต่างจากอินสแตนซ์อื่น ๆ มันมีประโยชน์ std::vectorแต่มันควรจะได้รับชื่อของตัวเองและไม่ได้เป็นความเชี่ยวชาญของ
Ben Voigt

@ BenVoight ฉันเห็นด้วยกับประเด็นที่เกี่ยวกับการตัดสินใจแปลก ๆ ในการสร้างความเชี่ยวชาญ "พิเศษจริงๆ" แต่ฉันรู้สึกว่าความคิดเห็นดั้งเดิมของคุณแสดงให้เห็นว่าการส่งคืนพร็อกซีนั้นแปลกโดยทั่วไป
Sergey.quixoticaxis.Ivanov

2

นอกจากคำตอบที่ยอมรับแล้ว:

struct immutableint {
    immutableint(int i) : i_(i) {}

    const int& get() const { return i_; }
private:
    int i_;
};

ฉันขอยืนยันว่าตัวอย่างนี้ไม่เป็นไรและควรหลีกเลี่ยงหากเป็นไปได้ ทำไม? มันง่ายมากที่จะจบลงด้วยการอ้างอิงห้อยการอ้างอิงห้อย

เพื่อแสดงจุดด้วยตัวอย่าง:

struct Foo
{
    Foo(int i = 42) : boo_(i) {}
    immutableint boo()
    {
        return boo_;
    }  
private:
    immutableint boo_;
};

เข้าสู่เขตอันตราย:

Foo foo;
const int& dangling = foo.boo().get(); // dangling reference!

1

การอ้างอิงย้อนกลับมักใช้ในการดำเนินการบรรทุกเกินพิกัดใน C ++ สำหรับวัตถุขนาดใหญ่เนื่องจากการส่งคืนค่าต้องดำเนินการคัดลอก (ในการบรรทุกเกินพิกัด perator เรามักจะไม่ใช้ตัวชี้เป็นค่าส่งคืน)

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

หากคุณต้องการใช้การส่งคืนการอ้างอิงคุณอาจใช้บัฟเฟอร์ของวัตถุคงที่ ตัวอย่างเช่น

const max_tmp=5; 
Obj& get_tmp()
{
 static int buf=0;
 static Obj Buf[max_tmp];
  if(buf==max_tmp) buf=0;
  return Buf[buf++];
}
Obj& operator+(const Obj& o1, const Obj& o1)
{
 Obj& res=get_tmp();
 // +operation
  return res;
 }

ด้วยวิธีนี้คุณสามารถใช้การอ้างอิงกลับอย่างปลอดภัย

แต่คุณสามารถใช้ตัวชี้แทนการอ้างอิงเพื่อส่งคืนค่าใน functiong


0

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


0

สิ่งที่ดีที่สุดคือการสร้างวัตถุและส่งผ่านมันเป็นพารามิเตอร์การอ้างอิง / ตัวชี้ไปยังฟังก์ชั่นที่จัดสรรตัวแปรนี้

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


-1
    Class Set {
    int *ptr;
    int size;

    public: 
    Set(){
     size =0;
         }

     Set(int size) {
      this->size = size;
      ptr = new int [size];
     }

    int& getPtr(int i) {
     return ptr[i];  // bad practice 
     }
  };

ฟังก์ชั่น getPtr สามารถเข้าถึงหน่วยความจำแบบไดนามิกหลังจากการลบหรือแม้กระทั่งวัตถุ null ซึ่งอาจทำให้เกิดข้อยกเว้นการเข้าถึงที่ไม่ดี ควรใช้ getter และ setter แทนและตรวจสอบขนาดก่อนส่งคืน


-2

ฟังก์ชั่นเป็น lvalue (aka, การคืนค่าการอ้างอิงที่ไม่ใช่แบบ const) ควรถูกลบออกจาก C ++ มันใช้งานง่ายมาก Scott Meyers ต้องการนาที () กับพฤติกรรมนี้

min(a,b) = 0;  // What???

ซึ่งไม่ได้เป็นการปรับปรุงอย่างแท้จริง

setmin (a, b, 0);

หลังยิ่งทำให้รู้สึกมากขึ้น

ฉันรู้ว่าฟังก์ชั่นเนื่องจาก lvalue มีความสำคัญสำหรับสตรีมสไตล์ C ++ แต่มันก็คุ้มค่าที่ชี้ให้เห็นว่าสตรีมสไตล์ C ++ นั้นแย่มาก ฉันไม่ใช่คนเดียวที่คิดอย่างนี้ ... เนื่องจากฉันจำได้ว่า Alexandrescu มีบทความขนาดใหญ่เกี่ยวกับวิธีการทำได้ดีขึ้นและฉันเชื่อว่าการเพิ่มประสิทธิภาพยังพยายามสร้างวิธีการ I / O ที่ปลอดภัยกว่าด้วย


2
แน่ใจว่ามันอันตรายและควรมีการตรวจสอบข้อผิดพลาดของคอมไพเลอร์ที่ดีขึ้น แต่ถ้าไม่มีมันโครงสร้างที่มีประโยชน์บางอย่างไม่สามารถทำได้เช่นโอเปอเรเตอร์ [] () ใน std :: map
j_random_hacker

2
การส่งคืนการอ้างอิงที่ไม่ใช่แบบ const นั้นมีประโยชน์อย่างเหลือเชื่อ vector::operator[]ตัวอย่างเช่น. คุณอยากจะเขียนv.setAt(i, x)หรือv[i] = x? หลังเป็น FAR ที่เหนือกว่า
Miles Rout

1
@MilesRout ฉันจะไปv.setAt(i, x)ตลอดเวลา มันยอดเยี่ยมกว่า
scravy

-2

ฉันวิ่งเข้าไปในปัญหาที่แท้จริงซึ่งมันชั่วร้ายจริงๆ เป็นหลักนักพัฒนากลับการอ้างอิงไปยังวัตถุในเวกเตอร์ แย่จัง !!!

รายละเอียดทั้งหมดที่ฉันเขียนเกี่ยวกับ Janurary: http://developer-resource.blogspot.com/2009/01/pros-and-cons-of-returing-references.html


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

ปัญหาเฉพาะนั้นเกิดจากการถือการอ้างอิงไปยังองค์ประกอบของเวกเตอร์และจากนั้นแก้ไขเวกเตอร์นั้นในทางที่ทำให้การอ้างอิงนั้นไม่ถูกต้อง: หน้า 153, ส่วน 6.2 ของ "C ++ Standard Library: A Tutorial and Reference" - Josuttis อ่าน: "การแทรก หรือการลบองค์ประกอบทำให้การอ้างอิงพอยน์เตอร์และตัววนซ้ำที่อ้างถึงองค์ประกอบต่อไปนี้ไม่ถูกต้องหากการแทรกทำให้เกิดการจัดสรรใหม่จะเป็นการยกเลิกการอ้างอิงทั้งหมดตัววนซ้ำและพอยเตอร์ "
Trent

-15

เกี่ยวกับรหัสที่น่ากลัว:

int& getTheValue()
{
   return *new int;
}

ดังนั้นแน่นอนตัวชี้หน่วยความจำหายไปหลังจากกลับมา แต่ถ้าคุณใช้ shared_ptr เช่นนั้น:

int& getTheValue()
{
   std::shared_ptr<int> p(new int);
   return *p->get();
}

หน่วยความจำไม่ได้หายไปหลังจากกลับมาและจะได้รับหลังจากการมอบหมาย


12
มันจะหายไปเนื่องจากตัวชี้ที่ใช้ร่วมกันออกจากขอบเขตและทำให้จำนวนเต็ม

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