لا تقدم المكتبة القياسية C ++ مجموعة من الفئات فحسب ، بل تحدد أيضًا كيفية كتابة البرامج. تتناول هذه المقالة المتطلبات العامة لتنفيذ البرامج باستخدام STL.
النظر في المهمة التالية:
اقرأ مجموعة من الأعداد الصحيحة مفصولة بمسافة بيضاء من ملف input.txt. فرزها والكتابة إلى output.txt
يمكنك كتابة الحل التالي:
#include <vector> #include <algorithm> #include <fstream> int main(){ // input.txt std::ifstream fin("input.txt"); // output.txt std::ofstream fout("output.txt"); // std::vector<int> v; // , std::copy(std::istream_iterator<int>(fin), std::istream_iterator<int>(), std::inserter(v, v.end())); // std::sort(v.begin(), v.end()); // , std::copy(v.begin(), v.end(), std::ostream_iterator<int>(fout, " ")); return 0; }
بضع كلمات عن "السحر" في الكود:
- واحدة من أسس المكتبة هي التكرارات ، وكذلك الفواصل نصف التي يحددونها. حسب الدلالات (قراءة بالسلوك) فإنها تتزامن مع المؤشرات. أي أن مشغل إلغاء التأجيل * سيعيد إليك العنصر الذي يشير إليه التكرار ، وسيترجم ++ التكرار إلى العنصر التالي. على وجه الخصوص ، يتم تمثيل أي حاوية بتكرارات النهاية [البداية والنهاية) ، حيث تشير نقاط البداية إلى العنصر الأول ، النهاية - لآخر ؛
- الخوارزميات التي تعمل مع الحاويات ، حيث تأخذ المعلمات بداية ونهاية الحاوية (أو جزء منها) ؛
- تقوم خوارزمية نسخة النسخ ببساطة بإعادة كتابة العناصر من فترة فاصل إلى أخرى. إذا لم يتم تخصيص أي ذاكرة في الحاوية المستهدفة ، يكون السلوك غير متوقع [ نسخة ] ؛
- تقوم الدالة inserter بإدراج قيمة في الحاوية قبل التكرار المحدد [ inserter ]
- يوفر istream_iterator و ostream_iterator وصولًا على نمط الحاوية إلى التدفقات [ istream_iterator ، ostream_iterator ]
هذا المثال هو في الواقع بسيط جدا. ومع ذلك ، يمكنه مساعدتنا في حل المشكلة التالية:
يحتوي ملف input.txt على قائمة تحتوي على معلومات حول الأشخاص: الاسم الأخير والاسم الأول والعمر (كل سطر عبارة عن سجل ، يتم فصل البيانات بمسافة). اقرأ هذه البيانات في صفيف ، وفرزها حسب العمر واكتب إلى ملف output.txt. عرض معلومات حول الشخص الذي يزيد عمره عن 20 عامًا ولكن أقل من 25 عامًا.
من حيث المبدأ ، فإن الحل سيكون هو نفسه تقريبا. ومع ذلك ، لإنقاذ القرار ، من الضروري القيام بالأعمال التحضيرية ، وهي:
- أعلن بنية البيانات. - يمكنك تحديد شيء مفيد ، ولكن في حالة معينة ، تكون البنية كافية:
struct man{ std::string firstname, secondname; size_t age; };
أوصي بشدة أن تفكر في تنفيذ مُنشئي النسخ ، باستخدام المعلمات الافتراضية ومشغل النسخ. مع مزيد من التطوير للمشروع ، ستحتاج بالتأكيد لهم. - عوامل تشغيل الإدخال / الإخراج الزائد - يتم التعامل مع هذه العوامل من خلال التكرارات على سلاسل العمليات. على أي حال ، من الشائع استخدامها.
std::ostream& operator << (std::ostream& out, const man& p){ out << p.firstname << " " << p.secondname << " " << p.age; return out; } std::istream& operator >> (std::istream& in, man& p){ in >> p.firstname >> p.secondname >> p.age; return in; }
- تحديد قواعد ترتيب الكائنات - هذا بالفعل امتداد كبير: يمكنك تخطي عامل التشغيل <، يمكنك وصف تعبير دالة أو عامل أو تعبير lambda. في هذه الحالة ، استخدم الوظيفة.
bool comparator(const man& p1, const man& p2){ return p1.age < p2.age; }
- تحديد قاعدة لاختيار الكائنات - مرة أخرى ، مجموعة كبيرة إلى حد ما من التطبيقات. هذه المرة ، دعنا نوجد عامل توجيه (كائن من الفئة يتم فيه تعريف عامل التشغيل [ functor ]) والذي يمكنك من خلاله تمرير الفئة العمرية:
struct predicate{ size_t begin, end; predicate(int p1, int p2): begin(p1), end(p2) {} bool operator ()(const man& p){ return (p.age > begin) && (p.age < end); } };
انتبه إلى مُنشئ المُشغل - وبهذه الطريقة يمكننا تخصيص سلوكه.
حسنًا ، في الواقع ، نقطة الدخول إلى البرنامج:
int main(){ std::ifstream fin("input.txt"); std::ofstream fout("output.txt"); std::vector<man> v; std::copy(std::istream_iterator<man>(fin), std::istream_iterator<man>(), std::inserter(v, v.end())); std::sort(v.begin(), v.end(), comparator); std::copy_if(v.begin(), v.end(), std::ostream_iterator<man>(std::cout, "\n"), predicate(20, 25)); std::copy(v.begin(), v.end(), std::ostream_iterator<man>(fout, "\n")); return 0; }
كما ترون ، فإن التغييرات في الوظيفة الرئيسية ضئيلة ، وتؤثر فقط على نوع عناصر المتجه. بالإضافة إلى إضافة مكالمة إلى خوارزمية copy_if. ظهرت هذه الخوارزمية المفيدة مع معيار C ++ 11 ، وهي تنسخ العناصر من حاوية واحدة فقط إلى تلك العناصر التي تفي بالشرط.
ما هي الاستنتاجات التي يمكن استخلاصها من هذا؟- إن معرفة خوارزميات المكتبة القياسية واستخدامها بفعالية يسرع عملية التطوير بشكل كبير (بشكل أدق ، يؤدي إلى التلقائية).
- من المفيد الإعلان عن المنشئات المختلفة ومشغلي النسخ لهياكل البيانات. يتم استخدامها في المواقف المختلفة ، خاصة عند إدراج العناصر في حاويات.
- للراحة ، يمكنك زيادة التحميل على مشغلي المدخلات والمخرجات ، وكذلك مشغل المقارنة ومشغل الطلب.
- Functors - أداة قوية تتيح لك تنفيذ وظائف مع "الذاكرة" أو سلوك إضافي
- ... ربما بعض أكثر ...
شكرا لك على التحمل!
كل رمز البرنامج:an_example.cpp #include <string> #include <vector> #include <fstream> #include <algorithm> #include <iostream> #include <iterator> struct man{ std::string firstname, secondname; size_t age; }; std::ostream& operator << (std::ostream& out, const man& p){ out << p.firstname << " " << p.secondname << " " << p.age; return out; } std::istream& operator >> (std::istream& in, man& p){ in >> p.firstname >> p.secondname >> p.age; return in; } bool comparator(const man& p1, const man& p2){ return p1.age < p2.age; } struct predicate{ size_t begin, end; predicate(int p1, int p2): begin(p1), end(p2) {} bool operator ()(const man& p){ return (p.age > begin) && (p.age < end); } }; int main(){ std::ifstream fin("input.txt"); std::ofstream fout("output.txt"); std::vector<man> v; std::vector<man>::iterator i; std::copy(std::istream_iterator<man>(fin), std::istream_iterator<man>(), std::inserter(v, v.end())); std::sort(v.begin(), v.end(), comparator); std::copy_if(v.begin(), v.end(), std::ostream_iterator<man>(std::cout, "\n"), predicate(20, 25)); std::copy(v.begin(), v.end(), std::ostream_iterator<man>(fout, "\n")); return 0; }
ببليوغرافيا- ستيبانوف آل. لي منغ ، مكتبة النماذج القياسية ، 1995
- المرجع CPP ، نسخة
- المرجع CPP ، الواضع
- مرجع CPP ، istream_iterator
- المرجع CPP ، ostream_iterator
- ويكي functor