C ++ Mengembalikan referensi ke variabel lokal


116

Apakah kode berikut (func1 ()) benar jika harus mengembalikan i? Saya ingat pernah membaca di suatu tempat bahwa ada masalah saat mengembalikan referensi ke variabel lokal. Apa yang membedakannya dari func2 ()?

int& func1()
{
    int i;
    i = 1;
    return i;
}

int* func2()
{
    int* p;
    p = new int;
    *p = 1;
    return p;
}

1
Jika Anda mengubah func1 () untuk menggunakan memori yang dialokasikan secara dinamis, maka keduanya sama :-)int& i = * new int;
Martin York

Jawaban:


193

Potongan kode ini:

int& func1()
{
    int i;
    i = 1;
    return i;
}

tidak akan berfungsi karena Anda mengembalikan alias (referensi) ke objek dengan masa pakai terbatas pada cakupan pemanggilan fungsi. Itu berarti sekali func1()kembali, int imati, membuat referensi yang dikembalikan dari fungsi tidak berharga karena sekarang merujuk ke objek yang tidak ada.

int main()
{
    int& p = func1();
    /* p is garbage */
}

Versi kedua berfungsi karena variabel dialokasikan di penyimpanan gratis, yang tidak terikat pada masa panggilan fungsi. Namun, Anda bertanggung jawab untuk deletemengalokasikan int.

int* func2()
{
    int* p;
    p = new int;
    *p = 1;
    return p;
}

int main()
{
    int* p = func2();
    /* pointee still exists */
    delete p; // get rid of it
}

Biasanya Anda akan membungkus pointer dalam beberapa kelas RAII dan / atau fungsi pabrik sehingga Anda tidak perlu melakukannya deletesendiri.

Dalam kedua kasus tersebut, Anda dapat mengembalikan nilainya sendiri (meskipun saya menyadari contoh yang Anda berikan mungkin dibuat-buat):

int func3()
{
    return 1;
}

int main()
{
    int v = func3();
    // do whatever you want with the returned value
}

Perhatikan bahwa mengembalikan objek besar dengan cara yang sama func3()mengembalikan nilai primitif karena hampir setiap compiler saat ini menerapkan beberapa bentuk pengoptimalan nilai pengembalian :

class big_object 
{ 
public:
    big_object(/* constructor arguments */);
    ~big_object();
    big_object(const big_object& rhs);
    big_object& operator=(const big_object& rhs);
    /* public methods */
private:
    /* data members */
};

big_object func4()
{
    return big_object(/* constructor arguments */);
}

int main()
{
     // no copy is actually made, if your compiler supports RVO
    big_object o = func4();    
}

Menariknya, mengikat sementara ke referensi const sangat legal C ++ .

int main()
{
    // This works! The returned temporary will last as long as the reference exists
    const big_object& o = func4();    
    // This does *not* work! It's not legal C++ because reference is not const.
    // big_object& o = func4();  
}

2
Penjelasan yang indah. : hattip: Pada potongan kode ketiga, Anda menghapus int* p = func2(); delete p;Now, ketika Anda menghapus 'p', apakah itu berarti memori yang dialokasikan "di dalam" func2()definisi fungsi juga terhapus?
Aquarius_Girl

2
@Anisha Kaul: Ya. Memori dialokasikan di dalam func2()dan dilepaskan di luar di baris berikutnya. Ini adalah cara yang agak rawan kesalahan untuk menangani memori, seperti yang saya katakan Anda akan menggunakan beberapa varian RAII sebagai gantinya. Ngomong-ngomong, Anda terdengar seperti sedang mempelajari C ++. Saya sarankan untuk mengambil buku pengantar C ++ yang bagus untuk dipelajari. Selain itu, untuk referensi di masa mendatang jika Anda memiliki pertanyaan, Anda selalu dapat memposting pertanyaan tersebut di Stack Overflow. Komentar tidak dimaksudkan untuk mengajukan pertanyaan yang sama sekali baru.
In silico

Sekarang saya mengerti, Anda telah melakukannya dengan benar! Fungsi mengembalikan pointer, dan di luar fungsi itu, Anda telah menghapus memori yang ditunjuknya. Sudah jelas sekarang, dan terima kasih untuk tautannya.
Aquarius_Girl

dan kamu sudah mengedit jawabannya ?? : mad: Saya bisa saja melewatkannya dengan mudah. ;);)
Aquarius_Girl

@Anisha Kaul: Tidak, saya tidak. Terakhir kali saya mengedit jawaban saya adalah pada 10 Januari, sesuai dengan cap waktu di bawah posting saya.
In silico

18

Variabel lokal adalah memori di tumpukan, memori itu tidak otomatis tidak valid ketika Anda keluar dari ruang lingkup. Dari Fungsi yang lebih dalam bersarang (lebih tinggi di tumpukan dalam memori), sangat aman untuk mengakses memori ini.

Setelah Fungsi kembali dan berakhir, semuanya menjadi berbahaya. Biasanya memori tidak dihapus atau ditimpa ketika Anda kembali, yang berarti memori di alamat itu masih berisi data Anda - penunjuk tampaknya valid.

Sampai fungsi lain membangun tumpukan dan menimpanya. Inilah sebabnya mengapa ini dapat berfungsi untuk sementara waktu - dan kemudian tiba-tiba berhenti berfungsi setelah satu set fungsi yang sangat bertingkat, atau fungsi dengan objek lokal berukuran sangat besar atau banyak, mencapai memori tumpukan itu lagi.

Bahkan dapat terjadi bahwa Anda mencapai bagian program yang sama lagi, dan menimpa variabel fungsi lokal lama Anda dengan variabel fungsi baru. Semua ini sangat berbahaya dan harus sangat dicegah. Jangan gunakan pointer ke objek lokal!


2

Hal yang baik untuk diingat adalah aturan sederhana ini, dan berlaku untuk parameter dan tipe kembalian ...

  • Nilai - membuat salinan item yang dimaksud.
  • Pointer - mengacu pada alamat item yang dimaksud.
  • Referensi - secara harfiah adalah item yang dimaksud.

Ada waktu dan tempat untuk masing-masing, jadi pastikan Anda mengenal mereka. Variabel lokal, seperti yang Anda tunjukkan di sini, hanyalah itu, terbatas pada waktu mereka secara lokal hidup dalam lingkup fungsi. Dalam contoh Anda, memiliki tipe pengembalian int*dan pengembalian &isama-sama salah. Anda akan lebih baik jika melakukan ini ...

void func1(int& oValue)
{
    oValue = 1;
}

Melakukannya akan langsung mengubah nilai parameter yang Anda berikan. Padahal kode ini ...

void func1(int oValue)
{
    oValue = 1;
}

tidak akan. Itu hanya akan mengubah nilai oValuelokal ke pemanggilan fungsi. Alasannya adalah karena Anda sebenarnya hanya mengubah salinan "lokal" dari oValue, dan bukan oValueitu sendiri.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.