Apakah ada cara standar untuk menunjukkan bahwa suatu fungsi mengembalikan pointer baru?

8

Kadang-kadang saya ingin mendelegasikan konstruksi objek yang dimiliki kelas ke fungsi terpisah. Sesuatu seperti

Vertex* new_vertex(const Options& options) {
  // do stuff...
  return new Vertex(...);
}

di mana fungsi ini hanya dimaksudkan untuk digunakan dalam kelas yang memiliki Vertex. Jelas fungsi ini dapat menyebabkan kebingungan kehabisan memori, jadi saya ingin membuatnya sejelas mungkin. Apakah ada konvensi penamaan untuk fungsi-fungsi seperti itu?

Shep
sumber
11
Iya. // TODO: Fix allocation of raw pointer.
Gort the Robot
3
@ SevenBurnap Saya tidak akan menggunakan kata "fix" kecuali masalahnya tidak berhasil.
user253751
1
Hanya komentar tentang kekhawatiran "kebingungan". Adalah sama pentingnya bahwa penelepon merawat benda yang dikembalikan dengan benar. Pointer pintar dapat menjadikan hal ini mudah (dengan menjadikan manajemen memori sebagai kebiasaan mekanis daripada usaha mental yang terus-menerus), tetapi jika penelepon tidak memiliki kebijakan atau standar pengkodean yang jelas atau gagasan tentang "kepemilikan objek" secara hierarkis, pada akhirnya ia akan masih mengalami masalah. Dalam beberapa kasus, programmer junior bahkan mungkin mencoba untuk mengelak unique_ptrdengan memanggil release()fungsinya, dan menggunakan pointer mentah seperti cara lama.
rwong
1
@ SvenvenBurnap Mengapa tidak sederhana // FIXME: Allocation of raw pointer?
Tobias Kienzler
1
Fungsi Anda membuatnya cukup jelas bagi saya. Tipe kembalinya bukan berdasarkan nilai atau referensi - yang mana salah satunya akan mencoba menjernihkan memori sebagai perselisihan sintaksis yang tidak naluriah. Fungsi ini disebut new_vertexjadi saya tahu objek baru dicetak. Anda bisa menyebutnya Create_new_vertexlebih jelas. Adapun gagasan bahwa Anda tidak harus mengelola memori tumpukan tanpa petunjuk pintar, tidak pernah melihat kebenaran dalam hal itu - bahkan jika Anda tidak dapat mengelola memori tumpukan tanpa mereka, Anda juga tidak punya bisnis mengelola memori tumpukan dengan mereka!
Grimm The Opiner

Jawaban:

21

Kembalikan unique_ptr:

std::unique_ptr<Vertex> new_vertex(const Options& options) {
  // do stuff...
  return std::make_unique<Vertex>(...);
}

Hanya ada satu yang unique_ptrmenunjuk ke objek tertentu (kecuali Anda menyalahgunakannya dengan melemparkan ke Vertex*dan kembali, lagi pula). Anda tidak dapat menyalin unique_ptr, hanya memindahkannya. Ketika a unique_ptrdihancurkan (dan belum dipindahkan dari) itu bahkan deleteobjek untuk Anda.

make_uniquemembuat yang baru Vertexdan membungkusnya dalam unique_ptr; argumen yang Anda berikan make_uniqueadalah argumen yang diteruskan ke konstruktor.

pengguna253751
sumber
4
Ini membantu, tetapi itu tidak benar - benar menjawab pertanyaan saya. Saya tahu tentang pointer pintar, saya hanya ingin tahu apakah ada konvensi penamaan untuk fungsi yang mengembalikan pointer mentah yang harus dijaga pengguna. Saya kira jawabannya adalah "tidak, tidak ada, karena Anda tidak boleh melakukan itu"?
Shep
3
@Shep Jika Anda ingin menunjukkan kepada orang yang memanggil fungsi Anda bahwa itu menciptakan objek baru, unique_ptrlakukan itu. Jika itu harus menjadi nama karena alasan tertentu, maka saya rasa tidak, tapi mengapa itu harus nama?
user253751
8
@Shep Nama adalah hal yang tidak berwujud dan semua orang bebas untuk mengabaikannya sama sekali. Jenis di sisi lain ditegakkan oleh kompiler, yang berarti membutuhkan upaya yang signifikan untuk memecahkan barang. Jangan pernah mengirim nama untuk melakukan pekerjaan tipe.
ComicSansMS
1
@Shep tidak ada salahnya mengembalikan pointer mentah dalam kasus ini . Kasus ini adalah bahwa Anda telah membuat objek yang terserah penelepon untuk mengelola masa hidup. Penelepon selalu dapat menjatuhkannya langsung ke pointer pintar jika mereka pikir mereka perlu.
Grimm The Opiner
1
@immibis: Kenapa kamu menimpa templat-argumen-deduksi std::make_unique? Itu verbose yang tidak perlu dan rawan kesalahan ...
Deduplicator
2

Apakah ada cara standar untuk menunjukkan bahwa suatu fungsi mengembalikan pointer baru?

Tidak, tidak ada "cara standar" (tetapi ada kebijakan desain API yang saat ini dianggap sebagai "praktik terbaik").

Karena ambiguitas ini ("apa fungsi yang ingin saya lakukan oleh pointer mengembalikan?"), Saat ini dianggap praktik terbaik untuk memaksakan kebijakan masa pakai dan kepemilikan, melalui jenis pengembalian:

<vertex pointer type> new_vertex(const Options& options);

<vertex pointer type>dapat std::unique_ptr("new_vertex tidak memiliki pointer"), atau std::shared_ptr("kode klien tidak memiliki pointer"), atau sesuatu yang lain, yang memiliki semantik kepemilikan yang jelas (misalnya, Vertex const * constakan menunjukkan ke kode klien "baca alamat dan nilai, tetapi jangan ubah / jangan hapus pointer ").

Secara umum, Anda tidak boleh mengembalikan pointer mentah (tetapi dalam beberapa kasus, "kepraktisan mengalahkan kemurnian").

TLDR : ada praktik terbaik ( ya ), tetapi bukan cara standar (dalam bahasa).

Edit :

di mana fungsi ini hanya dimaksudkan untuk digunakan dalam kelas yang memiliki Vertex

Jika kelas memiliki Vertex, saya akan menulis seperti ini:

class SomeClass // owns the vertex
{
    void do_stuff() // uses the vertex internally
    {
        init_vertex(); // see below
        assert(vertex.get());
        // use vertex as needed
    }
private:
    // "class owns the Vertex"
    std::unique_ptr<Vertex> vertex;

    // sets the internal vertex
    // function doesn't return a pointer of any kind
    void init_vertex(const Options& options); // or "reset_", "refresh_", 
                                              // or "make_" vertex,
                                              // if the pointer can change
                                              // throughout the lifetime
                                              // of a SomeClass instance
};
utnapistim
sumber
0

Ya, ada puluhan "standar" tentang ini

XKCD

Rekomendasi jawaban unique_ptrmungkin adalah jawaban terbaik, tetapi jika Anda melihat petunjuk mentah, semua orang mengembangkan standar mereka sendiri.

Secara umum fungsi bernama create_____atau new______atau alloc_____cenderung menyiratkan objek baru.

Namun

Pertimbangkan bahwa Anda mencampuradukkan perilaku. Anda sebenarnya tidak peduli apakah ini adalah contoh baru dari suatu objek atau tidak. Yang Anda pedulikan adalah apakah penelepon bertanggung jawab untuk menghancurkan objek atau tidak. Ada banyak API yang menyerahkan tanggung jawab untuk objek yang ada, dan fungsi-fungsi itu akan membutuhkan skema penamaan yang cocok. Mungkin give______.

Jika Anda bersedia untuk menjauh dari C ++ ...
Jika Anda ingin menganggap Objective-C cukup mirip dengan C ++ untuk menjadi sumber jawaban, sebenarnya ada jawaban nyata dalam bahasa itu. Objective-C mengelola umur objek menggunakan jumlah referensi. Mereka membutuhkan cara untuk menggambarkan apakah Anda diberi tanggung jawab untuk suatu objek, atau hanya referensi ke objek yang ada. Jika Anda salah menjawab, Anda salah menghitung ulang dan kehilangan objek. Oleh karena itu mereka membuat aturan: setiap objek yang Anda kembalikan dimiliki oleh orang lain (jadi Anda harus menambah dan mengurangi sendiri penghitungan ref), kecuali untuk panggilan penciptaan yang melewati penunjuk yang sudah bertambah untuk Anda (Anda hanya perlu mengurangi). Panggilan-panggilan ini diidentifikasi oleh nama metode. Metode dimulai dengan alloc new copyataumutableCopyselalu mengembalikan objek baru. Jadi di sana, standar ditegakkan oleh bahasa.

Cort Ammon
sumber