Bir sınıfla ilgili olmayan işlevleri nereye koymalıyım?


47

Başlangıçta bir sınıfın parçası olarak kullanmak için yazdığım birçok matematik fonksiyonuna sahip olduğum bir C ++ projesi üzerinde çalışıyorum. Daha fazla kod yazdığım için her yerde bu matematiksel fonksiyonlara ihtiyacım olduğunu anladım.

Onları koymak için en iyi yer neresidir? Diyelim ki bu var:

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

Ve başka bir sınıf yazdığımda, diğer sınıfta bunu kullanamam (ya da en azından nasıl kullanacağımı bilmiyorum) math_function1. Artı, bu fonksiyonlardan bazılarının gerçekten A sınıfı ile ilişkili olmadığını farkettim. Başlangıçta görünüyorlardı, ama şimdi onların sadece matematik fonksiyonlarının nasıl olduğunu görebiliyorum.

Bu durumda iyi uygulama nedir? Şu anda onları kopyalayıp yeni sınıflara yapıyorum, ki en kötüsüydü.


11
staticAnahtar kelimeyi öğrendiniz mi?
S.Lott

30
C ++ 'da, serbest işlevler neredeyse her zaman üye işlevlere göre tercih edilir.
Pubby

4
Her şeyin bir sınıfta olması gerektiğini söyleyen hiçbir kural yoktur. En azından C ++ 'da değil.
tdammers

2
Bir grup statik yöntemi olan bir sınıfa bir isim alanı tercih ederdim
Nick Keighley

Yanıtlar:


70

C ++ metot dışı fonksiyonlara sahip olabilir, eğer bir sınıfa ait değilse, onları bir sınıfa koymazlar, sadece global veya başka bir isim alanı kapsamına sokarlar.

namespace special_math_functions //optional
{
    int math_function1(int arg)
    {
         //definition 
    }
}

6
+1 Ekstra ad alanı gerekli görünmese de, bu en mantıklı çözüm.
Pubby

1
hayır gerekli değil
jk.

27
Bazı ad alanları, diğer kitaplıklardaki olası ad çakışmalarını azaltmak için kullanışlıdır.
Bill Door,

11
Bir ad boşluğu kullanmak da güzeldir çünkü aramanın bir yöntem mi yoksa işlev mi olduğunu belirsizleştirir. ( math_function1(42)geçerli sınıfın bir üyesini çağırabilir; special_math_functions::math_function1(42)açıkça bağımsız bir işlevi çağırıyor). Varlık dedi ki, ::math_function(42)aynı anlam ayrımı sağlar.
ipeet

2
İsim alanları gerekli değildir ancak yasak değildir. Dolayısıyla bu cevap neden diyor // optional. Zevke göre mevsim.
kullanıcı253751

6

Projenin nasıl organize edildiğine ve ne tür tasarım desenleri kullandığınıza bağlı olarak, bunun kesinlikle yardımcı kod olduğunu kabul ederek aşağıdaki seçeneklere sahip olduğunuzu varsayalım:

  • Her şey için nesneler kullanmak zorunda kalmazsanız, basitçe, etraflarında bir sınıf sarmalayıcısı olmayan bir dosyaya koymak gibi basit bir şey yapabilirsiniz. Bu, bir ad alanı olan veya olmayan olabilir, ancak ad alanı gelecekte oluşabilecek sorunları önlemek için önerilir.
  • Yönetilen C ++ için hepsini içerecek statik bir sınıf oluşturabilirsiniz ; Ancak, bu gerçekten gerçek bir sınıfla aynı şekilde çalışmamaktadır ve benim anladığım, bunun bir C ++ anti-kalıp olduğudur.
  • Eğer yönetilen C ++ kullanmıyorsanız, bunlara erişmenize ve hepsini tek bir sınıfta bulundurmanıza izin vermek için sadece statik fonksiyonları kullanabilirsiniz . Bu, aynı zamanda bir anti-patern için uygun bir başlatılmış nesneyi isteyebileceğiniz başka fonksiyonlar da varsa faydalı olabilir.
  • İşlevleri içeren nesnenin yalnızca bir örneğinin varolmasını sağlamak istiyorsanız, artık statik olmayan niteliklere erişebildiğiniz için gelecekte size biraz esneklik sağlayan bir hizmet sınıfı için Singleton Pattern kullanabilirsiniz . Bu sınırlı kullanımlı olacak ve sadece bir sebepten dolayı bir nesneye ihtiyaç duyuyorsanız gerçekten geçerlidir. Muhtemelen bunu yaparsanız nedenini zaten bileceksiniz.

İlk seçeneğin en iyi bahis olacağını ve aşağıdaki üçünün sınırlı bir faydası olacağını unutmayın. Bununla birlikte, bazı C ++ işleri yapan C # veya Java programcıları nedeniyle veya sınıfların kullanımı zorunlu olan C # veya Java kodu üzerinde çalışıyorsanız bununla karşılaşabileceğinizi söyledi.


Neden aşağı oy?
rjzii

10
Ben indirici değilim, ama muhtemelen statik işlevler veya bir singleton olan bir sınıfa tavsiyede bulunuyorsanız, serbest işlevler bu durumda iyi olur (ve C ++ 'da birçok şey için kabul edilebilir ve kullanışlıdır).
Anton Golov

@AntonGolov - Listede belirttiğim ilk şey ücretsiz fonksiyonlar. :) Geri kalanlar, "Her şey bir sınıf olmalı!" İle uğraştığınız durumlar için OOP odaklı yaklaşımlardır. ortamları.
rjzii

9
@Rob Z: Ancak, C ++ bunlardan biri değil "Her şey bir sınıf olmalı!" ortamları.
David Thornley

1
Ne zamandan beri saf işlevleri bir sınıfa zorlamak OOP? OOP-kargo-kült gibi görünüyor.
Deduplicator

1

Söylediğiniz gibi, kodu kopyalayıp yapıştırmak, kod kullanımının en kötü şeklidir. Herhangi bir sınıfınıza ait olmayan veya birkaç senaryoda kullanılabilecek fonksiyonlarınız varsa, bunları koymak için en iyi yer bir yardımcı veya yardımcı sınıf olacaktır. Örnek veri kullanmazlarsa, statik hale getirilebilirler, bu nedenle kullanmak için yardımcı sınıfın bir örneğini oluşturmanıza gerek yoktur.

Bkz burada yerel C ++ statik üye fonksiyonları bir tartışma için ve burada ++ yönetilen C statik sınıflar için. Daha sonra bu yardımcı program sınıfını kodunuzu yapıştırdığınız her yerde kullanabilirsiniz.

Örneğin, .NET'te , sınıfta statik üyeler gibi Min()ve Max()bunlar gibi şeyler sağlanır .System.Math

Tüm fonksiyonları matematik ile ilgili olan ve bir dev var otherwiese istiyorsanız Mathsınıfı, sen dökümünü ve benzeri sınıfları isteyebilirsiniz TrigonometryUtilities, EucledianGeometryUtilitiesvb.

Başka bir seçenek, paylaşılan işlevselliği, bahsedilen işlevselliği gerektiren sınıfların bir temel sınıfına koymak olacaktır. Bu, iyi çalışır, sorulardaki işlevlerin örnek verilerde çalışması gerektiğinde, birden fazla kalıtımdan kaçınmak ve yalnızca bir temel sınıfa yapışmak istiyorsanız, bir üssünüzü "kullanacağınız" için bu yaklaşım daha az esnektir; Bazı paylaşılan işlevlere erişmek için yalnızca sınıf.


18
IMHO, statik üyeler dışında hiçbir faydası olmayan sınıflar C ++ 'da anti-paterndir. Gerçekten bir anlam ifade etmeyen bir ad alanının davranışını kusursuz bir şekilde üretmek için bir sınıf kullanıyorsunuz.
ipeet

Yardımcı sınıflardan bahsetmek için +1. C # gibi diller her şeyin bir sınıfta olmasını gerektirir, bu yüzden çeşitli amaçlar için bir dizi yardımcı sınıf oluşturmak oldukça yaygındır. Bu sınıfları Statik olarak uygulamak, yardımcı programları daha kullanıcı dostu hale getirir ve özellikle temel sınıflar yalnızca bir ya da iki torun tarafından kullanılabilecek kodla şişirildiği zaman, kalıtımın yaratabileceği sıkıntılardan kaçınır. Diğer tekniklerde, yardımcı programınız için küresel kapsamda kayan bırakmak yerine anlamlı bir bağlam sağlamak için benzer teknikler uygulanabilir.
S.Robins

5
@ S.Robins: C ++ 'ta böyle bir şeye gerek yok, onları tam aynı etkiye sahip olan bir isim alanına koyabilirsiniz.
DeadMG

0

"Yardımcı fonksiyon" terimini açıklamayın. Bir tanım, sadece bir işin yapılması için her zaman kullandığınız bir kolaylık fonksiyonudur. Bunlar ana isim alanında yaşayabilir ve kendi başlıkları, vb. Olabilir. Diğer yardımcı fonksiyon tanımı, tek bir sınıf veya sınıf ailesi için bir yardımcı fonksiyondur.

// 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

Şimdi isPrinterbaşlığı da dahil olmak üzere herhangi bir kod için kullanılabilir, ancak print_alignment_pagebir using namespace printer_utils::Xerox;yönerge gerektirir . Biri olarak da referans olabilir

Canon::print_alignment_page();

daha net olmak için.

C ++ STL, std::sınıflarının ve işlevlerinin neredeyse tamamını kapsayan bir ad alanına sahiptir, ancak kodlayıcıya, sınıf adlarını, işlev adlarını vb. onların kendi.

Aslında, using namespace std;bir başlık dosyasında ya da sıklıkla yapıldığı gibi içerideki ilk satır olarak kullanılması önerilmez main(). std::5 harftir ve genellikle kullanmak istediği işlevi (özellikle std::coutve std::endl!) önyükleme görevi görür ancak bir amaca hizmet eder

Yeni C ++ 11, bunun gibi özel servisler için bazı alt ad alanlarına sahiptir.

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

kullanım için getirilebilir.

Yararlı bir teknik, isim alanı kompozisyonudur . Biri, özel .cppdosyanız için gereken ad alanlarını tutmak için özel bir ad alanı tanımlar ve usingihtiyaç duyabileceğiniz bir ad alanındaki her şey için bir sürü ifade yerine onu kullanır .

#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;
 // ...

Bu teknik, bütüne maruz kalmayı sınırlandırır std:: namespace( büyüktür ) ve kişinin en sık yazdığı en yaygın kod satırları için daha temiz kod yazmasına izin verir.


-2

Farklı tam sayılar ve / veya değişkenler için kullanılabilir kılmak üzere bir şablon işlevine koymak isteyebilirsiniz:

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

Ayrıca, şablon tipinizi kolaylaştırmak için özel tipiniz için ilgili operatörleri aşırı yükleyerek, örneğin büyük sayıları veya karmaşık sayıları temsil eden temiz özel türler oluşturabilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.