Faragiri

همهٔ زبان‌ها

آموزش سی‌پلاس‌پلاس (C++) سی، سی‌پلاس‌پلاس و سی‌شارپ

هر چیزی که باید آزاد شود در یک مخرب می‌نشیند و دامنه آزادش می‌کند. همین یک قاعده، جای هر بلوک آزادسازی دستی را می‌گیرد.

۴بخش
۳دقیقه خواندن
۴عمق

یک بلوک، چهار حالت

عمر دو شی داخل یک آکولاد

هر قاب یک حالت است و در هیچ‌کدام کسی چیزی را آزاد نکرده؛ همه‌اش را ورود و خروج از دامنه انجام داده.

  1. ۰۱ورود به بلوکهنوز هیچ شی‌ای ساخته نشده و هیچ منبعی گرفته نشده
  2. ۰۲اتصال ساخته شدسازنده منبع را گرفت، و از همین لحظه آزادسازی تضمین شده است
  3. ۰۳تراکنش روی آن ساخته شددومین شی، که به اولی وابسته است و بعد از آن ساخته شد
  4. ۰۴آکولاد بستهاول تراکنش بسته می‌شود و بعد اتصال، به ترتیب وارونه ساخت

این چهار قاب مسیر بدون خطا را می‌کشند. مسیر خطا دقیقا همین است: استثنا هم قاب آخر را اجرا می‌کند، و همان چیزی است که آزادسازی دستی هیچ‌وقت درست انجامش نمی‌دهد.

سی‌پلاس‌پلاس یک قاعده دارد که بقیه‌اش از آن می‌آید: وقتی نام یک شی از دامنه بیرون می‌رود، مخربش خودکار اجرا می‌شود. لازم نیست چیزی صدا بزنید و در مسیر خطا هم اتفاق می‌افتد.

پیامد عملی‌اش این است: هر چیزی که باید آزاد شود، بسته شود یا رها شود، در یک مخرب بگذارید و به آن چیز یک دامنه بدهید. فایل، اتصال، قفل، و حافظه همه یک الگو دارند و هیچ‌کدام به بلوک آزادسازی دستی نیاز ندارند.

ترتیب هم تصادفی نیست: مخرب‌ها به ترتیب وارونه ساخته شدن اجرا می‌شوند. یعنی اگر اول یک اتصال ساخته باشید و بعد یک تراکنش رویش، تراکنش اول بسته می‌شود و بعد اتصال، که همان ترتیبی است که می‌خواستید.

سه نوع اشاره‌گر، و پرسشی که میانشان انتخاب می‌کند

پرسش این است که چه کسی صاحب این شی است و چه کسی آزادش می‌کند.

  • std::unique_ptr یک صاحب دارد، کپی نمی‌شود و فقط منتقل می‌شود
  • std::shared_ptr چند صاحب دارد و با شمارش ارجاع آزاد می‌کند
  • اشاره‌گر خام یعنی «قرض گرفته‌ام و صاحبش نیستم»

پیش‌فرض باید اولی باشد و نه دومی. شمارش ارجاع هزینه دارد و مهم‌تر اینکه حلقه ارجاع می‌سازد: دو شی که به هم اشاره کنند هیچ‌وقت آزاد نمی‌شوند، و راه‌حلش اشاره‌گر ضعیف است. همان مسئله در زبان‌های با جمع‌کننده زباله هم هست: مقدارها کجا می‌نشینند و چه وقت آزاد می‌شوند.

کپی گران است و بی‌صدا

در سی‌پلاس‌پلاس انتساب یعنی کپی، و برای یک ظرف بزرگ یعنی کپی کل محتوا. هیچ خطایی نمی‌دهد و فقط کند می‌شود. پاس دادن به تابع هم همین‌طور است مگر آنکه ارجاع بگیرید، و برای همین قاعده رایج این است که پارامترهای بزرگ را با ارجاع ثابت بگیرید.

وقتی واقعا می‌خواهید مالکیت منتقل شود و نه کپی، حرکت دادن شکل درست است: محتوای شی مبدا به مقصد منتقل می‌شود و مبدا در حالتی معتبر ولی خالی می‌ماند. این تفاوت میان کپی و انتقال، بزرگ‌ترین چیزی است که سی‌پلاس‌پلاس امروزی را از نسخه‌های پیش از سال ۲۰۱۱ جدا می‌کند.

جایی که کار گیر می‌کند، آموزش سی‌پلاس‌پلاس قدیمی است

در درس‌های دانشگاهی فارسی، سی‌پلاس‌پلاس معمولا یعنی سی به‌علاوه کلاس، با new و delete دستی و آرایه خام. کسی که این‌طور یاد گرفته، کدی می‌نویسد که همان خطاهای حافظه سی را دارد و فکر می‌کند زبان امن‌تری استفاده کرده. تفاوت واقعی از جایی شروع می‌شود که delete را ننویسید: با اشاره‌گر یکتا و ظرف‌های استاندارد، بیشتر نشتی‌ها و آزادسازی‌های مضاعف اصلا ممکن نیستند. اگر در کدتان delete می‌بینید، تقریبا همیشه یعنی یک مخرب جا مانده.

ابزار، و سالی که این حرف درست است

در سال ۲۰۲۶، استاندارد جاافتاده برای پروژه تازه C++20 است و C++23 هم در کامپایلرهای اصلی پشتیبانی خوبی دارد؛ چیزی که از C++20 بیشترین تفاوت را می‌سازد ماژول‌ها و مفهوم‌ها هستند. ابزار ساخت پیش‌فرض CMake است و متن‌باز و رایگان. برای پیدا کردن همان خطاهایی که این صفحه درباره‌شان است، ضدعفونی‌کننده آدرس با یک پرچم روشن می‌شود و همراه خود کامپایلر می‌آید، و clang-tidy بخشی از آن‌ها را پیش از اجرا می‌گوید.

پرسش بعدی این است که آن حافظه‌ای که مخرب آزادش می‌کند از کجا آمده و آدرس‌ها چطور کار می‌کنند، که آدرس، تخصیص، و خطاهایی که می‌سازند است. و زبانی که همین مالکیت را در زمان کامپایل بررسی می‌کند به‌جای اینکه به قاعده تیمی بسپاردش، ایمنی حافظه، بدون جمع‌کننده زباله است.

کنار این

بالای این