Adakah orang di sini yang pernah menggunakan "penempatan baru" C ++? Jika demikian, untuk apa? Sepertinya saya hanya akan berguna pada perangkat keras yang dipetakan dengan memori.
c++
memory-management
new-operator
placement-new
Kepala Geek
sumber
sumber
p = pt
dan menggunakan operator penugasanPoint
alih-alih melakukannew(&p) Point(pt)
? Saya ingin tahu perbedaan antara keduanya. Apakah mantan memanggiloperator=
Point, sedangkan yang kedua memanggil copy constructorPoint
? tapi saya masih tidak begitu jelas mengapa yang satu lebih baik dari yang lain.U::operator=
baru saja dipanggil.Jawaban:
Penempatan baru memungkinkan Anda membuat objek dalam memori yang sudah dialokasikan.
Anda mungkin ingin melakukan ini untuk optimasi ketika Anda perlu membangun beberapa instance objek, dan lebih cepat untuk tidak mengalokasikan kembali memori setiap kali Anda membutuhkan instance baru. Sebagai gantinya, mungkin lebih efisien untuk melakukan alokasi tunggal untuk sepotong memori yang dapat menampung banyak objek, meskipun Anda tidak ingin menggunakan semuanya sekaligus.
DevX memberikan contoh yang baik :
Anda mungkin juga ingin memastikan tidak ada kegagalan alokasi pada bagian tertentu dari kode kritis (misalnya, dalam kode yang dijalankan oleh alat pacu jantung). Dalam hal ini Anda ingin mengalokasikan memori lebih awal, lalu gunakan penempatan baru dalam bagian kritis.
Deallokasi dalam penempatan baru
Anda seharusnya tidak membatalkan alokasi setiap objek yang menggunakan buffer memori. Sebaliknya Anda harus menghapus [] hanya buffer asli. Anda kemudian harus memanggil destruktor kelas Anda secara manual. Untuk saran yang bagus tentang ini, silakan lihat FAQ Stroustrup di: Apakah ada "penghapusan penempatan" ?
sumber
delete[]
aslichar
. Menggunakan penempatannew
telah mengakhiri masa pakaichar
objek asli dengan menggunakan kembali penyimpanannya. Jika sekarang Anda memanggildelete[] buf
tipe dinamis dari objek yang ditunjukkan tidak lagi cocok dengan tipe statisnya, maka Anda memiliki perilaku yang tidak terdefinisi. Itu lebih konsisten untuk menggunakanoperator new
/operator delete
mengalokasikan memori mentah dimasukkan untuk digunakan oleh penempatannew
.#include <new>
.Kami menggunakannya dengan kumpulan memori khusus. Hanya sebuah sketsa:
Sekarang Anda dapat mengelompokkan objek dalam satu arena memori tunggal, pilih pengalokasi yang sangat cepat tetapi tidak ada alokasi, gunakan pemetaan memori, dan semantik lain yang ingin Anda terapkan dengan memilih pool dan meneruskannya sebagai argumen penempatan objek. operator baru.
sumber
allocate()
suatu tempat?Ini berguna jika Anda ingin memisahkan alokasi dari inisialisasi. STL menggunakan penempatan baru untuk membuat elemen wadah.
sumber
Saya telah menggunakannya dalam pemrograman waktu nyata. Kami biasanya tidak ingin melakukan alokasi dinamis (atau deallokasi) setelah sistem dimulai, karena tidak ada jaminan berapa lama waktu yang diperlukan.
Apa yang bisa saya lakukan adalah mengalokasikan memori dalam jumlah besar (cukup besar untuk menampung jumlah berapapun yang dibutuhkan oleh kelas). Kemudian, begitu saya mengetahui pada saat runtime bagaimana membangun sesuatu, penempatan baru dapat digunakan untuk membangun objek tepat di tempat yang saya inginkan. Satu situasi yang saya tahu saya gunakan adalah untuk membantu membuat buffer melingkar yang heterogen .
Itu tentu saja bukan untuk orang yang lemah hati, tetapi karena itulah mereka membuat sintaksis untuknya secara agak degil.
sumber
Saya telah menggunakannya untuk membangun objek yang dialokasikan pada stack melalui alokasi ().
plug shameless: Saya ngeblog tentang hal itu di sini .
sumber
boost::array
. Bisakah Anda sedikit mengembangkannya?Head Geek: BINGO! Anda benar-benar mendapatkannya - itulah yang tepat untuk itu. Dalam banyak lingkungan tertanam, kendala eksternal dan / atau keseluruhan skenario penggunaan memaksa pemrogram untuk memisahkan alokasi suatu objek dari inisialisasi. Disatukan, C ++ menyebut ini "instantiation"; tetapi setiap kali tindakan konstruktor harus secara eksplisit dipanggil TANPA alokasi dinamis atau otomatis, penempatan baru adalah cara untuk melakukannya. Ini juga cara sempurna untuk menemukan objek C ++ global yang disematkan ke alamat komponen perangkat keras (I / O yang dipetakan memori), atau untuk objek statis apa pun yang, karena alasan apa pun, harus berada di alamat tetap.
sumber
Saya telah menggunakannya untuk membuat kelas Variant (yaitu objek yang dapat mewakili nilai tunggal yang dapat menjadi salah satu dari sejumlah jenis yang berbeda).
Jika semua tipe nilai yang didukung oleh kelas Variant adalah tipe POD (mis. Int, float, double, bool) maka gabungan gaya C yang ditandai sudah cukup, tetapi jika Anda ingin beberapa tipe nilai menjadi objek C ++ ( mis. std :: string), fitur C union tidak akan berfungsi, karena tipe data non-POD mungkin tidak dinyatakan sebagai bagian dari union.
Jadi alih-alih saya mengalokasikan array byte yang cukup besar (mis. Sizeof (the_largest_data_type_I_support)) dan menggunakan penempatan baru untuk menginisialisasi objek C ++ yang sesuai di area itu ketika Variant diatur untuk menyimpan nilai jenis itu. (Dan penghapusan penempatan sebelumnya ketika beralih dari tipe data non-POD yang berbeda, tentu saja)
sumber
new
untuk menginisialisasi subkelas non-POD. Ref: stackoverflow.com/a/33289972/2757035 Menemukan kembali roda ini menggunakan array byte besar yang sewenang-wenang adalah bagian akrobat yang mengesankan tetapi tampaknya sama sekali tidak perlu, Jadi, apa yang telah saya lewatkan? :)Penempatan baru juga sangat berguna saat membuat serial (katakan dengan boost :: serialisasi). Dalam 10 tahun c ++ ini hanya kasus kedua saya membutuhkan penempatan baru untuk (ketiga jika Anda menyertakan wawancara :)).
sumber
Ini juga berguna ketika Anda ingin menginisialisasi ulang struktur global atau yang dialokasikan secara statis.
Cara C lama digunakan
memset()
untuk mengatur semua elemen ke 0. Anda tidak bisa melakukan itu di C ++ karena vtables dan konstruktor objek kustom.Jadi saya terkadang menggunakan yang berikut ini
sumber
Saya pikir ini belum disorot oleh jawaban apa pun, tetapi contoh bagus dan penggunaan lain untuk penempatan baru adalah untuk mengurangi fragmentasi memori (dengan menggunakan kumpulan memori). Ini sangat berguna dalam sistem yang tersemat dan ketersediaan tinggi. Dalam kasus terakhir ini sangat penting karena untuk sistem yang harus berjalan 24/365 hari sangat penting untuk tidak memiliki fragmentasi. Masalah ini tidak ada hubungannya dengan kebocoran memori.
Bahkan ketika implementasi malloc yang sangat baik digunakan (atau fungsi manajemen memori serupa) sangat sulit untuk berurusan dengan fragmentasi untuk waktu yang lama. Pada titik tertentu jika Anda tidak mengelola dengan cerdas reservasi memori / pelepasan panggilan Anda bisa berakhir dengan banyak celah kecil yang sulit untuk digunakan kembali (ditetapkan untuk reservasi baru). Jadi, salah satu solusi yang digunakan dalam kasus ini adalah menggunakan kolam memori untuk mengalokasikan sebelum menyerahkan memori untuk objek aplikasi. After-ward setiap kali Anda membutuhkan memori untuk beberapa objek, Anda cukup menggunakan penempatan baru untuk membuat objek baru pada memori yang sudah dipesan.
Dengan cara ini, setelah aplikasi Anda mulai, Anda sudah memiliki semua memori yang diperlukan dicadangkan. Semua reservasi / rilis memori baru pergi ke kolam yang dialokasikan (Anda mungkin memiliki beberapa kolam, satu untuk setiap kelas objek yang berbeda). Tidak ada fragmentasi memori yang terjadi dalam kasus ini karena tidak ada celah dan sistem Anda dapat berjalan untuk periode yang sangat lama (bertahun-tahun) tanpa menderita fragmentasi.
Saya melihat ini dalam prakteknya khusus untuk VxWorks RTOS karena sistem alokasi memori standarnya banyak menderita dari fragmentasi. Jadi mengalokasikan memori melalui metode baru / malloc pada dasarnya dilarang dalam proyek. Semua pemesanan memori harus pergi ke kolam memori khusus.
sumber
Ini sebenarnya jenis yang diperlukan untuk mengimplementasikan segala jenis struktur data yang mengalokasikan lebih banyak memori daripada jumlah minimum yang diperlukan untuk jumlah elemen yang dimasukkan (yaitu, apa pun selain struktur tertaut yang mengalokasikan satu node pada suatu waktu).
Kontainer take suka
unordered_map
,vector
ataudeque
. Ini semua mengalokasikan lebih banyak memori daripada yang diperlukan minimal untuk elemen yang Anda masukkan sejauh ini untuk menghindari membutuhkan alokasi tumpukan untuk setiap penyisipan tunggal. Mari kita gunakanvector
sebagai contoh paling sederhana.Saat kamu melakukan:
... itu tidak benar-benar membangun seribu Foos. Itu hanya mengalokasikan / cadangan memori untuk mereka. Jika
vector
tidak menggunakan penempatan baru di sini, itu akan membangun-default diFoos
semua tempat serta harus memanggil destruktor mereka bahkan untuk elemen yang bahkan tidak pernah Anda masukkan di tempat pertama.Alokasi! = Konstruksi, Membebaskan! = Penghancuran
Secara umum, untuk menerapkan banyak struktur data seperti di atas, Anda tidak dapat memperlakukan alokasi memori dan membangun elemen sebagai satu hal yang tidak dapat dibagi, dan Anda juga tidak dapat memperlakukan memori bebas dan menghancurkan elemen sebagai satu hal yang tidak dapat dibagi.
Harus ada pemisahan antara ide-ide ini untuk menghindari konstruktor dan penghancur yang berlebihan yang tidak perlu ke kiri dan kanan, dan itulah sebabnya perpustakaan standar memisahkan ide
std::allocator
(yang tidak membangun atau menghancurkan elemen ketika mengalokasikan / membebaskan memori *) dari kontainer yang menggunakannya yang secara manual membangun elemen menggunakan penempatan yang baru dan secara manual menghancurkan elemen menggunakan doa eksplisit dari destruktor.Jadi, bagaimanapun, saya cenderung sering menggunakannya karena saya telah menulis sejumlah kontainer standar C ++ yang sesuai dengan tujuan umum yang tidak dapat dibangun dalam hal yang sudah ada. Termasuk di antaranya adalah implementasi vektor kecil yang saya buat beberapa dekade yang lalu untuk menghindari alokasi tumpukan dalam kasus-kasus umum, dan trie yang hemat memori (tidak mengalokasikan satu simpul pada satu waktu). Dalam kedua kasus saya tidak bisa benar-benar menerapkannya menggunakan wadah yang ada, jadi saya harus menggunakannya
placement new
untuk menghindari konstruktor dan destruktor berlebihan pada hal-hal yang tidak perlu kiri dan kanan.Tentu saja jika Anda pernah bekerja dengan pengalokasi khusus untuk mengalokasikan objek secara individual, seperti daftar gratis, maka Anda umumnya juga ingin menggunakan
placement new
, seperti ini (contoh dasar yang tidak repot dengan pengecualian-keselamatan atau RAII):sumber
Berguna jika Anda membangun kernel - di mana Anda menempatkan kode kernel yang Anda baca dari disk atau pagetable? Anda harus tahu ke mana harus melompat.
Atau dalam keadaan lain, sangat jarang seperti ketika Anda memiliki banyak ruang yang dialokasikan dan ingin menempatkan beberapa struktur di belakang satu sama lain. Mereka dapat dikemas dengan cara ini tanpa perlu offset () operator. Ada trik lain untuk itu juga.
Saya juga percaya beberapa implementasi STL menggunakan penempatan baru, seperti std :: vector. Mereka mengalokasikan ruang untuk 2 elemen seperti itu dan tidak perlu selalu realokasi.
sumber
Ini digunakan oleh
std::vector<>
karenastd::vector<>
biasanya mengalokasikan lebih banyak memori daripada yang adaobjects
divector<>
.sumber
Saya telah menggunakannya untuk menyimpan objek dengan file yang dipetakan memori.
Contoh spesifik adalah database gambar yang memproses banyak gambar besar (lebih dari yang bisa disimpan dalam memori).
sumber
Saya telah melihatnya digunakan sebagai sedikit peretasan kinerja untuk penunjuk "tipe dinamis" (di bagian "Di Balik Terpal"):
sumber
void*
membutuhkan 8 byte. Agak konyol menunjuk delapan bytevoid*
pada satu bytebool
. Tetapi sepenuhnya mungkin untuk benar-benar overlaybool
padavoid*
, seperti aunion { bool b; void* v }
. Anda perlu cara untuk mengetahui bahwa benda yang Anda panggilvoid*
sebenarnya adalahbool
(atau ashort
, atau afloat
, dll.). Artikel yang saya tautkan menjelaskan cara melakukannya. Dan, untuk menjawab pertanyaan awal, penempatannew
adalah fitur yang digunakan untuk membuatbool
(atau jenis lain) di mana avoid*
diharapkan, (gips digunakan untuk nanti mendapatkan / memodifikasi nilai).Saya menggunakannya untuk membuat objek berdasarkan memori yang berisi pesan yang diterima dari jaringan.
sumber
Umumnya, penempatan baru digunakan untuk menghilangkan biaya alokasi 'normal baru'.
Skenario lain di mana saya menggunakannya adalah tempat di mana saya ingin memiliki akses ke pointer ke objek yang masih akan dibangun, untuk mengimplementasikan per-dokumen tunggal.
sumber
Mungkin berguna saat menggunakan memori bersama, di antara penggunaan lainnya ... Misalnya: http://www.boost.org/doc/libs/1_51_0/doc/html/interprocess/synchronization_mechanisms.html#interprocess.synchronization_mechanisms.conditions. conditions_anonymous_example
sumber
Satu tempat saya bertemu adalah dalam wadah yang mengalokasikan buffer yang berdekatan dan kemudian mengisinya dengan benda-benda yang diperlukan. Seperti disebutkan, std :: vector mungkin melakukan ini, dan saya tahu beberapa versi MFC CArray dan / atau CList melakukan ini (karena di situlah saya pertama kali berlari melewatinya). Metode alokasi berlebih buffer adalah optimasi yang sangat berguna, dan penempatan yang baru cukup satu-satunya cara untuk membangun objek dalam skenario itu. Ini juga kadang-kadang digunakan untuk membangun objek di blok memori yang dialokasikan di luar kode langsung Anda.
Saya telah menggunakannya dalam kapasitas yang sama, meskipun tidak sering muncul. Ini adalah alat yang berguna untuk kotak alat C ++.
sumber
Mesin skrip dapat menggunakannya di antarmuka asli untuk mengalokasikan objek asli dari skrip. Lihat Angelscript (www.angelcode.com/angelscript) untuk contohnya.
sumber
Lihat file fp.h dalam proyek xll di http://xll.codeplex.com. Ini memecahkan masalah "kekejaman yang tidak beralasan dengan kompiler" untuk array yang suka membawa dimensi mereka dengan mereka.
sumber
Berikut ini adalah penggunaan pembunuh untuk konstruktor C ++ di tempat: menyelaraskan ke baris cache, serta kekuatan 2 batas lainnya. Berikut ini adalah algoritma penyelarasan pointer ultra-cepat untuk kekuatan 2 batas dengan 5 atau kurang instruksi satu siklus :
Sekarang bukankah itu hanya membuat senyum di wajah Anda (:-). Saya ♥♥♥ C ++ 1x
sumber