Pertanyaan saya dapat didiskusikan, di mana string kembali dari stringstream.str().c_str()
hidup dalam memori, dan mengapa tidak dapat ditugaskan ke const char*
?
Contoh kode ini akan menjelaskannya lebih baik daripada yang saya bisa
#include <string>
#include <sstream>
#include <iostream>
using namespace std;
int main()
{
stringstream ss("this is a string\n");
string str(ss.str());
const char* cstr1 = str.c_str();
const char* cstr2 = ss.str().c_str();
cout << cstr1 // Prints correctly
<< cstr2; // ERROR, prints out garbage
system("PAUSE");
return 0;
}
Asumsi yang stringstream.str().c_str()
dapat ditugaskan ke const char*
menyebabkan bug yang saya butuh waktu untuk melacak.
Untuk poin bonus, adakah yang bisa menjelaskan mengapa mengganti cout
pernyataan itu dengan
cout << cstr // Prints correctly
<< ss.str().c_str() // Prints correctly
<< cstr2; // Prints correctly (???)
mencetak string dengan benar?
Saya sedang menyusun di Visual Studio 2008.
sumber
str()
diterapkan sedemikian rupa sehingga RVO dapat menendang (yang sangat mungkin), kompiler diizinkan untuk membangun hasilnya secara langsung ke dalamtmp
, menghilangkan yang sementara; dan setiap kompiler C ++ modern akan melakukannya ketika optimisasi diaktifkan. Tentu saja, solusi bind-to-const-reference menjamin tidak ada salinan, jadi mungkin lebih disukai - tapi saya pikir itu masih layak diklarifikasi.Apa yang Anda lakukan adalah membuat sementara. Sementara itu ada dalam ruang lingkup yang ditentukan oleh kompiler, sehingga cukup lama untuk memenuhi persyaratan ke mana ia pergi.
Segera setelah pernyataan
const char* cstr2 = ss.str().c_str();
selesai, kompiler tidak melihat alasan untuk menjaga string sementara sekitar, dan itu hancur, dan dengan demikian Andaconst char *
menunjuk ke memori yang bebas.Pernyataan Anda
string str(ss.str());
berarti bahwa sementara digunakan di konstruktor untukstring
variabelstr
yang telah Anda tempatkan di tumpukan lokal, dan itu tetap ada selama yang Anda harapkan: sampai akhir blok, atau fungsi yang Anda tulis. Oleh karena itu,const char *
ingatan di dalamnya masih bagus ketika Anda mencobacout
.sumber
Di baris ini:
ss.str()
akan membuat salinan dari isi stringstream. Saat Anda meneleponc_str()
pada saluran yang sama, Anda akan mereferensikan data yang sah, tetapi setelah baris itu string tersebut akan dihancurkan, meninggalkan Andachar*
untuk menunjuk ke memori yang tidak dimiliki.sumber
Objek std :: string yang dikembalikan oleh ss.str () adalah objek sementara yang akan memiliki masa hidup terbatas pada ekspresi. Jadi, Anda tidak dapat menetapkan pointer ke objek sementara tanpa mendapatkan sampah.
Sekarang, ada satu pengecualian: jika Anda menggunakan referensi const untuk mendapatkan objek sementara, adalah sah untuk menggunakannya untuk waktu hidup yang lebih luas. Misalnya yang harus Anda lakukan:
Dengan begitu Anda mendapatkan string untuk waktu yang lebih lama.
Sekarang, Anda harus tahu bahwa ada semacam optimasi yang disebut RVO yang mengatakan bahwa jika kompilator melihat inisialisasi melalui pemanggilan fungsi dan fungsi itu mengembalikan sementara, itu tidak akan melakukan salinan tetapi hanya membuat nilai yang diberikan menjadi sementara . Dengan begitu Anda tidak perlu menggunakan referensi, itu hanya jika Anda ingin memastikan bahwa itu tidak akan menyalin bahwa itu perlu. Demikian melakukan:
akan lebih baik dan lebih sederhana.
sumber
The
ss.str()
sementara hancur setelah inisialisasicstr2
selesai. Jadi ketika Anda mencetaknyacout
, c-string yang dikaitkan denganstd::string
temporer itu telah lama rusak, dan dengan demikian Anda akan beruntung jika crash dan menegaskan, dan tidak beruntung jika mencetak sampah atau tampaknya berfungsi.C-string di mana
cstr1
menunjuk ke, dikaitkan dengan string yang masih ada pada saat Anda melakukancout
- sehingga mencetak hasil dengan benar.Dalam kode berikut, yang pertama
cstr
benar (saya menganggap itucstr1
dalam kode asli?). Yang kedua mencetak c-string yang terkait dengan objek string sementarass.str()
. Objek dihancurkan pada akhir evaluasi ekspresi penuh yang muncul. Ekspresi penuh adalah seluruhcout << ...
ekspresi - jadi ketika c-string adalah output, objek string yang terkait masih ada. Karenacstr2
- itu adalah kejahatan murni yang berhasil. Kemungkinan besar secara internal memilih lokasi penyimpanan yang sama untuk sementara baru yang sudah dipilih untuk sementara digunakan untuk menginisialisasicstr2
. Itu juga bisa crash.Kembalinya
c_str()
biasanya hanya akan menunjuk ke buffer string internal - tapi itu bukan keharusan. String dapat membuat buffer jika implementasi internalnya tidak berdekatan misalnya (itu sangat mungkin - tetapi dalam C ++ Standard berikutnya, string perlu disimpan secara bersebelahan).Dalam GCC, string menggunakan penghitungan referensi dan copy-on-write. Dengan demikian, Anda akan menemukan bahwa yang berikut ini benar (ya, setidaknya pada versi GCC saya)
Dua string berbagi buffer yang sama di sini. Pada saat Anda mengubah salah satunya, buffer akan disalin dan masing-masing akan menyimpan salinannya yang terpisah. Implementasi string lain melakukan hal yang berbeda.
sumber