درک مفهوم struct و چرا برای گروهبندی دادههای مرتبط لازم است
تعریف struct، دسترسی به فیلدها با عملگر نقطه
ساخت آرایهای از structها (مانند student x[100])
آشنایی با توابع عضو درون struct (روش ابتدایی OOP در C++)
پیادهسازی مرتبسازی حبابی روی آرایهی struct
ساختار (struct) چیست؟
فرض کنید میخواهید اطلاعات یک دانشجو را ذخیره کنید: نام، تاریخ تولد، نام خانوادگی و معدل. اگر برای هر کدام یک متغیر جداگانه بسازید، مدیریت دهها دانشجو بهشکل name1, name2, ... غیرممکن میشود.
struct به ما اجازه میدهد چند متغیر مرتبط را در یک نوع دادهی جدید گروهبندی کنیم. سپس میتوانیم بهسادگی متغیرهایی از این نوع تعریف کنیم:
main.cppcpp
حالا student x; یک متغیر کامل است که شامل همهی این فیلدهاست.
تعریف و استفاده از struct student
هر فیلد با عملگر نقطه . در دسترس است: x.name, x.avg و غیره. به نقطهویرگول }; در انتهای struct دقت کنید؛ فراموش کردن آن خطای کامپایل است.
تعریف struct، تعریف متغیر، دسترسی به فیلدهاcpp
$خروجی برنامه
nam: Ali
tarikh tavalod: 1380
nam khanevadegi: Rezaei
moadel: 18.5
--- etelaat daneshjoo ---
Ali Rezaei tarikh: 1380 moadel: 18.5
آرایهای از ساختارها
وقتی بخواهیم اطلاعات چندین شیء را نگه داریم (مثلاً ۱۰۰ دانشجو)، از آرایهی struct استفاده میکنیم:
main.cppcpp
هر عنصر x[i] یک student کامل با همهی فیلدهاست. دسترسی به فیلد iامین دانشجو به این شکل است: x[i].name, x[i].avg.
آرایهای از ساختارهای دانشجو
برای هر دانشجو نام و معدل میگیریم و سپس فقط کسانی را چاپ میکنیم که معدلشان بالای ۱۲ است.
خواندن اطلاعات چند دانشجو و چاپ معدل بالای ۱۲cpp
$خروجی برنامه
tedad daneshjoo: 3
name va moadel 1: Ali 18.5
name va moadel 2: Sara 9.5
name va moadel 3: Reza 15.0
--- moadel > 12 ---
Ali 18.5
Reza 15
توابع عضو (Member Functions)
در C++ میتوانیم توابعی را داخل خود struct تعریف کنیم که به فیلدهای آن دسترسی مستقیم دارند. این یک قدم اولیه به سمت برنامهنویسی شیءگرا (OOP) است.
main.cppcpp
فراخوانی: x.calc_sal();. این تابع مقدار salary را بر اساس hours و per_h محاسبه میکند.
محاسبه حقوق کارکنان با struct
در مثال زیر، یک کارمند میگیریم، ساعت کاری و نرخ ساعتی را وارد میکنیم، تابع عضو calc_sal حقوق را محاسبه میکند و سپس چاپ میکنیم.
struct employee با تابع عضو calc_salcpp
$خروجی برنامه
saat kar: 40
nerkh saat: 50
hoghoogh = 2000
خطاهای رایج در تعریف struct
در جزوه اصلی دو خطای متداول وجود داشت:
استفاده از علامت - در نام فیلد مثل per-h. در C++ نام شناسهها فقط میتواند شامل حروف، اعداد و _ باشد. باید per_h یا perH نوشته شود.
عدم تطابق بزرگی/کوچکی حرف در تعریف توابع عضو: در جزوه از Point (با P بزرگ) در تعریف تابع و از point (با p کوچک) در تعریف struct استفاده شده بود. C++ به بزرگی و کوچکی حروف حساس است.
نسخه اشتباه جزوه
کد دارای باگcpp
نسخه اصلاحشده و استاندارد
کد صحیحcpp
struct point برای کارهای هندسی
یکی از کاربردهای رایج struct، تعریف نقطه در صفحه است. با تعریف point که شامل x و y است، میتوانیم راحتتر با اشکال هندسی کار کنیم.
main.cppcpp
سپس با تعریف point P1, P2; دو نقطه داریم و میتوانیم توابعی بنویسیم که روی نقاط کار میکنند: دریافت مختصات، رسم خط، محاسبه نقطهی وسط، و غیره.
توابع عضو برای struct point
سه تابع عضو برای point تعریف میکنیم: get_point مختصات را از ورودی میگیرد (با ارجاع)، P_line خطی بین دو نقطه را شبیهسازی میکند (در محیط واقعی از line() استفاده میشود، اما اینجا چاپ متنی)، و mid نقطهی وسط دو نقطه را چاپ میکند.
get_point, P_line, midcpp
$خروجی برنامه
mokhtasate P1: 1 2
mokhtasate P2: 5 8
khat az (1,2) ta (5,8)
noghteye vaset = (3, 5)
حسابگر اشارهگر روی آرایهی struct
میتوانیم با اشارهگر روی آرایهی struct هم حرکت کنیم. اگر q یک اشارهگر به point باشد، با q++ به عنصر بعدی آرایه میرود (همان b+1).
main.cppcpp
نکته: برای دسترسی به فیلد از طریق اشارهگر از عملگر -> (پیکان) استفاده میشود. یعنی q->x معادل (*q).x است.
حرکت اشارهگر روی آرایهی struct
این مثال نشان میدهد چگونه با اشارهگر روی عناصر آرایهی struct حرکت میکنیم و با -> به فیلدها دسترسی پیدا میکنیم.
q = b + 1 و q--cpp
$خروجی برنامه
q->x = 3
q->x = 1
مرتبسازی حبابی روی آرایهی struct
میتوانیم با همان الگوریتم Bubble Sort که قبلاً یاد گرفتیم، یک آرایهی struct را بر اساس یکی از فیلدها مرتب کنیم. مثلاً مرتب کردن دانشجویان بر اساس avg نزولی.
کلیدیترین بخش: شرط مقایسه روی فیلد مورد نظر انجام میشود: x[j].avg < x[j+1].avg.
مرتبسازی حبابی آرایهی struct دانشجو
این برنامه ن Student میگیرد، آنها را بر اساس معدل از بیشترین به کمترین مرتب میکند و سپس چاپ میکند. برای جابجایی دو student از std::swap استفاده میکنیم که کل struct را عوض میکند.
مرتبسازی بر اساس معدل (نزولی)cpp
$خروجی برنامه
tedad daneshjoo: 3
Ali 14
Sara 19.5
Reza 17.5
--- morattab shode ---
Sara 19.5
Reza 17.5
Ali 14
ارسال struct به تابع
یک struct را میتوان به سه روش به تابع ارسال کرد:
با مقدار (by value):void f(student s) — کل struct کپی میشود. اگر struct بزرگ باشد، پرهزینه است.
با ارجاع (by reference):void f(student &s) — بدون کپی، میتواند ساختار اصلی را تغییر دهد.
با اشارهگر (by pointer):void f(student *s) — همان ارجاع اما با syntax اشارهگر (s->name).
برای توابعی که فقط میخواهند ساختار را بخوانند (نه تغییر دهند)، بهترین روش const student &s است: هم کپی نمیشود، هم از تغییر تصادفی جلوگیری میکند.
ارسال struct با ارجاع به تابع
این تابع بدون کپی کردن، اطلاعات دانشجو را چاپ میکند. کلمهی const به کامپایلر قول میدهد که s را تغییر نمیدهیم.
تابع printStudent با const referencecpp
$خروجی برنامه
Ali 15.5
Ali 17.5
خطاهای رایج در کار با struct
فراموش کردن }; در انتهای تعریف struct — یکی از رایجترین خطاها.
استفاده از کاراکترهای غیرمجاز در نام فیلدها (مثل - یا شروع با عدد).
عدم تطابق بزرگی/کوچکی حروف (مثل تعریف point و استفاده از Point).
دسترسی نادرست به فیلد با اشارهگر: اگر p اشارهگر به student باشد، باید p->name بنویسید، نه p.name.
کپی کردن بیمورد structهای بزرگ در توابع — بهتر است از const & استفاده کنید.
مقداردهینکردن فیلدها قبل از استفاده میتواند به مقادیر آشغال منجر شود.
struct در برابر class — تفاوت چیست؟
در C++ تفاوت اصلی struct و class در پیشفرض دسترسی است:
در struct همهی فیلدها و توابع عضو بهصورت پیشفرض public (قابل دسترس از بیرون) هستند.
در class همهی فیلدها بهصورت پیشفرض private (فقط از داخل خود class) هستند.
برای دادههای سادهی بدون منطق (مثل point, student) معمولاً از struct استفاده میشود. برای کلاسهای پیچیدهتر با منطق و کپسولهسازی، class مناسبتر است.
یک struct به نام `Book` با فیلدهای `title` (رشته)، `author` (رشته) و `price` (عدد اعشاری) تعریف کنید. در `main` یک کتاب بسازید، مقادیر را از ورودی بگیرید و چاپ کنید.
نمایش راهنما
فراموش نکنید هدر `<string>` را اضافه کنید. برای دسترسی به فیلد از عملگر `.` استفاده کنید.
02
تمرین•حل کامل دارد
struct به نام `Point` با فیلدهای `x` و `y` بسازید. تابع `double distance(Point a, Point b)` بنویسید که فاصلهی اقلیدسی دو نقطه را برگرداند. در `main` آن را تست کنید.
نمایش راهنما
فرمول فاصله: sqrt((a.x-b.x)² + (a.y-b.y)²). از `<cmath>` استفاده کنید.
03
تمرین•حل کامل دارد
struct `Student` با `name` و `avg` بسازید. در `main` آرایهای از ۵ دانشجو بسازید (با مقادیر اولیهی دلخواه) و دانشآموزی که بیشترین معدل را دارد پیدا و چاپ کنید.
نمایش راهنما
یک حلقه بزنید و در متغیر `int best = 0;` اندیس بهترین را نگه دارید. هر بار `x[i].avg > x[best].avg`، `best` را بهروز کنید.
04
تمرین•حل کامل دارد
struct `Circle` با فیلدهای `x` و `y` (مرکز دایره) و `r` (شعاع) تعریف کنید. تابع عضو `area()` بنویسید که مساحت دایره را برگرداند (πr²). در `main` یک دایره بسازید و مساحت آن را چاپ کنید.
نمایش راهنما
برای π از `M_PI` (در `<cmath>`) یا عدد `3.14159` استفاده کنید.
05
تمرین•حل کامل دارد
struct `Student` با `name` و `avg` بسازید. تابع `void sortByAvg(Student x[], int n)` بنویسید که آرایه را **صعودی** بر اساس معدل مرتب کند. در `main` آرایهای از ۴ دانشجو بسازید، مرتب کنید و چاپ کنید.
نمایش راهنما
از الگوریتم Bubble Sort استفاده کنید، اما شرط را روی `x[j].avg > x[j+1].avg` بگذارید.
// نکات-کلیدی۶
01struct چند متغیر مرتبط را در یک نوع دادهی جدید گروهبندی میکند و مدیریت دادههای پیچیده را آسان میکند.
02دسترسی به فیلدهای struct با عملگر `.` برای متغیر معمولی و با `->` برای اشارهگر انجام میشود.
03میتوان آرایهای از structها ساخت (مثل `student x[100];`) و توابعی نوشت که روی این آرایه کار میکنند.
04C++ اجازه میدهد توابع عضو داخل struct تعریف شوند (مثل `calc_sal`) که اولین قدم به سمت برنامهنویسی شیءگرا است.
05برای ارسال struct به تابع از `const &` استفاده کنید تا از کپی اضافی جلوگیری شود و از تغییر تصادفی محافظت گردد.
06الگوریتمهای مرتبسازی مثل Bubble Sort را میتوان روی آرایهی struct با مقایسهی یکی از فیلدها به کار برد.
گام بعدی پیشنهادی
توابع بازگشتی (Recursion)
از فاکتوریل تا فیبوناچی، توان و مجموع — با تحلیل پشته
1structemployee{2int per_h;// صحیح: از _ استفاده شده3int salary;4voidcalc_sal(){// صحیح: از _ استفاده شده5 salary = hours * per_h;6}7};// صحیح: فقط یک ; لازم است
structpoint{int x;int y;};
1#include<iostream>2usingnamespace std;34structpoint{5int x;6int y;78voidget_point(){9 cin >> x >> y;10}1112voidP_line(point other){13 cout <<"khat az ("<< x <<","<< y
14<<") ta ("<< other.x <<","<< other.y <<")"<< endl;15}16};1718voidmid(point P1, point P2){19 cout <<"noghteye vaset = ("20<<(double)(P1.x + P2.x)/2<<", "21<<(double)(P1.y + P2.y)/2<<")"<< endl;22}2324intmain(){25 point P1, P2;26 cout <<"mokhtasate P1: "; P1.get_point();27 cout <<"mokhtasate P2: "; P2.get_point();28 P1.P_line(P2);29mid(P1, P2);30return0;31}
point b[3]={{1,2},{3,4},{5,6}};point *q = b +1;// q به b[1] اشاره میکندq--;// حالا q به b[0] اشاره میکندcout << q->x;// 1 چاپ میشود (عملگر ->)
1#include<iostream>2usingnamespace std;34structpoint{5int x, y;6};78intmain(){9 point b[3]={{1,2},{3,4},{5,6}};10 point *q = b +1;// q به b[1] یعنی {3,4} اشاره میکند11 cout <<"q->x = "<< q->x << endl;// 312 q--;// بازگشت به b[0]13 cout <<"q->x = "<< q->x << endl;// 114return0;15}
1#include<iostream>2#include<string>3usingnamespace std;45structstudent{6 string name;7double avg;8};910voidbubbleSort(student x[],int n){11for(int i =0; i < n -1; i++)12for(int j =0; j < n -1- i; j++)13if(x[j].avg < x[j +1].avg)14swap(x[j], x[j +1]);15}1617intmain(){18int n;19 cout <<"tedad daneshjoo: ";20 cin >> n;21 student *x =new student[n];22for(int i =0; i < n; i++)23 cin >> x[i].name >> x[i].avg;2425bubbleSort(x, n);2627 cout <<"\n--- morattab shode ---"<< endl;28for(int i =0; i < n; i++)29 cout << x[i].name <<" "<< x[i].avg << endl;3031delete[] x;32return0;33}
1#include<iostream>2#include<string>3usingnamespace std;45structstudent{6 string name;7double avg;8};910voidprintStudent(const student &s){11 cout << s.name <<" "<< s.avg << endl;12}1314voidraiseAvg(student &s,double amount){15 s.avg += amount;// میتوانیم تغییر دهیم چون & داریم16}1718intmain(){19 student s ={"Ali",15.5};20printStudent(s);21raiseAvg(s,2.0);22printStudent(s);23return0;24}