Di mana saya harus meletakkan fungsi yang tidak terkait dengan kelas?

47

Saya sedang mengerjakan proyek C ++ di mana saya memiliki banyak fungsi matematika yang awalnya saya tulis untuk digunakan sebagai bagian dari kelas. Karena saya telah menulis lebih banyak kode, saya menyadari saya membutuhkan fungsi matematika ini di mana-mana.

Di mana tempat terbaik untuk meletakkannya? Katakanlah saya punya ini:

class A{
    public:
        int math_function1(int);
        ...
}

Dan ketika saya menulis kelas lain, saya tidak bisa (atau setidaknya saya tidak tahu bagaimana) menggunakannya math_function1di kelas lain. Plus, saya menyadari beberapa fungsi ini tidak benar-benar terkait dengan kelas A. Mereka tampaknya berada di awal, tetapi sekarang saya bisa melihat bagaimana mereka hanya fungsi matematika.

Apa praktik yang baik dalam situasi ini? Saat ini saya telah menyalin-menempelkan mereka ke kelas-kelas baru, yang saya yakin adalah praktik terburuk.

kelapa
sumber
11
Sudahkah Anda belajar tentang statickata kunci?
S.Lott
31
Dalam C ++, fungsi bebas hampir selalu lebih disukai daripada fungsi anggota.
Pubby
4
Tidak ada aturan yang mengatakan bahwa semuanya harus ada di kelas. Paling tidak di C ++.
tdammers
2
Saya lebih suka namespace daripada kelas dengan banyak metode statis
Nick Keighley

Jawaban:

71

C ++ dapat memiliki fungsi non-metode dengan baik, jika mereka bukan milik kelas tidak menempatkan mereka di kelas, hanya menempatkan mereka di lingkup namespace global atau lainnya

namespace special_math_functions //optional
{
    int math_function1(int arg)
    {
         //definition 
    }
}
jk.
sumber
6
+1 Ini adalah solusi yang paling masuk akal, meskipun namespace tambahan sepertinya tidak diperlukan.
Pubby
1
tidak itu tidak perlu
jk.
27
Beberapa namespace berguna untuk mengurangi potensi tabrakan nama dengan perpustakaan lain.
Bill Door
11
Menggunakan namespace juga bagus karena tidak membedakan apakah panggilan itu metode atau fungsi. ( math_function1(42)mungkin memanggil anggota kelas saat ini; special_math_functions::math_function1(42)jelas memanggil fungsi independen). Yang sedang berkata, ::math_function(42)memberikan disambiguasi yang sama.
ipeet
2
Ruang nama tidak diperlukan, tetapi juga tidak dilarang. Karenanya mengapa jawaban ini mengatakan // optional. Bumbui sesuai selera.
user253751
6

Tergantung pada bagaimana proyek diatur dan apa pola desain yang Anda gunakan, dengan asumsi ini adalah kode utilitas Anda memiliki pilihan berikut:

  • Jika Anda tidak harus menggunakan objek untuk semua yang Anda bisa lakukan sesuatu yang sederhana seperti hanya menempatkan mereka semua dalam file tanpa pembungkus kelas di sekitar mereka. Ini bisa dengan atau tanpa namespace meskipun namespace direkomendasikan untuk mencegah masalah apa pun di masa depan.
  • Untuk C ++ yang dikelola, Anda dapat membuat kelas statis untuk menampung semuanya; Namun, ini tidak benar-benar berfungsi sama dengan kelas yang sebenarnya dan pemahaman saya adalah bahwa itu adalah anti-pola C ++.
  • Jika Anda tidak menggunakan C ++ terkelola maka Anda bisa menggunakan fungsi statis untuk memungkinkan Anda mengaksesnya dan semuanya terkandung dalam satu kelas. Ini mungkin berguna jika ada juga fungsi-fungsi lain yang Anda inginkan sebagai objek instantiated yang tepat oleh mungkin juga merupakan anti-pola.
  • Jika Anda ingin memastikan bahwa hanya satu instance objek yang berisi fungsi akan ada maka Anda dapat menggunakan Pola Singleton untuk kelas utilitas yang juga memungkinkan Anda beberapa fleksibilitas di masa depan karena Anda sekarang memiliki akses ke atribut non-statis. Ini akan menjadi penggunaan terbatas dan hanya benar-benar berlaku jika Anda memerlukan objek karena suatu alasan. Kemungkinannya adalah jika Anda melakukan ini, Anda sudah akan tahu mengapa.

Perhatikan bahwa opsi pertama akan menjadi taruhan terbaik dan ketiganya adalah kegunaan terbatas. Walaupun begitu, Anda mungkin akan bertemu hanya karena C # atau programmer Java melakukan beberapa pekerjaan C ++ atau jika Anda pernah bekerja pada C # atau kode Java di mana penggunaan kelas adalah wajib.

rjzii
sumber
Mengapa memilih?
rjzii
10
Saya bukan downvoter, tapi mungkin karena Anda menasihati kelas dengan fungsi statis atau singleton, sementara fungsi bebas mungkin akan baik-baik saja dalam hal ini (dan dapat diterima dan berguna untuk banyak hal di C ++).
Anton Golov
@AntonGolov - Fungsi gratis adalah hal pertama yang saya sebutkan dalam daftar. :) Sisanya adalah pendekatan yang lebih berorientasi OOP untuk situasi di mana Anda berurusan dengan "Semuanya harus kelas!" lingkungan.
rjzii
9
@Rob Z: Namun, C ++ bukan salah satu dari mereka "Semuanya harus kelas!" lingkungan.
David Thornley
1
Sejak kapan OOP memaksa fungsi murni ke dalam kelas? Sepertinya lebih seperti OOP-kargo-kultus.
Deduplicator
1

Seperti yang sudah Anda katakan, copy-paste kode adalah bentuk terburuk dari penggunaan kembali kode. Jika Anda memiliki fungsi yang bukan milik kelas Anda atau dapat digunakan untuk beberapa skenario, tempat terbaik untuk meletakkannya adalah kelas pembantu atau kelas utilitas. Jika mereka tidak menggunakan data instance apa pun, mereka bisa dibuat statis, jadi Anda tidak perlu membuat instance dari kelas utilitas untuk menggunakannya.

Lihat di sini untuk diskusi tentang fungsi anggota statis dalam C ++ asli dan di sini untuk kelas statis dalam C ++ terkelola. Anda kemudian dapat menggunakan kelas utilitas ini di mana pun Anda akan menempelkan kode Anda.

Dalam .NET misalnya, hal-hal seperti Min()dan Max()disediakan sebagai anggota statis di System.Mathkelas .

Jika semua fungsi Anda berhubungan dengan matematika dan Anda ingin orang lain memiliki Mathkelas raksasa , Anda mungkin ingin memecahnya lebih lanjut dan memiliki kelas seperti TrigonometryUtilities, EucledianGeometryUtilitiesdan sebagainya.

Pilihan lain adalah menempatkan fungsionalitas bersama ke kelas dasar dari kelas-kelas yang membutuhkan fungsionalitas tersebut. Ini bekerja dengan baik, ketika fungsi-fungsi dalam pertanyaan perlu beroperasi pada data instan, namun, pendekatan ini juga kurang fleksibel jika Anda ingin menghindari pewarisan berganda dan hanya menempel pada satu kelas dasar, karena Anda akan "menggunakan" basis satu Anda kelas hanya untuk mendapatkan akses ke beberapa fungsi bersama.

PersonalNexus
sumber
18
IMHO, kelas utilitas dengan apa pun kecuali anggota statis adalah anti-pola dalam C ++. Anda menggunakan kelas untuk mereproduksi dengan sempurna perilaku namespace, yang benar-benar tidak masuk akal.
ipeet
+1 untuk menyebutkan kelas utilitas. Bahasa seperti C # mengharuskan semuanya berada di kelas sehingga cukup umum untuk membuat sejumlah kelas utilitas untuk berbagai keperluan. Menerapkan kelas-kelas ini sebagai Statis membuat utilitas lebih ramah pengguna dan menghindari sakit kepala yang kadang-kadang dapat diwariskan, terutama ketika kelas dasar menjadi membengkak dengan kode yang hanya dapat digunakan oleh satu atau dua keturunan. Teknik serupa dapat diterapkan dalam bahasa lain untuk memberikan konteks yang bermakna bagi fungsi utilitas Anda, alih-alih membiarkannya mengambang di lingkup global.
S.Robins
5
@ S.Robins: Tidak perlu untuk hal seperti itu di C ++, Anda bisa meletakkannya di namespace, yang memiliki efek yang sama persis.
DeadMG
0

Tidak jelas istilah "Fungsi pembantu". Satu definisi adalah fungsi kenyamanan yang Anda gunakan sepanjang waktu hanya untuk menyelesaikan pekerjaan. Mereka dapat hidup di namespace utama dan memiliki header sendiri, dll. Definisi fungsi helper lainnya adalah fungsi utilitas untuk satu kelas atau keluarga kelas.

// a general helper 
template <class T>
bool isPrinter(T& p){
   return (dynamic_cast<Printer>(p))? true: false;
}

    // specific helper for printers
namespace printer_utils {    
  namespace HP {
     print_alignment_page() { printAlignPage();}
  }

  namespace Xerox {
     print_alignment_page() { Alignment_Page_Print();}
  }

  namespace Canon {
     print_alignment_page() { AlignPage();}
  }

   namespace Kyocera {
     print_alignment_page() { Align(137,4);}
   }

   namespace Panasonic {
      print_alignment_page() { exec(0xFF03); }
   }
} //namespace

Sekarang isPrintertersedia untuk semua kode termasuk tajuknya, tetapi print_alignment_pagememerlukan using namespace printer_utils::Xerox;arahan. Orang juga dapat merujuknya sebagai

Canon::print_alignment_page();

agar lebih jelas.

C ++ STL memiliki std::namespace yang mencakup hampir semua kelas dan fungsinya, tetapi memecahnya secara kategorikal menjadi lebih dari 17 header yang berbeda untuk memungkinkan pembuat kode mendapatkan nama kelas, nama fungsi, dll. Jika mereka ingin menulis mereka sendiri.

Bahkan, TIDAK dianjurkan untuk menggunakan using namespace std;file header atau, seperti yang sering dilakukan, sebagai baris pertama di dalam main(). std::adalah 5 huruf dan sering tampaknya menjadi tugas untuk mengawali fungsi yang ingin digunakan (terutama std::coutdan std::endl!) tetapi memang memiliki tujuan.

C ++ 11 baru memiliki beberapa sub-namespaces di dalamnya untuk layanan khusus seperti

std::placeholders,
std::string_literals,
std::chrono,
std::this_thread,
std::regex_constants

yang bisa dibawa untuk digunakan.

Teknik yang bermanfaat adalah komposisi namespace . Satu mendefinisikan ruang nama kustom untuk menahan ruang nama yang Anda butuhkan untuk .cppfile khusus Anda dan menggunakannya sebagai ganti sekelompok usingpernyataan untuk setiap hal dalam ruang nama yang mungkin Anda butuhkan.

#include <iostream>
#include <string>
#include <vector>

namespace Needed {
  using std::vector;
  using std::string;
  using std::cout;
  using std::endl;
}

int main(int argc, char* argv[])
{
  /*  using namespace std; */
      // would avoid all these individual using clauses,
      // but this way only these are included in the global
      // namespace.

 using namespace Needed;  // pulls in the composition

 vector<string> str_vec;

 string s("Now I have the namespace(s) I need,");

 string t("But not the ones I don't.");

 str_vec.push_back(s);
 str_vec.push_back(t);

 cout << s << "\n" << t << endl;
 // ...

Teknik ini membatasi pemaparan pada keseluruhan std:: namespace( ini besar! ) Dan memungkinkan seseorang untuk menulis kode pembersih untuk baris kode paling umum yang paling sering ditulis orang.

Chris Reid
sumber
-2

Anda mungkin ingin meletakkannya di fungsi templat untuk membuatnya tersedia untuk berbagai jenis bilangan bulat dan / atau mengapung:

template <typename T>
T math_function1(T){
 ..
}

Anda dapat juga membuat jenis kustom yang rapi yang mewakili misalnya angka besar atau angka kompleks dengan membebani operator yang relevan untuk jenis kustom Anda untuk membuatnya ramah terhadap template.

pengguna1703394
sumber