C++
| ++C | |
|---|---|
![]() The C++ Programming Language הספר שנכתב אודות השפה בידי מפתחה. |
|
| פרדיגמות | תכנות אימפרטיבי, תכנות פרוצדורלי, תכנות מונחה-עצמים, תכנות גנרי, |
| מתכנן | בְּיַאנֵה סְטְרוֹוסְטְרוּפ |
| גרסה אחרונה | (C++11 (2011 (ידוע גם כ C++0x) |
| טיפוסיות | מפורשת, חזקה, סטטית, לא בטוחה. |
| מימושים | C++ Builder, clang, Comeau C/C++, GCC, Intel C++ Compiler, Microsoft Visual C++ |
| הושפעה על ידי | עדה, Smalltalk, פסקל, Simula, ALGOL, ML |
| השפיעה על | Java, C#, Perl |
| סיומת | h .hh .hpp .hxx .h++ .cc .cpp .cxx .p. |
C++ היא שפת תכנות מרובת פרדיגמות המבוססת על שפת התכנות C, שפותחה בשנות השמונים. C++ מיישמת עקרונות של תכנות פרוצדורלי, תכנות מונחה-עצמים ותכנות גנרי. שפה זו הינה אחת השפות הפופולריות בקרב מתכנתים בעולם עד היום, ושפות פופולריות אחרות (כגון Java ו-#C) הושפעו ממנה במידה רבה מאוד.
תוכן עניינים |
[עריכה] היסטוריה
פיתוח השפה החל בשנת 1979 על ידי בְּיַאנֵה סְטְרוֹוסְטְרוּפ ממעבדות בל, כאשר הוא החל את עבודתו על הגרסה הראשונה בשמה C with classes (דהיינו "C עם מחלקות"). סטרוסטרופ רצה ליצור שפה חדשה שתשלב את היתרונות של Simula, שפה מונחת עצמים איטית, יחד עם היתרונות של BCPL, שפה מהירה שקרובה לשפת הסף עליה מבוססת C.
בשנת 1983 זכתה השפה לשימוש במעבדות בל. בשנה זו נוספו לשפה כלים חדשים, בין היתר: פונקציות וירטואליות, העמסת פונקציות, העמסת אופרטורים, הפניות, קבועים, טיפוסיות חזקה והערות שורה (//). באותו זמן קיבלה השפה את שמה החדש ++C. השם בא לבטא את העובדה ש-C++ היא הרחבה לשפת C. פלוס-פלוס הוא אופרטור הגדלה עצמית שקיים ב-C, הוא מגדיל את ערך המשתנה שלם ב-1. הגרסה המסחרית הראשונה הופצה בשנת 1985 יחד עם פרסום הספר הראשון לשפה מאת סטרוסטרופ.
לאורך השנים נוספו לשפה כלים חדשים, למשל תמיכה בתבניות נוספה רק בשנות ה-90. השפה תוקננה על ידי ארגון התקינה הבינלאומי בשנת 1998. התקן השני לשפה יצא בשנת 2003 בו פורסמו מספר תיקונים לתקן הראשון. השפה נמצאת בתהליך פיתוח עד עצם היום הזה.
התקן החדש לשפה,C++11, הידוע באופן לא רשמי כC++0x פורסם באופן רשמי ב12 באוגוסט 2011[1]. התקן מגדיר מאפיינים חדשים רבים מאוד לשפה, כולל פונקציות אנונימיות, רשימות אתחול משופרות ואתחול אחיד, ביטויי קבועים דמויי פונקציה, טיפוסים אוטומטיים, לולאות מבוססות טווח (לולאת "foreach"), ועוד מאפיינים רבים מאוד שנדרשו במשך השנים.
[עריכה] עקרונות השפה
שפת C++ היא שפה מרובת פרדיגמות, כלומר היא משלבת כמה מודלים תכנותיים. השפה תומכת בתכנות פרוצדורלי שהורש משפת C, ומוסיפה תמיכה בתכנות מונחה עצמים ובתכנות גנרי. ++C תוכננה כך שהיא שומרת על תאימות לאחור עם שפת C במידה רבה מאוד. כלומר, ברוב המקרים, קוד הכתוב בשפת C יתקמפל וירוץ באותה דרך גם בעזרת מהדר של ++C, ללא שינויים, או שיידרשו שינויים מינוריים (הדרישה להוספת פעולת המרה מפורשת בעת השמה של מצביעים ספציפיים ל-*void ובחזרה היא דוגמה בולטת לשינוי כזה).
כשפה מונחת עצמים, תומכת C++ בעקרונות כימוס, הורשה ורב־צורתיות (פולימורפיזם). מטרת כלים אלו היא לפשט את מבנה התוכנה, לאפשר שימוש חוזר בחלקי תוכנה קיימים ולהקל על תהליך הפיתוח. שימוש נכון בהם מאפשר לזהות שגיאות כבר בשלב ההידור ולחסוך את גילויין ותיקונן בשלבים מאוחרים יותר של תהליך הפיתוח. C++ תומכת בירושה מרובה (מחלקה אחת יכולה לרשת משתי מחלקות ויותר), שלא נתמכת בשפות מודרניות אחרות כגון C# או Java.
++C איננה שפה מונחית עצמים "טהורה", כלומר קיימים בה טיפוסים שאינם מחלקות, וניתן לכתוב בה פונקציות שאינן שיטות של מחלקה.
מבחינת התכנות הגנרי, מאפשרת C++ שימוש בתבניות (templates). התבניות מאפשרות כתיבת קוד כללי יעיל, ללא תלות בהיררכית הורשה. C++ תומכת בתבניות הן לפונקציות והן למחלקות. בנוסף, קיימת טכניקת תכנות בשם "Template Meta-Programming" (TMP) המהווה בעצם "תת-שפה" נפרדת, פונקציונלית, המנצלת את התבניות על מנת לאפשר ביצוע חישובים מורכבים בזמן הידור.
בשל היותה מרובת פרדיגמות ניתן להשתמש ב-C++ ברמות שונות לאורך הקשת הנפרשת בין שפת C, שהיא שפה פרוצדורלית, לבין הכלים של תכנות מונחה עצמים ותכנות גנרי. השפה מאפשרת לשלב מספר פרדיגמות תכנות בתוכנית אחת.
C++ שומרת במרבית המקרים על היעילות והגמישות בהן מצטיינת שפת C. רוב הפעולות בשפה מתורגמת לפעולות מועטות יחסית בשפת מכונה המבוצעת בזמן ריצה ישירות על ידי המעבד. זאת בניגוד לשפות #C ו-Java המתורגמות לרוב לשפת ביניים המורצת ע"י מכונה וירטואלית. מנגנונים המוסיפים overhead מסוים (לדוגמה חריגות או RTTI) לא פוגעים בזמן הריצה אלא אם כן חל שימוש בהם. העובדה שהקוד איננו רץ על מכונה וירטואלית פוגעת במידת הניידות שלו - תכנית מהודרת בשפת C תרוץ בדרך כלל רק על מערכת מהסוג אליו הודרה, ותהיה תלויה בארכיטקטורת המעבד ובמערכת ההפעלה.
[עריכה] מושגים בשפה
[עריכה] מחלקה
המחלקה (class) היא לב לבה של השפה. המחלקה היא תיאור של טיפוס הכולל נתונים ופעולות שאפשר לבצע על הנתונים. משתנים מסוג אותו טיפוס נקרא "עצמים" (objects). המחלקה כוללת משתנים (data members) ושיטות (member functions או methods). השיטות הן פונקציות שפועלות על המשתנים של המחלקה. שני סוגים חשובים של שיטות הן פונקציות הבנייה (Constructors) ופונקציית ההשמדה (Destructor) אשר תפקידן הוא לאתחל עצם מהמחלקה ולמחוק את תוכנו כאשר הוא נמחק.
[עריכה] הגבלת גישה
הגבלת הגישה לרכיבי המחלקה השונים מהווה כלי מרכזי למימוש עקרון הכימוס. ישנן שלוש רמות של הגבלות גישה:
- פרטי (private) – רק שיטות המחלקה יכולות להשתמש בהם.
- שמור (protected) – רק שיטות המחלקה ומחלקות שנורשות ממנה יכולות להשתמש בהם.
- ציבורי (public) – לכולם יש גישה אליהם.
כמו כן ניתן להגדיר פונקציה או מחלקה מסוימת כחבר (friend). חברי המחלקה מקבלים גישה לכל המשתנים, השיטות והטיפוסים שמוגדרים במחלקה, גם לפרטיים וגם לשמורים.
[עריכה] הורשה
ניתן להגדיר מחלקה נורשת ("derived class") על סמך מחלקה אחרת, בסיסית ("base class"). המחלקה הנורשת מכילה אוטומטית את המשתנים, השיטות ושאר רכיבי המחלקה הבסיסית ובנוסף מגדירה כאלו משל עצמה. לעומת שפות מודרניות אחרות שמתבססות עליה, C++ תומכת בהורשה מרובה, כלומר ירושה ממספר מחלקות בו זמנית.
כאשר מורישים תכונות למחלקה אחת ממחלקה שניה ניתן לעשות זאת באחת משלוש דרכים:
- הורשה ציבורית – יורשת את כל המשתנים, השיטות והטיפוסים באותה רמת הגישה.
- הורשה שמורה – המשתנים, השיטות והטיפוסים הציבוריים של מחלקת האב מקבלים הרשאת גישה שמורה (protected) במחלקת הבן.
- הורשה פרטית – המחלקה הנגזרת וחבריה הם היחידים שיכולים להשתמש בתכונות של מחלקת האב. כל התכונות הללו הופכות לפרטיות במחלקת הבן.
השפה תומכת גם בפולימורפיזם: ניתן להתייחס לעצם דרך מצביע או ייחוס (Reference) למחלקה בסיסית, ולהפעיל שיטות של העצם בלי לדעת מראש את הטיפוס המדויק שלו. שיטות כאלו מוגדרות "וירטואליות". הן ניתנות להפעלה על ידי התייחסות לעצם ממחלקה בסיסית אך בפועל מבוצעת השיטה במחלקה הנורשת, בהתאם לסוג העצם עבורו הופעלה השיטה.
מנגנון ה־Run-Time Type Information (RTTI) מאפשר לקבל מידע על הטיפוס של העצם הנתון, תוך כדי ריצת התוכנית. מנגנון זה מגדיר את האופרטור dynamic_cast שמאפשר לבצע המרה בטוחה בין מצביע (הוא הפנייה) למחלקת בסיס לבין מצביע למחלקה הנגזרת. אופרטור זה מאפשר לבדוק האם העצם הנתון הוא מטיפוס מסוים או לא. אופרטור נוסף שנוסף לשפה במסגרת ה-RTTI הוא אופרטור ה-typeid. אופרטור זה מאפשר לקבל את הטיפוס המדויק של העצם הנתון.
לעומת שפות אחרות, כמו C# לדוגמה, ב-C++ אין הבדל בין מחלקות (class) לבין מבנים (struct). גם מחלקות וגם מבנים יכולים להכיל שיטות, לרשת האחד מהשני, להגדיר פונקציות וירטואליות ולהגדיר רמות גישה שונות. ההבדל היחיד הוא שתכונות המבנה מוגדרות ציבוריות כברירת מחדל ואילו תכונות המחלקה כפרטיות.
[עריכה] תבניות
שפת C++ תומכת ב־templates – תבניות. התבניות מהוות רמת הפשטה נוספת מעל רמת ההפשטה של המחלקה. הן מאפשרות יצירת מחלקות או פונקציות על ידי תבנית, כאשר בכל פעם הפונקציה או מחלקה נוצרת עבור טיפוס או טיפוסים אחרים. ה"טיפוס" הוא טיפוס פשוט או מחלקה בעצמו. הטיפוסים "מועברים" בכמעין פרמטר על ידי שימוש בסוגריים זויתיים: "<...>" במקום השימוש ב "(...)" הרגילים. המהדר (קומפיילר) מזהה אילו פרמטרים הועברו לתבנית ומשכפל את התבנית בהתאם.
[עריכה] Template Metaprogramming
ישנה טכניקת תכנות בשם "Template Metaprogramming" או בקיצור TMP, המנצלת את התבניות על מנת לבצע חישובים מורכבים בזמן ההידור, ובכך עשויה להפחית את החישובים שיש לבצע בזמן ריצה.
טכניקה זו נתגלתה במקרה: במהלך הכנת התקן של שפת ++C, התברר שהתבניות של השפה הן בעצמן תת-שפה שלמה-טיורינג, כלומר שניתן לבצע באמצעותה כל חישוב שיכול מחשב לבצע. הדוגמה הראשונה לתוכנית כזאת ביצעה חישוב של מספרים ראשוניים על אף שלא סיימה לעבור הידור. הרשימה של המספרים הראשוניים הייתה חלק מהודעת השגיאה שהפיק המהדר בזמן שניסה להדר את הקוד.
כיום ישנן ספריות רבות המסייעות לעבודה בטכניקה זו, אך כיוון שמלכתחילה לא הייתה כוונה ליצור את תת-השפה הזאת בתוך ++C, התחביר שלה קשה להבנה. להלן דוגמה לקטע קוד שמבצע חישוב של עצרת:
template <int N> struct Factorial { enum { value = N * Factorial<N - 1>::value }; }; template <> struct Factorial<0> { enum { value = 1 }; }; // Factorial<4>::value == 24 // Factorial<0>::value == 1 void foo() { int x = Factorial<4>::value; // == 24 int y = Factorial<0>::value; // == 1 }
[עריכה] טיפוסיות חזקה
מערכת הטיפוסים בשפה חזקה יותר מזו שבשפת C, בה המהדר מבצע המרות טיפוסים בצורה אוטומטית ובקלות יחסית, ובה ניתן להשתמש במצביע *void שיכול להצביע אל אובייקט מכל טיפוס.
בשפת C++ הוקשחו הכללים והמרות טיפוסים מובלעות נעשות רק לפי הגדרות המובנות בשפה או כאלה שהוגדרו על ידי המשתמש. שימוש ב *void דורש המרה מפורשת, ולכל סוג של המרה ישנו אופרטור מפורש מתאים (static_cast, dynamic_cast, const_cast, reinterpret_cast). תכונה זו מאפשרת גילוי שגיאות רבות יותר בשלב ההידור וחוסכת את מציאתן המייגעת בזמן הריצה.
בשל הגישה הכללית של השפה לאפשר למתכנת לבצע כל פעולה, בתנאי שברור שהוא באמת מעוניין בכך, הטיפוסיות בשפה בטוחה פחות מאשר בשפת Java, למשל.
[עריכה] הגדרת הקבוע
את הגדרת הקבוע const ניתן להצמיד למשתנה של מחלקה, לפרמטר של שיטה, לפרמטר של פונקציה, לערך המוחזר מהן וכן לשיטה עצמה. הגדרה זו מאפשרת למהדר לגלות שימוש סותר להגדרה. לדוגמה: שיטה שהוגדרה כקבועה ומשנה את העצם עליו היא פועלת (שגיאה), פרמטר שהועבר כקבוע ומושם לתוכו ערך (שגיאה). על ידי שימוש בכלי זה ניתן לגלות שגיאות כבר בשלב ההידור - למעשה, הוא חלק ממערכת הטיפוסים.
משתנים שהם const יוצרים גם בעיות, משני סוגים:
- ישנן שיטות, כגון אופרטור האינדקס (סוגריים מרובעים []) שצריכות להתאפשר בצורה שונה מעט עבור אובייקטים שהם const או שאינם כאלה. הדבר מביא לעתים לשכפול קוד - עד כדי כתיבה של אותה פונקציה בדיוק, פעמיים; פעם כשהיא const ופעם ללא const. ישנן דרכים להתגבר על כך, אולם חלקן גוררות "שבירת" מערכת הטיפוסים - שימוש ב const_cast.
- ה-const מוגדר כ bitwise-const - כלומר, המהדר מנסה לוודא שאף ביט של האובייקט לא ישתנה. עם זאת, המצב הרצוי הוא אי שינוי של המצב של האובייקט, אך שינוי של ביטים איננו גורר בהכרח שינוי מצב. כך למשל אובייקט המעוניין לשמור מטמון (cache) תוך כדי ביצוע של שיטה המוגדרת const לשם שיפור הריצה בקריאה הבאה, לא יכול לעשות זאת באופן ישיר, כי מבחינת השפה מדובר כאן בשינוי המצב של האובייקט. זאת, אף שאין כל שינוי אמיתי במצב, מבחינת התנהגות האובייקט בעתיד. בתקן החדש ישנו פתרון חלקי לבעיה הזאת.
מנגד, הגדרת אובייקט כ-const לא מפריעה לאובייקט לשנות את המצב של עצמו דרך מצביעים, בדרכים שונות.
[עריכה] משתנה ייחוס (reference variable)
ניתן להגדיר משתנים, פרמטרים וערכים מוחזרים כמתייחסים לעצם (reference). הייחוס דומה מאוד להצבעה: כמה משתנים יכולים להתייחס לאותו עצם ולפעול עליו במשותף, כמו שמצביעים שונים יכולים לפעול על אותו עצם. ישנם מספר הבדלים בין מצביע לייחוס: מצביע יכול להיות ריק (NULL) ואילו ייחוס תמיד יתייחס לעצם כלשהו. כמו כן ניתן לשנות הצבעה של מצביע אבל לא ניתן לשנות ייחוס, הייחוס נקבע לכל אורך חייו של משתנה הייחוס. מבחינה תחבירית השימוש בייחוס הוא כמו בעצם עצמו ללא האופרטורים * ו <-.
[עריכה] העמסת פונקציות
העמסת פונקציות הינה היכולת להשתמש באותו השם לפונקציות (או שיטות) שונות, בתנאי שישנו הבדל ביניהן במספר הפרטרים או בטיפוסי הפרמטרים אותם הן מקבלות. הדבר מאפשר להגדיר מספר רב של פונקציות שמבחינה לוגית מבצעות פעולה דומה אך מקבלות פרמטרים שונים. תכונה זו מאפשרת לכתוב קוד קריא יותר, אם כי הגזמה בהעמסת פונקציות עשויה ליצור אי-בהירות או מקרים של דו-משמעות.
[עריכה] פרמטרים אופציונאלים
דרך נוספת להעמיס פונקציות היא להגדיר ערכי ברירת מחדל לפרמטרים של שיטה או פונקציה. אם מתבצעת קריאה לפונקציה שאיננה כוללת ערך עבורם, הם יקבלו את ערך ברירת המחדל. כך ניתן בקלות להגדיר פעולה זהה עבור מספר שונה של פרמטרים, מבלי לבצע שכפולי קוד.
כאמור, ניתן לכתוב ב ++C על כל הקשת מתכנות פרוצדוראלי עם טיפוסיות חלשה ועוד מרעין בישין של שפת C, ועד לניצול פרדיגמות התיכנות המודרנית ביותר. מסיבה זו ניתן, אך לא מומלץ, לכתוב פונקציות המקבלות מספר בלתי מוגדר מראש של פרמטרים, על ידי שימוש בשלוש נקודות (...) בחתימת הפונקציה. תכונה זו קיימת רק על מנת להקל על הגירה של תוכניות משפת C לשפת ++C. את ה-printf של C בה נפוץ ביותר השימוש ב (...) מחליפות ב ++C מחלקות כגון iostream המאפשרות לשרשר פרמטרים להדפסה תוך שימוש בהעמסת אופרטורים וטיפוסיות חזקה.
[עריכה] העמסת אופרטורים
בשפה קיים מנגנון העמסת אופרטורים. כלומר, ניתן להגדיר מחדש כמעט כל אופרטור המוגדר בשפה כך שהוא יהיה בעל משמעות עבור טיפוסים שונים, כולל טיפוסים המוגדרים על ידי המשתמש. למשל, אופרטור החיבור (+) בצורתו הרגילה מאפשר חיבור שני משתנים המכילים ערך מספרי. אך ניתן להגדיר אותו כך שיהיה בעל משמעות גם עבור משתנים מטיפוס שבר שהוגדר על ידי המתכנת.
אין כל הבדל סמנטי בין העמסת אופרטורים להעמסת פונקציות. אך מבחינת תחבירית העמסת אופרטורים היא תכונה שנויה במחלוקת, כיוון שהיא מאפשרת להעמיס אופרטורים ללא התייחסות למשמעות הטבעית והמקובלת שלהם; במיוחד, דבר זה עלול ליצור בעיות בעת כתיבת תבניות (templates), בהם לא ניתן לדעת מה יהיה הטיפוס שיועבר כפרמטר. דוגמה להעמסה שנויה במחלוקת היא העמסת אופרטור החיבור + כך שיאפשר שרשור מחרוזות, עבור המחלקה string. שרשור מחרוזות, בניגוד לחיבור רגיל, איננו מקיים את חוק החילוף. וכך בדוגמה הבאה המתכנת עשוי שלא להיות מודע לתוצאות של החלפת סדר המשתנים באופרטור:
template <typename T>
T add(T x, T y) {
return y+x;
}
int z = add<int>(1,2);
string s = add<string>("hello", "world");
החלפת המקומות בין הפרמטרים y, x לא תשנה דבר במקרה של הקריאה הראשונה, עבור מספרים שלמים, והיא תחזיר את התוצאה הצפויה 3. אבל עבור הקריאה של המחרוזת הפונקציה מחזירה "worldhello" ולא "helloworld" - אולי לא מה שהמתכנת שכתב את הקוד התכוון שיקרה.
מצד שני, יש להעמסת אופרטורים יתרונות, כאשר היא מבוצעת בטעם, והיא עשויה לשפר את קריאות הקוד.
[עריכה] ספרית התבניות התקנית
ספריית התבניות התקנית (STL - Standard Template Library) מכילה מימושים של מבני נתונים רבים ויעילים (מחסנית, רשימה מקושרת, עץ חיפוש מאוזן ועוד), וכן טיפוסי נתונים סטנדרטיים חשובים כמו וקטור (מערך משוכלל) ומחרוזת. הספרייה גם מכילה אלגוריתמים גנריים שניתן להפעיל על מבני הנתונים כמו גם מחלקות לטיפול במספרים ובקלט־פלט. הארכיטקטורה של הספרייה מאפשרת להרחיב אותה בקלות יחסית. ניתן להוסיף אלגוריתם חדש שפועל על מבני הנתונים הקיימים ומבנה נתונים חדש שהאלגוריתמים הקיימים עובדים עליו.
תוספות אלו, תורשה מרובה (1989), העמסת אופרטורים (1989), תבניות (1991), ספרית התבניות הסטנדרטית ו־RTTI (1995) הוכנסו לשפה בהדרגה.
[עריכה] קלט פלט
הקלט והפלט מתבצעים בשפה על ידי stream-ים מהם מבצעים קריאה באמצעות האופרטור << וכתיבה באמצעות האופרטור >>. טיפוסים אלו מוגדרים בעיקר באמצעות הספריות iostream (קלט/פלט סטנדרטי) ו-fstream (קלט/פלט באמצעות קבצים) ובמרבית המהדרים על ידי ספריות נוספות.
למשל תוך שימוש בהגדרת הקלט הסטנדרטי כ-cin והפלט הסטנדרטי כ-cout ניתן לכתוב:
int x; cout<<"please enter a number: "; cin>>x;
במקרה כאן הפלט יראה כך:
please enter a number:
והמחשב ימתין לקבלת מספר שלם.
[עריכה] שלום עולם
דוגמה לתוכנית שלום עולם בשפת C++:
#include <iostream> using namespace std; int main() { cout << "Hello, world!" << endl; }
[עריכה] ראו גם
- ספריית התבניות התקנית
- מונחים בתוכנה
- Microsoft Foundation Classes - ספרייה העוטפת חלקי API של חלונות שנכתבו ב־C, במחלקות C++.
[עריכה] לקריאה נוספת
- Stroustrup, Bjarne (2000). The C++ Programming Language, Special Edition, Addison-Wesley. ISBN 0-201-70073-5.
- Stroustrup, Bjarne (1994). The Design and Evolution of C++. Addison-Wesley. ISBN 0-201-54330-3.
- Meyers Scott (2005). Effective C++: 55 Specific Ways to Improve Your Programs and Designs, 3rd Edition, Addison-Wesley. ISBN 0-321-33487-6.
- Meyers Scott (1995). More Effective C++: 35 New Ways to Improve Your Programs and Designs, Addison-Wesley. ISBN 0-201-63371-X.
[עריכה] קישורים חיצוניים
| מיזמי קרן ויקימדיה |
|---|
- קורס C++ שניתן באוניברסיטת תל אביב הכולל את יסודות תכנות מונחה-עצמים בעברית (מניח ידע בשפת C)
- קורס C++ שניתן באוניברסיטה העברית (מניח ידע בשפת C)
- המדריך השלם ל++C, אתר איתן
- תכניות פשוטות ב++С (אנגלית)
- מדריך לשפה, אנגלית
- מילים שמורות בויקיספר עם הסבר
[עריכה] הערות שוליים
| מיזמי קרן ויקימדיה |
|---|
