ฉันควรใส่ฟังก์ชั่นที่ไม่เกี่ยวข้องกับคลาสได้ที่ไหน


47

ฉันกำลังทำงานในโครงการ C ++ ที่ฉันมีฟังก์ชั่นทางคณิตศาสตร์มากมายซึ่งตอนแรกฉันเขียนเพื่อใช้เป็นส่วนหนึ่งของชั้นเรียน ถึงแม้ว่าฉันจะเขียนโค้ดเพิ่มเติม แต่ฉันก็ตระหนักว่าฉันต้องการฟังก์ชันคณิตศาสตร์เหล่านี้ทุกที่

สถานที่ที่ดีที่สุดที่จะนำพวกเขาอยู่ที่ไหน สมมติว่าฉันมีสิ่งนี้:

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

และเมื่อฉันเขียนชั้นเรียนอื่นฉันไม่สามารถ (หรืออย่างน้อยฉันไม่รู้วิธี) ที่ใช้math_function1ในชั้นเรียนอื่นนั้น นอกจากนี้ฉันได้ตระหนักถึงฟังก์ชั่นบางอย่างที่ไม่เกี่ยวข้องกับคลาส A พวกเขาดูเหมือนจะเป็นจุดเริ่มต้น แต่ตอนนี้ฉันสามารถเห็นได้ว่าพวกเขาเป็นเพียงฟังก์ชันทางคณิตศาสตร์

การปฏิบัติที่ดีในสถานการณ์นี้คืออะไร? ตอนนี้ฉันได้คัดลอกพวกเขาไปสู่คลาสใหม่ซึ่งฉันแน่ใจว่าเป็นการปฏิบัติที่แย่ที่สุด


11
คุณเรียนรู้เกี่ยวกับstaticคำหลักแล้วหรือยัง
S.Lott

30
ใน C ++ ฟังก์ชั่นฟรีมักจะต้องการฟังก์ชั่นสมาชิกมากกว่า
Pubby

4
ไม่มีกฎที่บอกว่าทุกอย่างต้องอยู่ในชั้นเรียน อย่างน้อยไม่ได้อยู่ใน C ++
tdammers

2
ฉันต้องการเนมสเปซให้กับชั้นเรียนด้วยวิธีการแบบคงที่
Nick Keighley

คำตอบ:


70

C ++ สามารถมีฟังก์ชั่นที่ไม่ใช่วิธีได้ดีถ้าพวกเขาไม่ได้อยู่ในชั้นเรียนไม่ได้ใส่ไว้ในชั้นเรียนเพียงแค่วางพวกเขาในระดับโลกหรือขอบเขต namespace อื่น ๆ

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

6
+1 นี่เป็นทางออกที่สมเหตุสมผลที่สุดแม้ว่าจะไม่จำเป็นต้องใช้เนมสเปซพิเศษ
Pubby

1
ไม่จำเป็นเลย
jk

27
บางเนมสเปซมีประโยชน์ในการลดความขัดแย้งของชื่อที่อาจเกิดขึ้นกับไลบรารีอื่น ๆ
Bill Door

11
การใช้เนมสเปซก็ดีเช่นกันเพราะมันทำให้เกิดความสับสนว่าการโทรเป็นวิธีการหรือฟังก์ชั่น ( math_function1(42)อาจเรียกสมาชิกของคลาสปัจจุบันspecial_math_functions::math_function1(42)กำลังเรียกฟังก์ชันอิสระอย่างชัดเจน) ที่ถูกกล่าวว่า::math_function(42)ให้ disambiguation เดียวกัน
ipeet

2
ไม่จำเป็นต้องใช้เนมสเปซ แต่อย่างใด // optionalดังนั้นทำไมคำตอบนี้กล่าวว่า ปรุงรสตามชอบ
user253751

6

ขึ้นอยู่กับวิธีการจัดระเบียบโครงการและรูปแบบการออกแบบที่คุณใช้สมมติว่านี่คือรหัสยูทิลิตี้อย่างเคร่งครัดคุณมีตัวเลือกดังต่อไปนี้:

  • หากคุณไม่จำเป็นต้องใช้วัตถุสำหรับทุกสิ่งคุณสามารถทำสิ่งที่ง่าย ๆ เช่นใส่พวกเขาทั้งหมดลงในไฟล์โดยไม่ต้องใช้ตัวล้อมรอบคลาส อาจมีหรือไม่มีเนมสเปซแม้ว่าแนะนำให้ใช้เนมสเปซเพื่อป้องกันปัญหาใด ๆ ในอนาคต
  • สำหรับ C ++ ที่มีการจัดการคุณสามารถสร้างคลาสแบบสแตติกเพื่อให้มีทั้งหมด อย่างไรก็ตามนี่ไม่ได้ผลเหมือนกับคลาสจริงและความเข้าใจของฉันคือมันเป็นรูปแบบต่อต้าน C ++
  • หากคุณไม่ได้ใช้ C ++ ที่มีการจัดการคุณสามารถใช้ฟังก์ชั่นคงที่เพื่อให้คุณสามารถเข้าถึงพวกเขาและให้พวกมันทั้งหมดมีอยู่ในคลาสเดียว สิ่งนี้อาจมีประโยชน์หากมีฟังก์ชั่นอื่น ๆ ที่คุณต้องการให้วัตถุอินสแตนซ์ที่เหมาะสมโดยอาจเป็นรูปแบบการต่อต้าน
  • หากคุณต้องการให้แน่ใจว่ามีเพียงอินสแตนซ์เดียวของวัตถุที่มีฟังก์ชั่นจะมีอยู่คุณสามารถใช้รูปแบบ Singletonสำหรับคลาสยูทิลิตี้ซึ่งช่วยให้คุณมีความยืดหยุ่นในอนาคตในขณะที่คุณสามารถเข้าถึงคุณลักษณะที่ไม่คงที่ สิ่งนี้จะเป็นการใช้งานที่ จำกัด และมีผลบังคับใช้จริงๆหากคุณต้องการวัตถุด้วยเหตุผลบางอย่าง ราคาต่อรองคือถ้าคุณทำเช่นนี้คุณจะรู้ว่าทำไม

โปรดทราบว่าตัวเลือกแรกจะเป็นทางออกที่ดีที่สุดและสามข้อต่อไปนี้มีประโยชน์ จำกัด ที่กล่าวว่าแม้ว่าคุณอาจพบแล้วเพียงเพราะ C # หรือ Java โปรแกรมเมอร์ทำงาน C ++ บางอย่างหรือถ้าคุณเคยทำงานกับ C # หรือรหัส Java ที่ใช้การเรียนเป็นสิ่งจำเป็น


ทำไมต้องโหวต
rjzii

10
ฉันไม่ใช่ผู้ลงคะแนน แต่อาจเป็นเพราะคุณกำลังแนะนำคลาสที่มีฟังก์ชั่นสแตติกหรือซิงเกิลในขณะที่ฟังก์ชั่นฟรีอาจจะใช้ได้ในกรณีนี้ (และเป็นที่ยอมรับและมีประโยชน์สำหรับหลาย ๆ สิ่งใน C ++)
Anton Golov

@AntonGolov - ฟังก์ชั่นฟรีเป็นสิ่งแรกที่ฉันพูดถึงในรายการ :) ส่วนที่เหลือเป็นแนวทางที่มุ่งเน้น OOP มากขึ้นสำหรับสถานการณ์ที่คุณกำลังเผชิญกับ "ทุกอย่างต้องเป็นคลาส!" สภาพแวดล้อม
rjzii

9
@Rob Z: อย่างไรก็ตาม C ++ ไม่ใช่หนึ่งในนั้น "ทุกอย่างต้องเป็นคลาส!" สภาพแวดล้อม
David Thornley

1
OOP จะบังคับให้ฟังก์ชั่นบริสุทธิ์เข้ามาในชั้นเรียนตั้งแต่เมื่อใด ดูเหมือนว่ามากขึ้นเช่น OOP-cargo-ลัทธิ
Deduplicator

1

ดังที่คุณได้กล่าวไปแล้วการคัดลอกโค้ดเป็นรูปแบบที่เลวร้ายที่สุดของการใช้รหัสซ้ำ หากคุณมีฟังก์ชั่นที่ไม่ได้อยู่ในคลาสใด ๆ ของคุณหรืออาจถูกใช้ในหลาย ๆ สถานการณ์สถานที่ที่ดีที่สุดในการทำให้เป็นผู้ช่วยหรือคลาสยูทิลิตี้ หากพวกเขาไม่ได้ใช้ข้อมูลอินสแตนซ์ใด ๆ พวกเขาสามารถสร้างแบบคงที่ดังนั้นคุณไม่จำเป็นต้องสร้างอินสแตนซ์ของคลาสยูทิลิตี้เพื่อใช้งาน

ดูที่นี่สำหรับการสนทนาของฟังก์ชันสมาชิกแบบคงที่ในภาษา C ++ และที่นี่สำหรับคลาสแบบคงที่ในภาษา C ++ ที่มีการจัดการ จากนั้นคุณสามารถใช้คลาสยูทิลิตี้นี้ทุกที่ที่คุณจะวางรหัสของคุณ

ใน .NET เช่นสิ่งที่ชอบMin()และMax()จะให้เป็นสมาชิกแบบคงที่ในระดับSystem.Math

หากฟังก์ชั่นของคุณทั้งหมดจะเกี่ยวข้องกับคณิตศาสตร์และคุณจะ otherwiese มีขนาดใหญ่Mathระดับคุณอาจต้องการที่จะทำลายมันลงไปและมีการเรียนเช่นTrigonometryUtilities, EucledianGeometryUtilitiesและอื่น ๆ

อีกทางเลือกหนึ่งคือการใส่ฟังก์ชั่นที่ใช้ร่วมกันไว้ในคลาสฐานของคลาสที่ต้องการฟังก์ชันดังกล่าว วิธีนี้ใช้งานได้ดีเมื่อฟังก์ชันที่มีคำถามจำเป็นต้องดำเนินการกับข้อมูลอินสแตนซ์อย่างไรก็ตามวิธีการนี้ยังมีความยืดหยุ่นน้อยลงหากคุณต้องการหลีกเลี่ยงการรับมรดกหลายรายการและยึดติดกับคลาสฐานเดียวเท่านั้นเพราะคุณจะ ชั้นเรียนเพียงเพื่อให้สามารถเข้าถึงฟังก์ชั่นที่ใช้ร่วมกัน


18
IMHO คลาสยูทิลิตี้ที่ไม่มีอะไรนอกจากสมาชิกแบบสแตติกคือรูปแบบการต่อต้านใน C ++ คุณกำลังใช้คลาสเพื่อสร้างพฤติกรรมของเนมสเปซอย่างสมบูรณ์แบบซึ่งไม่สมเหตุสมผล
ipeet

+1 สำหรับการกล่าวถึงคลาสยูทิลิตี้ ภาษาอย่าง C # ต้องการทุกสิ่งในคลาสดังนั้นจึงเป็นเรื่องปกติที่จะสร้างคลาสยูทิลิตี้จำนวนมากเพื่อจุดประสงค์ที่หลากหลาย การใช้คลาสเหล่านี้เป็นแบบคงที่ทำให้ยูทิลิตี้เป็นมิตรกับผู้ใช้มากขึ้นและหลีกเลี่ยงอาการปวดหัวที่การสืบทอดสามารถสร้างได้ในบางครั้งโดยเฉพาะอย่างยิ่งเมื่อคลาสพื้นฐานเริ่มบวมด้วยรหัสที่อาจใช้โดยผู้สืบทอดหนึ่งหรือสองคนเท่านั้น เทคนิคที่คล้ายกันสามารถนำไปใช้ในภาษาอื่น ๆ เพื่อให้บริบทที่มีความหมายสำหรับฟังก์ชั่นยูทิลิตี้ของคุณแทนที่จะปล่อยให้มันลอยอยู่ในขอบเขตทั่วโลก
S.Robins

5
@ S.Robins: ไม่จำเป็นต้องมีอะไรแบบนั้นใน C ++ คุณสามารถใส่ไว้ในเนมสเปซซึ่งมีผลเหมือนกันทุกประการ
DeadMG

0

ยกเลิกการตีความคำว่า "ฟังก์ชันผู้ช่วย" คำจำกัดความเดียวคือฟังก์ชั่นอำนวยความสะดวกที่คุณใช้ตลอดเวลาเพื่อให้งานเสร็จ สิ่งเหล่านี้สามารถอาศัยอยู่ในเนมสเปซหลักและมีส่วนหัวของตัวเอง ฯลฯ นิยามฟังก์ชันตัวช่วยอื่น ๆ เป็นฟังก์ชันยูทิลิตี้สำหรับคลาสเดี่ยวหรือตระกูลคลาส

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

ขณะนี้isPrinterมีให้สำหรับรหัสใด ๆ รวมถึงส่วนหัว แต่print_alignment_pageต้องมี using namespace printer_utils::Xerox;คำสั่ง หนึ่งอาจอ้างอิงเป็น

Canon::print_alignment_page();

เพื่อความชัดเจนมากขึ้น

C ++ STL มีstd::เนมสเปซซึ่งครอบคลุมเกือบทุกคลาสและฟังก์ชั่นของมัน แต่มันแบ่งออกเป็นส่วนหัวมากกว่า 17 ส่วนหัวที่แตกต่างกันเพื่อให้ coder ได้รับชื่อคลาสชื่อฟังก์ชั่นและอื่น ๆ หากพวกเขาต้องการเขียน ด้วยตัวของพวกเขาเอง.

ในความเป็นจริงมันจะไม่แนะนำให้ใช้ในไฟล์ส่วนหัวหรือเป็นจะทำมักจะเป็นภายในบรรทัดแรกusing namespace std; เป็น 5 ตัวอักษรและมักจะเป็นงานที่น่าเบื่อที่จะนำหน้าฟังก์ชั่นที่ต้องการใช้ (โดยเฉพาะและ!) แต่มันก็มีจุดประสงค์main()std::std::coutstd::endl

C ++ 11 ใหม่มีเนมสเปซย่อยบางรายการอยู่ในนั้นสำหรับบริการพิเศษเช่น

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

ที่สามารถนำมาใช้งานได้

เทคนิคที่มีประโยชน์เป็นองค์ประกอบ namespace หนึ่งกำหนดเนมสเปซที่กำหนดเองเพื่อเก็บเนมสเปซที่คุณต้องการสำหรับ.cppไฟล์เฉพาะของคุณและใช้สิ่งนั้นแทนการใช้usingข้อความสั่งจำนวนมากสำหรับแต่ละสิ่งในเนมสเปซที่คุณอาจต้องการ

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

เทคนิคนี้ จำกัด การเปิดเผยทั้งหมดstd:: namespace( ใหญ่มาก ) และอนุญาตให้หนึ่งเขียนโค้ดที่สะอาดกว่าสำหรับบรรทัดโค้ดที่พบบ่อยที่สุดที่ผู้คนเขียนบ่อยที่สุด


-2

คุณอาจต้องการวางไว้ในฟังก์ชันเทมเพลตเพื่อให้พร้อมใช้งานสำหรับจำนวนเต็มและ / หรือจำนวนชนิดต่าง ๆ :

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

คุณสามารถสร้างประเภทที่กำหนดเองที่ประณีตซึ่งเป็นตัวแทนของตัวอย่างจำนวนมากหรือจำนวนเชิงซ้อนได้ด้วยการให้ตัวดำเนินการที่เกี่ยวข้องมากเกินไปสำหรับประเภทที่กำหนดเองของคุณเพื่อให้เทมเพลตเป็นมิตร

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.