تماس درباره   صفحه اصلی
  زبان ++C > فضاهای اسمی  
 
 

فضاهای اسمی

صفحه بعد   صفحه قبل

فضاهای اسمی برای پياده سازی اجزای نرم افزاری و کتابخانه های جديد بدون ايجاد برخورد اسامی با اجزای موجود بکارگرفته می شوند. در اين صفحه علاوه بر شرح فضاهای اسمی به انواع کلاس های حافظه نيز اشاره شده است.

فضای اسمی
راهنمای using
فضای اسمی std
کلاس های حافظه


فضای اسمی

وقتی يک برنامه به اندازه معينی می رسد معمولا به چند قطعه شکسته می شود، هر تکه ممکن است توسط افراد يا گروه متفاوتی ساخته و تکميل شود. زبان C تنها يک مکان برای نگهداری اسامی کليه شناسه ها و توابع دارد. يعنی برنامه نويسان بايد درباره نامگذاری اسامی محتاط باشند تا برخوردی پيش نيايد.

++C مکانيسمی برای جلوگيری از اين تصادم دارد: فضای اسمی (namespace). هر مجموعه از تعاريف که در يک کتابخانه يا يک برنامه قرار دارند درون يک فضای اسمی مجزا جا می گيرند. اعلان اسامی مشابه در فضاهای اسمی متفاوت هيچ برخوردی پيش نمی آورد.

فضای اسمی ناحيه ای است که يک شناسه اضافی را به اسامی عناصر درون خود الحاق می کند. هر نام بدليل اضافه شدن شناسه فضای اسمی منحصر بفرد خواهد شد. بنابراين می توان اسامی مشابه در فضاهای اسمی متفاوت حتی در يک فايل داشت. مادامی که اسامی مشابه در فضاهای اسمی مجزا هستند مشکلی در برنامه بروز نمی کند.

کلمه کليدی namespace يک نام منحصربفرد را برای يک فضای اسمی تعريف می کند. شکل کلی تعريف فضای اسمی به صورت زير است:

namespace identifier
{
   [ declaration-list ]
}

identifier شناسه فضای اسمی است که برای ارجاع به اعضای آن استفاده می شود. declaration-list اعضای فضای اسمی هستند که حتما بايد درون آکولاد محصور شوند. اعضا می تواند شامل اعلان متغير، تابع، اشيا و فضای اسمی باشد.

به نام هرکدام از اعضا شناسه فضای اسمی توسط عملگر حوزه (::) اضافه می شود.

مثال. دو متغير x در دو فضای اسمی. يکی one::x و ديگری two::x خواهد شد.


namespace one {
   int x;
}
namespace two {
   float x;
}
int main() {
   one::x++;
}


نکته. شناسه فضای اسمی در واحدی که استفاده می شود بايد يکتا باشد.
نکته. اعلان فضاهای اسمی می توانند تودرتو باشند.
نکته. عملگر حوزه (::) که قبل از آن شناسه ای نباشد به ناحيه سراسری اشاره می کند.


مثال. فضای اسمی B درون A تعريف شده است.

int i;
namespace A {
   int j;
   namespace B {
      int j, k;
   }
}
int main() {
   A::j++;        //A's j
   A::B::j++;   // B's j
   ::i++;          // the global i
}


می توانيد از فضای اسمی بدون شناسه به عنوان راهی برای اعلان متغيرهای ايستای سراسری بهره ببريد. فضای اسمی بدون نام شکل کلی زير را دارد.

namespace { declaration-list }

که در حقيقت مثل اين است که به صورت زير تعريف شده باشد.

namespace unique { declaration-list }
using namespace unique;

برخلاف نواحی تعريفی ديگر تعريف يک فضای اسمی می تواند در چندين قسمت درون يک واحد (مانند فايل) تقسيم شود. البته اعضای يک فضای اسمی شناسه دار می توانند در خارج از آن تعريف شوند به شرطی که بعد از تعريف فضای اسمی قرار بگيرد.


مثال. تعريف اعضای فضای اسمی در دو قسمت.

namespace A { // declare namespace A variables
   int i;
   int j;
}
namespace A { // declare namespace A functions
   void func(void);
   int int_func(int i);
}

مثال. توابع f() و g() متعلق به فضای اسمی V هستند که خارج از ان تعريف شده اند.

namespace X { void f() { } }
namespace V {
   void f();
}
void V::f() { }   // ok
void V::g() { }   // g() is not yet a member of V
namespace V {
   void g();
}


راهنمای using

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

با کلمه کليدی using به کامپايلر می گوييد از اعلانات يا تعاريف کدام فضای اسمی می خواهيد استفاده کنيد.

راهنمای using اجازه می دهد اعضای فضای اسمی بدون ذکر شناسه فضای اسمی استفاده شوند. البته استفاده از شناسه برنامه را خواناتر می کند. اما راهنمای using راه کوتاهتری برای ارجاع به اعضای فضای اسمی را فراهم می کند.


مثال. استفاده از راهنمای using .

namespace A {
   int i;   // A::i
   int j;   // A::j
}
namespace B {
   int i;   // B::i
}
using namespace A;
void h() {
   i++;        // A::i++
   B::i++;   // B::i++
   j++;        // A::j++
}


نکته. اگر متغير محلی هم نام با متغير فضای اسمی باشد متغير فضای اسمی مخفی می شود.
نکته. داشتن يک متغير فضای اسمی همنام با متغير سراسری خطا دارد.
نکته. راهنمای using را در ابتدای متن برنامه قرار دهيد.


فضای اسمی std

کليه کتابخانه های استاندارد ++C درون يک فضای اسمی قرارداده شده اند که std نام دارد. ممکن است راهنمای زير را در خيلی از برنامه ها مشاهده کنيد. اين خط بيان می کند که می خواهيد کليه عناصر درون فضای اسمی std در اختيار قرار بگيرد.

using namespace std;

بين فضای اسمی و نحوه ضميمه کردن فايل هدر ارتباط وجود دارد. اگر فايل هدر را به شکل include# <iostream.h> با پسوند .h ضميمه کنيد به معنی دو خط زير خواهد بود:

#include <iostream>
using namespace std;

اگر پسوند .h صريحا ذکر نشود راهنمای using را بايد اضافه کنيد.


مثال. استفاده از cout.

#include <iostream>
int main() {
   std::cout << "Hello ";
   using namespace std;
   cout << "World." << endl;
}


کلاس های حافظه

کلاس حافظه (storage class) تعيين می کند چگونه حافظه به متغيرها توسط کامپايلر واگذار شود. فضای حافظه برای ذخيره متغيرها و توابع و طول عمر اين فضا در طول اجرای برنامه توسط کلاس حافظه مشخص می شود.

محل ذخيره و محدوده متغيرها بسته به محلی (که می تواند داخل يا خارج بدنه تابع باشد) دارد که تعريف می شوند. وقتی پيش فرض های ذخيره سازی شما را راضی نمی کنند می توانيد از کلاس های حافظه استفاده کنيد.

کلاس های حافظه در C و ++C عبارتند از:

auto •
register •
static •
extern •
typedef •
mutable •
(تنها در ++C)

auto

اگر کلمه کليدی auto را قبل از اعلان متغيری قرار دهيد صريحا نوع ذخيره سازی اتوماتيک (automatic storage) را برای متغير مدنظر گرفته ايد. متغيری که ذخيره سازی اتوماتيک دارد با خروج از بلاکی که در آن تعريف شده پاک می شود. حافظه زمان ورود به بلاک اختصاص و زمان خروج از آن آزاد می شود.


مثال. اعلان صريح يک متغير اتوماتيک.

auto int i;


دسترسی به متغير از طريق اسم آن است. بخشی از برنامه است که نام متغير در آن قابل رويت است ميدان يا حوزه (scope) ناميده می شود.

ميدان عمل متغيرهای اتوماتيک درون بلاکی است که در آن اعلان شده اند و خارج از آن قابل رويت نيستند. به همين دليل به آنها متغيرهای محلی نيز گفته می شود.

کلاس auto برای متغيرهای محلی و درون بلاک نوع پيش فرض است و نيازی به ذکر کلمه auto نيست.

نکته. کلاس auto را می توانيد برای متغيرهای درون يک بلاک يا پارامترهای تابع اعمال کنيد.
نکته. با اشاره گرها می شود به متغيرهای محلی از خارج بلاک دسترسی پيدا کرد.
نکته. اگر متغير اتوماتيک مقداردهی اوليه نشود مقدارش نامعلوم خواهد بود. اگر يک مقدار اوليه تعيين کنيد هر زمان که وارد بلاکی که متغير در آن تعريف شده شود مقدار به متغير تخصيص داده می شود.
نکته. اگر از دستور goto برای پرش به بلوکی استفاده کنيد متغيرهای اتوماتيک مقداردهی نمی شوند.
نکته. اگر متغيری درون تابع بازگشتی اعلان شود در هربار فراخوانی تابع حافظه ايجاد و مقداردهی می شود.

register

ثبات ها حافظه هائی درون خود CPU هستند که داده آنها می تواند به سرعت دسترسی شود. معمولا کامپايلر تعيين می کند چه زمانی چه داده ای بايد در ثبات ذخيره شود. با وجود اين کلاس register اين امکان را به برنامه نويس می دهد که به کامپايلر بگوييد در صورت امکان متغير اتوماتيکی را درون ثبات ذخيره کند.

کامپايلر مجبور به اجرای اين درخواست نيست زيرا اندازه و تعداد ثبات های موجود در سيستم محدود است و تعداد کمی از متغيرها می توانند حقيقتا در ثبات ها قرار گيرند. اگر کامپايلر ثباتی را اختصاص ندهد با متغيرثباتی مانند متغير اتوماتيک برخورد می کند.


مثال. اعلان متغير از کلاس ثبات.

register int var;


نکته. متغيرهای ثباتی نوعی متغير اتوماتيک محسوب می شوند.
نکته. متغيرهای ثبات بايد درون يک بلاک يا به صورت پارامتر تابع اعلان شده باشند.
نکته. ذخيره سازی کلاس ثبات از نوع اتوماتيک است يعنی با ورود به بلاک ايجاد و در انتها از بين می رود.
نکته. متغيرهای ثبات را می توان مقداردهی اوليه داد.
نکته. در C اشاره گر به متغير ثبات نمی توانيد داشته باشيد. اما C++ اجازه می دهد آدرس متغير از نوع ثبات را بگيريد.
نکته. کلاس ثبات را نمی توانيد برای اشيای درون فضای اسمی استفاده کنيد.

static

متغيرهایئی که با کلاس static اعلان می شوند طول عمر ذخيره ايستا (static storage duration) دارند. بدين معنی که حافظه ثابتی از زمان شروع اجرای برنامه به آنها اختصاص داده می شود و تا پايان اجرای برنامه آزاد نمی شود.


مثال. اعلان و مقداردهی متغيری از کلاس ايستا.

static int staticInt = 5;


متغيرهای static به حياط خود حتی بعد از خروج از بلاکی که در آن تعريف شده اند ادامه می دهند. بنابراين مقدار متغير در تابع بين فراخوانی های مکرر همان تابع حفظ می شود.


مثال. فرض کنيد متغير x در تابع ()f اعلان شده است که تا زمان اجرای برنامه باقی می ماند.

#include <iostream.h>
int f(void) {
   static int x = 0;
   x++;
   return x;
}
int main(void) {
   int j;
   for (j = 0; j < 5; j++) {
      cout << "Value of f(): " << f() << endl;
   }
   return 0;
}

چون x از نوع static است هربار صفر نمی شود، بلکه مقدار قبلی خود را در تابع حفظ می کند. خروجی برنامه به صورت زير خواهد بود:

Value of f(): 1
Value of f(): 2
Value of f(): 3
Value of f(): 4
Value of f(): 5


نکته. متغير static درون بلاک تنها يکبار مقداردهی می شود در حاليکه متغير auto هربار که اجرا وارد بلاک شود مقداردهی می شود.
نکته. اگر يک متغير static صريحا مقداردهی نشود مقداراوليه صفر را می گيرد.
نکته. کلاس static مادام العمر هستند اما مشابه auto می توان ميدان آنها را محدود به بلاک کرد.
نکته. کلاس static می تواند برای متغيرهای اتوماتيک و خارجی هم استفاده بشود.

extern

کلمه کليدی extern برای توصيف متغيری است که خارجی تعريف شده است. متغيرهای خارجی بيرون از هر بلاکی درون يک فايل اعلان می شوند و می توانند خارج از فايلی که در آن تعريف شده است استفاده شوند.

با کلاس extern متغيرها و توابعی را می توان تعريف کرد که در چندين فايل قابل دسترسی هستند.


مثال. اگر برنامه شامل چندين فايل منبع file1.cpp، file2.cpp و file3.cpp باشد. متغيری که در فايل file1.cpp تعريف است در دو فايل ديگر همراه با کلمه extern بايد ذکر شود تا قابل دسترسی باشد.


حافظه چنين متغيرهائی زمان شروع اجرای برنامه اختصاص داده می شود و تا انتهای برنامه باقی می ماند. ميدان متغيرهای خارجی سراسری است يعنی در کل متن فايل بعد از خطی که اعلان شده اند قابل دسترسی هستند.

متغيرهای خارجی ممکن مانند متغيرهای اتوماتيک با يک مقدار ثابت مقداردهی شوند. مقداردهی در زمان کامپايل تنها يکبار وقتی حافظه اختصاص داده می شود انجام می شود.

چون هر تابعی در برنامه می تواند به متغير خارجی دسترسی پيدا کند اشکال زدائی برنامه سخت تر خواهد شد. به همين علت شايد بهتر باشد از متغيرهای خارجی استفاده نشود. اما گاهی متغيرهای خارجی باعث ساده تر شدن پياده سازی الگوريتم می شود.

نکته. در اکثر کامپايلرها مقداراوليه صفر به متغيرهای خارجی داده می شود.
نکته. اگر تابعی متغيرمحلی هم نام با خارجی داشته باشد درون تابع ارجاع به متغير محلی خواهد بود.
نکته. در عمل بهتر است کليه اعلانات extern در يک فايل هدر جمع شده و فايل با راهنمای include# ضميمه شود.
نکته. در ++C اعلان خارجی نمی تواند در داخل کلاس ظاهر شود.

typedef

اعلان typedef به شما اجازه می دهد شناسه های خودتان را تعريف کنيد که می توانند جای نوع داده نظير int يا float را بگيرند. يک اعلان typedef محلی برای ذخيره نمی گيرد و نوع داده جديدی نيست بلکه مترادفی برای انواع داده يا ترکيبی از آنهاست.

Typedef بخاطر شباهت های گرامری در دسته شاخص های کلاس حافظه گروه بندی می شود.


مثال. عبارت زير LENGTH را مترادف با int اعلان می کند.

typedef int LENGTH;
LENGTH length, width, height;

اعلان زير معادل مثال قبل است:

int length, width, height;

مثال. اعلان يک ساختمان توسط typedef. متغيرهای chicken، cow، horse و whale از نوع ساختمان WEIGHT تعريف شده اند.

typedef struct {
   int scruples;
   int drams;
   int grains;
   } WEIGHT;
WEIGHT chicken, cow, horse, whale;

مثال. يک کلاس توسط typedef بدون نام تعريف شده و يک نام مستعار Trees را گرفته. چنين کلاس هائی نمی توانند سازنده و مخرب داشته باشند. ()Trees نمی تواند سازنده برای کلاس باشد.

typedef class {
   Trees();
   } Trees;


mutable

کلاس ذخيره mutable تنها روی اعضای داده ای يک کلاس می تواند اعمال شود و باعث می شود اين اعضا حتی اگر در کلاس به صورت const تعريف شده اند قابل تغيير باشند.


مثال. در اين برنامه کامپايلر اجازه نمی دهد انتساب var2.y=2345 انجام بشود زيرا var2 به صورت يک ثابت تعريف شده است. اما var2.x=345 اجرا می شود چون A::x به صورت mutable اعلان شده است.

class A {
   public:
      A() : x(4), y(5) { };
      mutable int x;
      int y;
};
int main() {
   const A var2;
   var2.x = 345;
   // var2.y = 2345;
}

نکته. کلاس mutable را نمی توانيد همراه با static يا const اعلان کنيد.



 


صفحه اصلی| PDF| درباره| تماس