การส่งต่อการอ้างอิงไปยังพอยน์เตอร์ใน C ++


130

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

นี่คือสิ่งที่ฉันทำ:

void myfunc(string*& val)
{
    // Do stuff to the string pointer
}

// sometime later 
{
    // ...
    string s;
    myfunc(&s);
    // ...
}

และฉันได้รับข้อผิดพลาดนี้:

ไม่สามารถแปลงพารามิเตอร์ 1 จาก 'std :: string *' เป็น 'std :: string * &'

คำตอบ:


126

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

string s;
string* _s = &s;
myfunc(_s);

ควรรวบรวมได้ดี

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


86

ปัญหาคือว่าคุณกำลังพยายามที่จะผูกชั่วคราวเพื่อการอ้างอิงซึ่ง c ++ constไม่อนุญาตเว้นแต่อ้างอิง

ดังนั้นคุณสามารถทำอย่างใดอย่างหนึ่งต่อไปนี้:

void myfunc(string*& val)
{
    // Do stuff to the string pointer
}


void myfunc2(string* const& val)
{
    // Do stuff to the string pointer
}

int main()
// sometime later 
{
    // ...
    string s;
    string* ps = &s;

    myfunc( ps);   // OK because ps is not a temporary
    myfunc2( &s);  // OK because the parameter is a const&
    // ...

    return 0;
}

มันเป็นคำแนะนำโดยเฉพาะอย่างยิ่งในการเขียนการพิมพ์ออกทางนายเสี้ยนไม่ในตัวอย่างฉบับที่ 2 - แทนเทียบเท่าน้อยชัดเจนเหมือนเช่นstring* const& val const string* &valในสายตาของฉันนี่เป็นการอ้างอิง const อย่างชัดเจนถึงตัวชี้ไปยังสตริง โดยส่วนตัวฉันชอบเขียนT const&แทนการconst T&ประกาศเพื่อให้ได้คำชี้แจงที่แน่นอนนี้
fish2000

1
เลือกนิตย์ (ไม่วิจารณ์): สำหรับผมแล้วคำพูดที่ว่าbind a temporary to the referenceเป็นที่ชัดเจนในคำตอบนี้มากกว่า @ Chris reference to an actual string pointer in the calling scope, not an anonymous string pointerเป็น ไม่ว่าทั้งสองอย่างจะถูกต้องจากมุมมองของฉัน
kevinarpe

1
@ fish2000 ไม่เพียงแค่นั้นconst string*&และstring* const&เป็นชนิดที่แตกต่างกันจริง อย่างแรกคือการไม่constอ้างอิงถึง a const string*ในขณะที่อันที่สองเป็นการconstอ้างอิงถึง a string*.
Justin Time - คืนสถานะ Monica

@ fish2000 ระวัง "ชอบ" T const&ถึงconst T&- เนื่องจาก @Justin Time ชี้ให้เห็นว่าเป็นประเภทต่างๆ มันอาจจะไม่มีผลกระทบบางครั้ง แต่ในสถานการณ์อื่น ๆ ก็จะมีความสำคัญอย่างยิ่งที่จะชอบเดียวหรืออื่น ๆ มากเช่นเลือกที่จะint double
omatai

3
@ fish2000 - มีคนหนึ่งพูดว่า "นี่คือข้อมูลอ้างอิงที่คุณสามารถเปลี่ยนเป็นสิ่งที่คุณไม่สามารถเปลี่ยนแปลงได้" ในขณะที่อีกคนพูดว่า "นี่คือข้อมูลอ้างอิงที่คุณไม่สามารถเปลี่ยนเป็นสิ่งที่คุณเปลี่ยนแปลงได้" ในตัวอย่างนี้คุณไม่สามารถแลกเปลี่ยนทั้งสองได้
omatai

10

เปลี่ยนเป็น:

  std::string s;
  std::string* pS = &s;
  myfunc(pS);

แก้ไข:

สิ่งนี้ถูกเรียกref-to-pointerและคุณไม่สามารถส่งผ่านที่อยู่ชั่วคราวเพื่ออ้างอิงถึงฟังก์ชันได้ (เว้นแต่จะเป็นconst reference)

แม้ว่าฉันได้แสดงstd::string* pS = &s;(ตัวชี้ไปยังตัวแปรท้องถิ่น) การใช้งานโดยทั่วไปจะเป็น: เมื่อคุณต้องการให้ callee เปลี่ยนตัวชี้เองไม่ใช่วัตถุที่มันชี้ ตัวอย่างเช่นฟังก์ชันที่จัดสรรหน่วยความจำและกำหนดที่อยู่ของบล็อกหน่วยความจำที่จัดสรรให้กับอาร์กิวเมนต์ต้องใช้การอ้างอิงไปยังตัวชี้หรือตัวชี้ไปยังตัวชี้:

void myfunc(string*& val)
{
//val is valid even after function call
   val = new std::string("Test");

}

5

&s สร้างตัวชี้ชั่วคราวไปยังสตริงและคุณไม่สามารถอ้างอิงถึงวัตถุชั่วคราวได้


4
นี่ไม่เป็นความจริง - คุณสามารถอ้างอิง const ชั่วคราว
1800 ข้อมูล

3

ลอง:

void myfunc(string& val)
{
    // Do stuff to the string pointer
}

// sometime later 
{
    // ...
    string s;
    myfunc(s);
    // ...
}

หรือ

void myfunc(string* val)
{
    // Do stuff to the string pointer
}

// sometime later 
{
    // ...
    string s;
    myfunc(&s);
    // ...
}

สำหรับสิ่งที่ฉันกำลังทำอยู่ฉันต้องการที่อยู่ของตัวชี้และตัวชี้ด้วย ฉันไม่ต้องการส่งตัวชี้ตามค่า
Alex

ยังไม่เข้าใจว่าคุณพยายามทำอะไรให้สำเร็จ คุณไม่สามารถมี "address of the pointer" ของ "string s" ได้เพียงเพราะว่า "string s" ไม่ใช่ตัวชี้
สาเก

@ อเล็กซ์: ฉันคิดว่าคุณต้องตรวจสอบว่าสตริงตรงกับสตริงอื่นที่คุณจัดเก็บหรือไม่และไม่ใช่แค่ว่าเนื้อหาเหมือนกันหรือไม่ หากเป็นเช่นนั้นโปรดทราบว่าคุณสามารถใช้แอดเดรสของตัวดำเนินการเพื่ออ้างอิงและจะทำให้คุณได้รับแอดเดรสของอ็อบเจ็กต์ที่อ้างอิง: void f (std :: string const & s) {std :: string const * p = & s; }
David Rodríguez - หยดน้ำ

3

แก้ไข: ฉันทดลองบางอย่างและค้นพบสิ่งที่ละเอียดกว่าที่ฉันคิดไว้เล็กน้อย นี่คือสิ่งที่ฉันคิดว่าตอนนี้เป็นคำตอบที่ถูกต้อง

&sไม่ได้เป็น lvalue constเพื่อให้คุณไม่สามารถสร้างการอ้างอิงถึงมันจนกว่าประเภทของการอ้างอิงการอ้างอิงถึง ตัวอย่างเช่นคุณไม่สามารถทำได้

string * &r = &s;

แต่คุณสามารถทำได้

string * const &r = &s;

หากคุณใส่คำประกาศที่คล้ายกันในส่วนหัวของฟังก์ชันก็จะใช้ได้

void myfunc(string * const &a) { ... }

ยังมีอีกประเด็นหนึ่งคือ กฎคือคุณจะได้รับการอ้างอิงไปยังชั่วคราวก็ต่อเมื่อเป็นconstเช่นนั้น ดังนั้นในกรณีนี้อาจมีคนโต้แย้งว่า & s เป็นแบบชั่วคราวและต้องประกาศconstในฟังก์ชันต้นแบบ จากมุมมองในทางปฏิบัติมันไม่ได้สร้างความแตกต่างในกรณีนี้ (เป็นค่า rvalue หรือชั่วคราวไม่ว่าจะด้วยวิธีใดก็ใช้กฎเดียวกัน) อย่างไรก็ตามพูดอย่างเคร่งครัดฉันคิดว่ามันไม่ใช่ค่าชั่วคราว แต่เป็นค่า rvalue ฉันสงสัยว่ามีวิธีแยกแยะระหว่างสองอย่างนี้หรือไม่ (บางทีมันอาจจะกำหนดง่ายๆว่าชั่วคราวทั้งหมดเป็นค่า rvalues ​​และค่าที่ไม่ใช่ lvalues ​​ทั้งหมดเป็นชั่วคราวฉันไม่ใช่ผู้เชี่ยวชาญในมาตรฐานนี้)

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

string *p = &s;
myfunc(p);

หากคุณต้องการอ้างอิงsหรือชี้ไปให้sทำสิ่งที่ตรงไปตรงมา


1

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

void BinTree::safe_tree(BinTree * &vertex ) {
    if ( vertex!=0 ) {  // base case
        safe_tree(vertex->left);    // left subtree.
            safe_tree(vertex->right);   //  right subtree.
          // delete vertex;  // using this delete causes an error, since they were deleted on the fly using inorder_LVR. If inorder_LVR does not perform delete to the nodes, then, use delete vertex;
        vertex=0;  // making a safe pointer
    }
} // end in

บรรทัดล่างการอ้างอิงถึงตัวชี้ไม่ถูกต้องเมื่อพารามิเตอร์ที่เป็นทางการคือตัวชี้ (นี้)


1

ยินดีต้อนรับสู่การอ้างอิง C ++ 11 และ rvalue:

#include <cassert>
#include <string>

using std::string;

void myfunc(string*&& val)
{
    assert(&val);
    assert(val);
    assert(val->c_str());
    // Do stuff to the string pointer
}

// sometime later 
int main () {
    // ...
    string s;
    myfunc(&s);
    // ...
}

ตอนนี้คุณสามารถเข้าถึงค่าของตัวชี้ (อ้างถึงโดยval) ซึ่งเป็นที่อยู่ของสตริง

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

ระวัง: ค่าของตัวชี้จะใช้ได้จนกว่าจะmyfunc()ส่งคืนเท่านั้น ในที่สุดมันก็ชั่วคราว


-1

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

Myfunc(String** s)

-8

myfunc ("string * & val") ตัวนี้ไม่สมเหตุสมผลเลย "string * & val" หมายถึง "string val", * และ & ยกเลิกซึ่งกันและกัน ในที่สุดก็ไม่สามารถส่งตัวแปรสตริงไปยังฟังก์ชัน ("string val") ประเภทข้อมูลพื้นฐานเท่านั้นที่สามารถส่งผ่านไปยังฟังก์ชันสำหรับข้อมูลประเภทอื่น ๆ จำเป็นต้องส่งผ่านเป็นตัวชี้หรือการอ้างอิง คุณสามารถมี string & val หรือ string * val ให้กับฟังก์ชัน


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