Saya telah menemukan beberapa perilaku yang sangat aneh (pada dentang dan GCC) dalam situasi berikut. Saya punya vektor,, nodes
dengan satu elemen, turunan dari kelas Node
. Saya kemudian memanggil fungsi nodes[0]
yang menambahkan baru Node
ke vektor. Ketika Node baru ditambahkan, bidang objek panggilan direset! Namun, mereka tampaknya kembali normal lagi setelah fungsi selesai.
Saya percaya ini adalah contoh minimal yang dapat direproduksi:
#include <iostream>
#include <vector>
using namespace std;
struct Node;
vector<Node> nodes;
struct Node{
int X;
void set(){
X = 3;
cout << "Before, X = " << X << endl;
nodes.push_back(Node());
cout << "After, X = " << X << endl;
}
};
int main() {
nodes = vector<Node>();
nodes.push_back(Node());
nodes[0].set();
cout << "Finally, X = " << nodes[0].X << endl;
}
Output yang mana
Before, X = 3
After, X = 0
Finally, X = 3
Meskipun Anda akan mengharapkan X tetap tidak berubah oleh proses.
Hal lain yang saya coba:
- Jika saya menghapus baris yang menambahkan bagian
Node
dalamset()
, maka itu menghasilkan X = 3 setiap kali. - Jika saya membuat yang baru
Node
dan menyebutnya (Node p = nodes[0]
) maka hasilnya adalah 3, 3, 3 - Jika saya membuat referensi
Node
dan menyebutnya pada itu (Node &p = nodes[0]
) maka hasilnya adalah 3, 0, 0 (mungkin yang ini adalah karena referensi tersebut hilang ketika vektor diubah ukurannya?)
Apakah ini perilaku yang tidak terdefinisi untuk beberapa alasan? Mengapa?
reserve(2)
vektor sebelum memanggilset()
ini akan didefinisikan perilaku. Tapi menulis fungsi sepertiset
itu mengharuskan pengguna untukreserve
ukuran yang cukup sebelum memanggilnya untuk menghindari perilaku yang tidak jelas adalah desain yang buruk, jadi jangan lakukan itu.Jawaban:
Kode Anda memiliki perilaku yang tidak terdefinisi. Di
Akses ke
X
benarthis->X
- benar danthis
merupakan penunjuk ke anggota vektor. Ketika Anda melakukannya,nodes.push_back(Node());
Anda menambahkan elemen baru ke vektor dan proses yang realokasi, yang membatalkan semua iterator, pointer dan referensi ke elemen dalam vektor. Itu berartimenggunakan
this
yang tidak lagi valid.sumber
push_back
perilaku yang sudah tidak terdefinisi (karena kita kemudian dalam fungsi anggota dengan tidak validthis
) atau apakah UB muncul pertama kali kita menggunakanthis
pointer? Apakah mungkin untuk itureturn 42;
?nodes
independen dariNode
instance sehingga tidak ada UB yang meneleponpush_back
. UB menggunakan pointer yang tidak valid setelahnya.void set(Node* this)
, tidak terdefinisi untuk memberikannya sebuah pointer yang tidak valid, atau kefree()
dalam fungsinya. Saya tidak yakin tetapi saya membayangkan bahwa bahkan((Node*) nullptr)->set()
didefinisikan jika Anda tidak menggunakanthis
dan metode ini bukan virtual.((Node *) nullptr)->set()
ok, karena dereferences ini pointer nol (Anda melihat kore itu dengan jelas ketika menulisnya sama dengan(*((Node *) nullptr)).set();
).akan realokasi vektor, sehingga mengubah alamat
nodes[0]
, tetapithis
tidak diperbarui.coba ganti
set
metode dengan kode ini:perhatikan bagaimana
&nodes[0]
perbedaannya setelah meneleponpush_back
.-fsanitize=address
akan menangkap ini, dan bahkan memberi tahu Anda di baris mana memori itu dibebaskan jika Anda juga mengompilasinya-g
.sumber