#بیشتر_بدانید :
struct S1 {
bool a;
int b;
char c[100];
};
struct S2 {
char c[100];
int b;
bool a;
};
سرعت تغییر/دسترسی متغیر عضو هر شی از نوع S1 از هرشی از نوع S2 بیشتر است
struct S1 {
bool a;
int b;
char c[100];
};
struct S2 {
char c[100];
int b;
bool a;
};
سرعت تغییر/دسترسی متغیر عضو هر شی از نوع S1 از هرشی از نوع S2 بیشتر است
#بیشتر_بدانید
در این پست قصد دارم یه چیزی به دانش کسانی که اطلاع ندارن اضافه کنم که شاید یه روزی یه جایی بدردشون بخوره
حتما دیگه همتون با فایل ها آشنایی دارید
fstream outfile("filename",ios::out|ios::binary)
شاید تا حالا سوال براتون پیش اومده باشه که ios::out|ios::binary چه شکلی کار میکنه
این بار این رو میخوام به کسایی که نمیدونن خیلی خلاصه یاد بدم
اول باینری:
باید با باینری و عمل گر هاش آشنایی داشته باشین وگرنه با دیدن این کد ها به چیزی پی نمی برین (پس برید یاد بگیرید)
یه مثال
5 = 00000101
خب حالا چرا از اینios::out|ios::binary به جای bool استفاده میشه
برای استفاده بهتر اگه مثلا تعداد bool های توی یه کلاستون بیش از سه چهارتا بود بهتره به جای اینکه bool های جدا تعریف کنین یا آرایه از bool ها تعریف کنین یه متغیر تعریف کنین و بیت هاش رو به عنوان یه bool در نظر بگیرید(دلیلش هم حافظه هست وگرنه اگه میخواین به همون روش عمل کنین | عمل کنین . اشکالی نداره😄)
اعداد ضرب در دو که برای دو دویی بدردمون میخوره
1 و2 و4 و8 و16 و32 و64 و128 و256 و 512 و1024 و2048 و4096 و 8192 و ...
که اعداد معادل فقط یه بیت 1 هستن
مثلا برای مثال پایین من یه متغیر char در نظر میگرم چون تعداد bool هایی که در نظر دارم کمتر از 7 تا هست
class MyClass
{
public:
char My_Class_Flags=0;
};
حالا چند تا فلگ براش در نظر میگیرم
small = بیت اول
medium = بیت دوم
big = بیت سوم
cold = بیت چهارم
hot = بیت پنجم
حالا بیایین کلاسی که با همین ها با استفاده از bool کار میکنه رو بهتون نشون بدم
class MyClass
{
public:
bool small,medium,big,cold,hot;
};
😕
بریم همون با فلگ کار کنیم مثلا بهش خاصیت cold , medium رو بصورت همزمان بهش بدیم
int main()
{
MyClass a;
a.My_Class_Flags|=2;
a.My_Class_Flags|=8;
if(a.My_Class_Flags&1)
cout«"It's Small!"«endl;
if(a.My_Class_Flags&2)
cout«"It's Medium!"«endl;
if(a.My_Class_Flags&4)
cout«"It's Big!"«endl;
if(a.My_Class_Flags&8)
cout«"It's Cold!"«endl;
if(a.My_Class_Flags&16)
cout«"It's Hot!"«endl;
return 0;
}
به همین راحتی
حالا بیایید کار رو یخورده آسون تر کنیم
میدونم همتون بهتر از من کار enum رو بلد هستید😀😉
حالا تنها کاری که باید انجام بدیم اینه که مقادیر رو به enum بدیم تا اسم داشته باشن و خوانایی کد بالاتر بره
مثلا این شکلی
enum MyClassFeature
{
small = 1,medium=2,big=4,cold =8,hot = 16
};
حالا بیایین کد قبلیمون رو قشنگترش کنیم😊
int main()
{
MyClass a;
a.My_Class_Flags|=MyClassFeature::medium;
a.My_Class_Flags|=MyClassFeature::cold;
if(a.My_Class_Flags&MyClassFeature::small)
cout«"It's Small!"«endl;
if(a.My_Class_Flags&MyClassFeature::medium)
cout«"It's Medium!"«endl;
if(a.My_Class_Flags&MyClassFeature::big)
cout«"It's Big!"«endl;
if(a.My_Class_Flags&MyClassFeature::cold)
cout«"It's Cold!"«endl;
if(a.My_Class_Flags&MyClassFeature::hot)
cout«"It's Hot!"«endl;
return 0;
}
به همین راحتی
حالا سوال پیش میاد ما باید همه ی اون عدد ها رو بلد باشیم
جواب مسلما نه هست
استفاده از اعداد در مبنای 16 بهتر هست و خب همه حرفه ای ها اون رو اینجور مواقع ترجیح میدن
این شکل صحیح ترش
enum MyClassFeature
{
small = 0x01,medium=0x02,big=0x04,cold =0x08,hot = 0x10
};
حالا میتونیم یه تابع هم تعریف کنیم برای چک کردن مثلا:
class MyClass
{
public:
char My_Class_Flags=0;
bool isit(MyClassFeature in)
{
return My_Class_Flags∈
}
};
حالا همون کدمون یخورده خوشگل ترش😜
int main()
{
MyClass a;
a.My_Class_Flags|=MyClassFeature::medium|MyClassFeature::cold;//مثل همون فایل
if(a.isit(MyClassFeature::small))
cout«"It's Small!"«endl;
if(a.isit(MyClassFeature::medium))
cout«"It's Medium!"«endl;
if(a.isit(MyClassFeature::big))
cout«"It's Big!"«endl;
if(a.isit(MyClassFeature::cold))
cout«"It's Cold!"«endl;
if(a.isit(MyClassFeature::hot))
cout«"It's Hot!"«endl;
return 0;
}
درست مثل iosشد
میتونین ویژگی های بیشتر هم کنارش بذارین و مقادیر مثال رو به دلخواه خودتون تغییر بدید
در این پست قصد دارم یه چیزی به دانش کسانی که اطلاع ندارن اضافه کنم که شاید یه روزی یه جایی بدردشون بخوره
حتما دیگه همتون با فایل ها آشنایی دارید
fstream outfile("filename",ios::out|ios::binary)
شاید تا حالا سوال براتون پیش اومده باشه که ios::out|ios::binary چه شکلی کار میکنه
این بار این رو میخوام به کسایی که نمیدونن خیلی خلاصه یاد بدم
اول باینری:
باید با باینری و عمل گر هاش آشنایی داشته باشین وگرنه با دیدن این کد ها به چیزی پی نمی برین (پس برید یاد بگیرید)
یه مثال
5 = 00000101
خب حالا چرا از اینios::out|ios::binary به جای bool استفاده میشه
برای استفاده بهتر اگه مثلا تعداد bool های توی یه کلاستون بیش از سه چهارتا بود بهتره به جای اینکه bool های جدا تعریف کنین یا آرایه از bool ها تعریف کنین یه متغیر تعریف کنین و بیت هاش رو به عنوان یه bool در نظر بگیرید(دلیلش هم حافظه هست وگرنه اگه میخواین به همون روش عمل کنین | عمل کنین . اشکالی نداره😄)
اعداد ضرب در دو که برای دو دویی بدردمون میخوره
1 و2 و4 و8 و16 و32 و64 و128 و256 و 512 و1024 و2048 و4096 و 8192 و ...
که اعداد معادل فقط یه بیت 1 هستن
مثلا برای مثال پایین من یه متغیر char در نظر میگرم چون تعداد bool هایی که در نظر دارم کمتر از 7 تا هست
class MyClass
{
public:
char My_Class_Flags=0;
};
حالا چند تا فلگ براش در نظر میگیرم
small = بیت اول
medium = بیت دوم
big = بیت سوم
cold = بیت چهارم
hot = بیت پنجم
حالا بیایین کلاسی که با همین ها با استفاده از bool کار میکنه رو بهتون نشون بدم
class MyClass
{
public:
bool small,medium,big,cold,hot;
};
😕
بریم همون با فلگ کار کنیم مثلا بهش خاصیت cold , medium رو بصورت همزمان بهش بدیم
int main()
{
MyClass a;
a.My_Class_Flags|=2;
a.My_Class_Flags|=8;
if(a.My_Class_Flags&1)
cout«"It's Small!"«endl;
if(a.My_Class_Flags&2)
cout«"It's Medium!"«endl;
if(a.My_Class_Flags&4)
cout«"It's Big!"«endl;
if(a.My_Class_Flags&8)
cout«"It's Cold!"«endl;
if(a.My_Class_Flags&16)
cout«"It's Hot!"«endl;
return 0;
}
به همین راحتی
حالا بیایید کار رو یخورده آسون تر کنیم
میدونم همتون بهتر از من کار enum رو بلد هستید😀😉
حالا تنها کاری که باید انجام بدیم اینه که مقادیر رو به enum بدیم تا اسم داشته باشن و خوانایی کد بالاتر بره
مثلا این شکلی
enum MyClassFeature
{
small = 1,medium=2,big=4,cold =8,hot = 16
};
حالا بیایین کد قبلیمون رو قشنگترش کنیم😊
int main()
{
MyClass a;
a.My_Class_Flags|=MyClassFeature::medium;
a.My_Class_Flags|=MyClassFeature::cold;
if(a.My_Class_Flags&MyClassFeature::small)
cout«"It's Small!"«endl;
if(a.My_Class_Flags&MyClassFeature::medium)
cout«"It's Medium!"«endl;
if(a.My_Class_Flags&MyClassFeature::big)
cout«"It's Big!"«endl;
if(a.My_Class_Flags&MyClassFeature::cold)
cout«"It's Cold!"«endl;
if(a.My_Class_Flags&MyClassFeature::hot)
cout«"It's Hot!"«endl;
return 0;
}
به همین راحتی
حالا سوال پیش میاد ما باید همه ی اون عدد ها رو بلد باشیم
جواب مسلما نه هست
استفاده از اعداد در مبنای 16 بهتر هست و خب همه حرفه ای ها اون رو اینجور مواقع ترجیح میدن
این شکل صحیح ترش
enum MyClassFeature
{
small = 0x01,medium=0x02,big=0x04,cold =0x08,hot = 0x10
};
حالا میتونیم یه تابع هم تعریف کنیم برای چک کردن مثلا:
class MyClass
{
public:
char My_Class_Flags=0;
bool isit(MyClassFeature in)
{
return My_Class_Flags∈
}
};
حالا همون کدمون یخورده خوشگل ترش😜
int main()
{
MyClass a;
a.My_Class_Flags|=MyClassFeature::medium|MyClassFeature::cold;//مثل همون فایل
if(a.isit(MyClassFeature::small))
cout«"It's Small!"«endl;
if(a.isit(MyClassFeature::medium))
cout«"It's Medium!"«endl;
if(a.isit(MyClassFeature::big))
cout«"It's Big!"«endl;
if(a.isit(MyClassFeature::cold))
cout«"It's Cold!"«endl;
if(a.isit(MyClassFeature::hot))
cout«"It's Hot!"«endl;
return 0;
}
درست مثل iosشد
میتونین ویژگی های بیشتر هم کنارش بذارین و مقادیر مثال رو به دلخواه خودتون تغییر بدید
#بیشتر_بدانید
در این پست قصد دارم یه چیزی به دانش کسانی که اطلاع ندارن اضافه کنم که شاید یه روزی یه جایی بدردشون بخوره
کدی به شکل زیر داریم:
int main()
{
MyArray myarray;
myarray.a0 = 10;
myarray.a[1] = 20;
cout«myarray.a[0]«endl«myarray.a1«endl;
myarray.a1=100;
myarray.a[0]=50;
cout«myarray.a[1]«endl«myarray.a0«endl;
return 0;
}
و خروجی
10
20
100
50
نکته اصلی در کد بالا اینه که a0 با a[0] هیچ فرقی نداره
دستکاری تو یکیش معادل دستکاری تو اون یکی هست
همینطور a1 با a[1]
یه روش:
struct MyArray{
int a[2];
int & a0 = a[0];
int &a1=a[1];
};
و یه روش دیگر با استفاده از union:
struct MyArray
{
union{
struct{int a0,a1;};
int a[2];
};
};
به همین راحتی اعضای آرایه و اعضای متغیر همه با هم از یک حافظه استفاده میکنن و نکته اصلی که من دنبالش بودم همین بود .(اشتراک اعضای یک آرایه با متغیر های مختلف)
در این پست قصد دارم یه چیزی به دانش کسانی که اطلاع ندارن اضافه کنم که شاید یه روزی یه جایی بدردشون بخوره
کدی به شکل زیر داریم:
int main()
{
MyArray myarray;
myarray.a0 = 10;
myarray.a[1] = 20;
cout«myarray.a[0]«endl«myarray.a1«endl;
myarray.a1=100;
myarray.a[0]=50;
cout«myarray.a[1]«endl«myarray.a0«endl;
return 0;
}
و خروجی
10
20
100
50
نکته اصلی در کد بالا اینه که a0 با a[0] هیچ فرقی نداره
دستکاری تو یکیش معادل دستکاری تو اون یکی هست
همینطور a1 با a[1]
یه روش:
struct MyArray{
int a[2];
int & a0 = a[0];
int &a1=a[1];
};
و یه روش دیگر با استفاده از union:
struct MyArray
{
union{
struct{int a0,a1;};
int a[2];
};
};
به همین راحتی اعضای آرایه و اعضای متغیر همه با هم از یک حافظه استفاده میکنن و نکته اصلی که من دنبالش بودم همین بود .(اشتراک اعضای یک آرایه با متغیر های مختلف)
#بیشتر_بدانید
در پست بالا یه چیزهایی گفتم که جایگزینشون کنید با bool ها در کلاس
و من میخوام با چندتا کد ساده قانعتون کنم چرا bool در این گونه مواقع جالب نیست و چرا بهتره از این روش استفاده کرد
class MyClassOptimized
{
public:
char My_Class_Flags=0;
};
class MyClassNonOptimized
{
public:
bool small,medium,big,cold,hot;
};
این کد دوم واقعا زباله محسوب میشه چرا؟
از طریق کد زیر پی میبریم
cout«sizeof(MyClassNonOptimized)«endl;
cout«sizeof(MyClassOptimized)«endl;
خب حالا بگید که مقدار زیادی نیست ولی هست.😏
MyClassNonOptimized a[10000];
MyClassOptimized b[10000];
cout«sizeof(a)«endl;
cout«sizeof(b)«endl;
در پست بالا یه چیزهایی گفتم که جایگزینشون کنید با bool ها در کلاس
و من میخوام با چندتا کد ساده قانعتون کنم چرا bool در این گونه مواقع جالب نیست و چرا بهتره از این روش استفاده کرد
class MyClassOptimized
{
public:
char My_Class_Flags=0;
};
class MyClassNonOptimized
{
public:
bool small,medium,big,cold,hot;
};
این کد دوم واقعا زباله محسوب میشه چرا؟
از طریق کد زیر پی میبریم
cout«sizeof(MyClassNonOptimized)«endl;
cout«sizeof(MyClassOptimized)«endl;
خب حالا بگید که مقدار زیادی نیست ولی هست.😏
MyClassNonOptimized a[10000];
MyClassOptimized b[10000];
cout«sizeof(a)«endl;
cout«sizeof(b)«endl;
#بیشتر_بدانید
در این پست قصد دارم یه چیزی به دانش کسانی که اطلاع ندارن اضافه کنم که شاید یه روزی یه جایی بدردشون بخوره
دوتا تابع/متغیر داریم . یکی اسمش hello1 و دیگری hello2 که نوع هیچکدومشون مشخص نیست
با استفاده از سه روش زیر پی ببرید که کدوم تابعه و کدوم متغیر
این هم مثال های مثال همین طوری شانسی
float hello1(int a,float b)
{
}
int hello2;
روش اول:
خواص
generic programming
کد
template <typename T>
struct A{
const static bool isvar=true;
const static bool isfunc=false;
};
template <typename RT,typename ...Args>
struct A<RT(Args...)>{
const static bool isvar=false;
const static bool isfunc=true;
};
و
if(A<decltype(hello1)>::isfunc)
cout«"hello1 is function"«endl;
else if(A<decltype(hello1)>::isvar)
cout«"hello1 is var"«endl;
if(A<decltype(hello2)>::isfunc)
cout«"hello2 is function"«endl;
else if(A<decltype(hello2)>::isvar)
cout«"hello2 is var"«endl«endl;
روش دوم :
فرستادن به یک تابع اور لود شده
template<typename T>
bool isvar(T in)
{
return true;
}
template<typename RT,typename ...Args>
bool isvar(RT(*f)(Args...))
{
return false;
}
این تابع اگه متغیر بود صحیح بر میگردونه و تابع بود غلط
بر عکسش هم میشد
if(!isvar(hello1))
cout«"hello1 is function"«endl;
else
cout«"hello1 is var"«endl;
if(!isvar(hello2))
cout«"hello2 is function"«endl;
else
cout«"hello2 is var"«endl«endl;
و روش سوم:
تایپ آی دی
همه چی آماده هست ولی کارکردش تضمینی نیست
if(string(typeid(hello1).name()).find("F")==0)
cout«"hello1 is function"«endl;
else
cout«"hello1 is var"«endl;
if(string(typeid(hello2).name()).find("F")==0)
cout«"hello2 is function"«endl«endl;
else
cout«"hello2 is var"«endl;
در این پست قصد دارم یه چیزی به دانش کسانی که اطلاع ندارن اضافه کنم که شاید یه روزی یه جایی بدردشون بخوره
دوتا تابع/متغیر داریم . یکی اسمش hello1 و دیگری hello2 که نوع هیچکدومشون مشخص نیست
با استفاده از سه روش زیر پی ببرید که کدوم تابعه و کدوم متغیر
این هم مثال های مثال همین طوری شانسی
float hello1(int a,float b)
{
}
int hello2;
روش اول:
خواص
generic programming
کد
template <typename T>
struct A{
const static bool isvar=true;
const static bool isfunc=false;
};
template <typename RT,typename ...Args>
struct A<RT(Args...)>{
const static bool isvar=false;
const static bool isfunc=true;
};
و
if(A<decltype(hello1)>::isfunc)
cout«"hello1 is function"«endl;
else if(A<decltype(hello1)>::isvar)
cout«"hello1 is var"«endl;
if(A<decltype(hello2)>::isfunc)
cout«"hello2 is function"«endl;
else if(A<decltype(hello2)>::isvar)
cout«"hello2 is var"«endl«endl;
روش دوم :
فرستادن به یک تابع اور لود شده
template<typename T>
bool isvar(T in)
{
return true;
}
template<typename RT,typename ...Args>
bool isvar(RT(*f)(Args...))
{
return false;
}
این تابع اگه متغیر بود صحیح بر میگردونه و تابع بود غلط
بر عکسش هم میشد
if(!isvar(hello1))
cout«"hello1 is function"«endl;
else
cout«"hello1 is var"«endl;
if(!isvar(hello2))
cout«"hello2 is function"«endl;
else
cout«"hello2 is var"«endl«endl;
و روش سوم:
تایپ آی دی
همه چی آماده هست ولی کارکردش تضمینی نیست
if(string(typeid(hello1).name()).find("F")==0)
cout«"hello1 is function"«endl;
else
cout«"hello1 is var"«endl;
if(string(typeid(hello2).name()).find("F")==0)
cout«"hello2 is function"«endl«endl;
else
cout«"hello2 is var"«endl;
#آموزش ابتدایی دستورات پیش پردازنده و ماکرو ها
اول از همه اشاره کنم تمام پیش پردازنده ها هنگام کامپایل عمل میکنن نه هنگام اجرای برنامه
پس هیچوقت اون ها رو با متغیر های معمولی . توابع معمولی اشتباه نگیرید
تعدادی از خاصیت های اونها
کاهش حجم کدنویسی
استفاده به عنوان قالب
تعریف عمل کرد های مختلف
تعیین راحت تر رفتارهای مختلف با توجه به سیستم عامل یا کامپایلر
...
#include
خودتون از من هم بهتر بلدید
کارش اینه که محتویات یه فایل(هدر) رو به کدتون اضافه میکنه
مثال
اگر محتویات
header.h
این باشه
void helloworld();
و فایل
cpp
این باشه
#include “header.h”
int main(){
return 0;
}
مثل اینه که بنویسید
void helloworld();
int main(){
return 0;
}
عیننا کپی ش میکنه
#define نام
با استفاده از این میتونیم
یه ثابت داشته باشیم
که مقداری نداره
پس اگه هر جای کد اون رو بذارید مثل این میمونه چیزی اضافه نکردید
#define amir
و داخل کد
cout«”Hello!”«endl; amir
امیر رو اون انتها گذاشتم و انگار نه انگار
#define مقدار نام
این فرقش با بالایی اینه که هر جای کد بذارین
چیزی که جلوش هست رو مثل یه قالب میذاره همون جا
مثال:
#define pi 3.1415
cout«pi«endl;
حالا چه اتفاقی میفته
موقع کامپایل وقتی کامپایلر اون رو می بینه میفهمه که به جای پی باید مقدار جلوش رو جایگزین کنه
و این دقیقا مثل این میمونه که بنویسید
cout«3.1415«endl;
#define Fname(Args...) blo blo
ماکروهای شبیه تابع
باز هم میگم این کاملا فرق داره با توابع معمولی و هنگام کامپایل مثل قالب عمل میکنه
تنها کاری که میکنن اینه که با توجه به چیزهایی که بهشون میدید کارهای مختلف انجام میدن
یه مثال
#define MYMACRO() cout«
حالا کد
یادتون باشه این آرگومان نداره پس میتونید این کار رو مثل حالت بالا مثل عدد پی تعریف کنید
کد
MYMACRO() "Hello";
زیاد نترسید
هیچ چیز ترسناکی نیست
مثل حالت بالا کامپایلر محتویات جلوی اون ماکرو رو جایگزین میکنه
و دقیقا مثل اینه که بنویسید
cout«”Hello”«endl;
یه مثال دیگه
#define MYMACRO(A1) cout«A1«endl;
تو مثال بالا
A1
آرگومانش هست
تنها کاری که باید انجام میده اینه
شما بعدا هر چیزی رو و باز تاکید میکنم هر چیزی رو که به عنوان آرگومان بهش بدید عیننا اون رو داخل محتویات قرار میده
مثال کد
MYMACRO(25)
حتی لازم نیست سمی کالن بذارین
چون همون بالا یدونه براش تعریف کردیم
البته اگه بذارید هم اشکالی نداره چون سمی کالن های بیخودی پشت سرهم اثر ندارن
این کد مثل دقیقا مثل اینه که بنویسید
cout«25«endl;
حالا سایر دستورات
#ifdef
این دستور مشخص میکنه آیا بالای اون چیزی رو تعریف کردیم یا نه
یادتون نره حتما بالاش
چون کامپایلر از بالا به پایین کد رو کامپایل میکنه
ترتیب بالا و پایین خیلی مهمه
پایان این دستور وتمام حالت های شرطی با
#endif
مشخص میشه
یه مثال سطحی
#ifdef L
این قسمت رو به کدام اضافه کن
#endif
این یعنی اگر
L
تعریف شده بود
اون قسمت توی کد هامون موجود باشه
وگرنه موجود نباشه و کامپایل نشه
یه مثال
#ifdef L
void helloworld()
{
cout«"Hello World!"«endl;
}
#endif
سعی کنید تابع رو فراخوانی کنید
میبینید که نمیشه
فقط کافیه
L
رو بالاش تعریف کنین
مثلا
#define L
به همین راحتی مشخص میکنید داخل شرط به کدهاتون برای کامپایل اضافه شه.
دیگر ماکرو ها
#ifndef
یعنی اگر تعریف نشده بود اون قسمت تا
endif
کامپایل شه
#else
شبیه
else
هست یعنی اگر شرط برقرار بود که هیچ
درغیر اینصورت این قسمت به خروجی اضافه شه
پایانش هم با endif
مشخص میشه
مثال
#ifdef world
void helloworld()
{
cout«"Hello World!"«endl;
}
#else
void hellocity()
{
cout«"Hello city!"«endl;
}
#endif // city
این یعنی اگر
world
تعریف شده بود اون قسمت کامپایل شه
در غیر اینصورت
اون یکی قسمت کامپایل بشه
سعی کنید تعریف ها رو اضافه کنید و تست کنید
نکته اینه که
#else
برای سایر شروط هم عمل میکنه
#if
این یعنی اگر شرطی برقرار بود کامپایل شه
مثال
#if value==10
void helloworld()
{
cout«"Hello World!"«endl;
}
#endif // world
این یعنی اگه
value
تعریف شده بود و مقدار مساوی10 بود انوقت اون قسمت کامپایل شه
#elif
مثل
else if
میمونه
مثال
#if value==10
void helloworld()
{
cout«"Hello World!"«endl;
}
#elif value==20
void hellocity()
{
cout«"Hello city!"«endl;
}
#endif // world
این یعنی اگه مقدار برابر 10 بود اون قسمت اضافه شه در غیر اینصورت اگر مساوی بیست بود اون یکی قسمت اضافه شه
یه مقدار بدید و تست کنید حتما
#define value 10
مثلا
#error
این یعنی با استفاده از اون شرط ها میتونیم ارور درست کنیم
مثال
#if value!=10
#error value is not 10!
#endif // world
اول از همه اشاره کنم تمام پیش پردازنده ها هنگام کامپایل عمل میکنن نه هنگام اجرای برنامه
پس هیچوقت اون ها رو با متغیر های معمولی . توابع معمولی اشتباه نگیرید
تعدادی از خاصیت های اونها
کاهش حجم کدنویسی
استفاده به عنوان قالب
تعریف عمل کرد های مختلف
تعیین راحت تر رفتارهای مختلف با توجه به سیستم عامل یا کامپایلر
...
#include
خودتون از من هم بهتر بلدید
کارش اینه که محتویات یه فایل(هدر) رو به کدتون اضافه میکنه
مثال
اگر محتویات
header.h
این باشه
void helloworld();
و فایل
cpp
این باشه
#include “header.h”
int main(){
return 0;
}
مثل اینه که بنویسید
void helloworld();
int main(){
return 0;
}
عیننا کپی ش میکنه
#define نام
با استفاده از این میتونیم
یه ثابت داشته باشیم
که مقداری نداره
پس اگه هر جای کد اون رو بذارید مثل این میمونه چیزی اضافه نکردید
#define amir
و داخل کد
cout«”Hello!”«endl; amir
امیر رو اون انتها گذاشتم و انگار نه انگار
#define مقدار نام
این فرقش با بالایی اینه که هر جای کد بذارین
چیزی که جلوش هست رو مثل یه قالب میذاره همون جا
مثال:
#define pi 3.1415
cout«pi«endl;
حالا چه اتفاقی میفته
موقع کامپایل وقتی کامپایلر اون رو می بینه میفهمه که به جای پی باید مقدار جلوش رو جایگزین کنه
و این دقیقا مثل این میمونه که بنویسید
cout«3.1415«endl;
#define Fname(Args...) blo blo
ماکروهای شبیه تابع
باز هم میگم این کاملا فرق داره با توابع معمولی و هنگام کامپایل مثل قالب عمل میکنه
تنها کاری که میکنن اینه که با توجه به چیزهایی که بهشون میدید کارهای مختلف انجام میدن
یه مثال
#define MYMACRO() cout«
حالا کد
یادتون باشه این آرگومان نداره پس میتونید این کار رو مثل حالت بالا مثل عدد پی تعریف کنید
کد
MYMACRO() "Hello";
زیاد نترسید
هیچ چیز ترسناکی نیست
مثل حالت بالا کامپایلر محتویات جلوی اون ماکرو رو جایگزین میکنه
و دقیقا مثل اینه که بنویسید
cout«”Hello”«endl;
یه مثال دیگه
#define MYMACRO(A1) cout«A1«endl;
تو مثال بالا
A1
آرگومانش هست
تنها کاری که باید انجام میده اینه
شما بعدا هر چیزی رو و باز تاکید میکنم هر چیزی رو که به عنوان آرگومان بهش بدید عیننا اون رو داخل محتویات قرار میده
مثال کد
MYMACRO(25)
حتی لازم نیست سمی کالن بذارین
چون همون بالا یدونه براش تعریف کردیم
البته اگه بذارید هم اشکالی نداره چون سمی کالن های بیخودی پشت سرهم اثر ندارن
این کد مثل دقیقا مثل اینه که بنویسید
cout«25«endl;
حالا سایر دستورات
#ifdef
این دستور مشخص میکنه آیا بالای اون چیزی رو تعریف کردیم یا نه
یادتون نره حتما بالاش
چون کامپایلر از بالا به پایین کد رو کامپایل میکنه
ترتیب بالا و پایین خیلی مهمه
پایان این دستور وتمام حالت های شرطی با
#endif
مشخص میشه
یه مثال سطحی
#ifdef L
این قسمت رو به کدام اضافه کن
#endif
این یعنی اگر
L
تعریف شده بود
اون قسمت توی کد هامون موجود باشه
وگرنه موجود نباشه و کامپایل نشه
یه مثال
#ifdef L
void helloworld()
{
cout«"Hello World!"«endl;
}
#endif
سعی کنید تابع رو فراخوانی کنید
میبینید که نمیشه
فقط کافیه
L
رو بالاش تعریف کنین
مثلا
#define L
به همین راحتی مشخص میکنید داخل شرط به کدهاتون برای کامپایل اضافه شه.
دیگر ماکرو ها
#ifndef
یعنی اگر تعریف نشده بود اون قسمت تا
endif
کامپایل شه
#else
شبیه
else
هست یعنی اگر شرط برقرار بود که هیچ
درغیر اینصورت این قسمت به خروجی اضافه شه
پایانش هم با endif
مشخص میشه
مثال
#ifdef world
void helloworld()
{
cout«"Hello World!"«endl;
}
#else
void hellocity()
{
cout«"Hello city!"«endl;
}
#endif // city
این یعنی اگر
world
تعریف شده بود اون قسمت کامپایل شه
در غیر اینصورت
اون یکی قسمت کامپایل بشه
سعی کنید تعریف ها رو اضافه کنید و تست کنید
نکته اینه که
#else
برای سایر شروط هم عمل میکنه
#if
این یعنی اگر شرطی برقرار بود کامپایل شه
مثال
#if value==10
void helloworld()
{
cout«"Hello World!"«endl;
}
#endif // world
این یعنی اگه
value
تعریف شده بود و مقدار مساوی10 بود انوقت اون قسمت کامپایل شه
#elif
مثل
else if
میمونه
مثال
#if value==10
void helloworld()
{
cout«"Hello World!"«endl;
}
#elif value==20
void hellocity()
{
cout«"Hello city!"«endl;
}
#endif // world
این یعنی اگه مقدار برابر 10 بود اون قسمت اضافه شه در غیر اینصورت اگر مساوی بیست بود اون یکی قسمت اضافه شه
یه مقدار بدید و تست کنید حتما
#define value 10
مثلا
#error
این یعنی با استفاده از اون شرط ها میتونیم ارور درست کنیم
مثال
#if value!=10
#error value is not 10!
#endif // world
این یعنی اگر مقدار چیزی غیر از 10 باشه
ارور میده
اون چیزی که جلوی اروره پیامیه که نمایش داده میشه
#undef
از بین بردن یک تعریف که از قبل تعریف شده
مثال
#define L
#ifndef L
#error L is not defined
#endif // L
#undef L
#ifndef L
#error L is not defined
#endif // L
در ارور دوم ارور میده
چرا؟
چون
L
تعریفش رو پاک کردیم
اما بعدش اون رو از بین بردیم
همونطور که قبلا گفتم کد از بالا به پایین کامپایل میشه
یه مثال دیگه
#define L 10
#if L==10
void a(){cout«"hello a!"«endl;}
#endif // L
#undef L
#define L 20
#if L==20
void b(){cout«"hello b!"«endl;}
#endif // L
در مثال بالا هر دو کامپایل میشن
نکته
اون
undef
رو هم نذارید فرقی نمیکنه
ولی اگه
#define L 10
#if L==10
void a(){cout«"hello a!"«endl;}
#endif // L
#undef L
#ifndef L
void b(){cout«"hello b!"«endl;}
#endif // L
تو مثال بالا برای قسمت دوم وجودش مهمه
این یه آموزش ساده از تعدادی از دستورات پیش پردازنده بود
ارور میده
اون چیزی که جلوی اروره پیامیه که نمایش داده میشه
#undef
از بین بردن یک تعریف که از قبل تعریف شده
مثال
#define L
#ifndef L
#error L is not defined
#endif // L
#undef L
#ifndef L
#error L is not defined
#endif // L
در ارور دوم ارور میده
چرا؟
چون
L
تعریفش رو پاک کردیم
اما بعدش اون رو از بین بردیم
همونطور که قبلا گفتم کد از بالا به پایین کامپایل میشه
یه مثال دیگه
#define L 10
#if L==10
void a(){cout«"hello a!"«endl;}
#endif // L
#undef L
#define L 20
#if L==20
void b(){cout«"hello b!"«endl;}
#endif // L
در مثال بالا هر دو کامپایل میشن
نکته
اون
undef
رو هم نذارید فرقی نمیکنه
ولی اگه
#define L 10
#if L==10
void a(){cout«"hello a!"«endl;}
#endif // L
#undef L
#ifndef L
void b(){cout«"hello b!"«endl;}
#endif // L
تو مثال بالا برای قسمت دوم وجودش مهمه
این یه آموزش ساده از تعدادی از دستورات پیش پردازنده بود
using namespace Ali;
saybye();
{
using Ashkan::saybye;
saybye();
}
این یعنی داخل اون بلاک فقط از تابع در فضای نام امیر استفاده شه و میگیم که داخل اون بلاک اون استثنا هست
اگه ننویسیم تابع موجود در فضای نام علی اجرا میشه چون خبری از استثنا نیست
اما تفاوتش با
using
using Ashkan::saybye;
saybye();
{
using namespace Ali;
saybye();
}
در مثال بالا همیشه از
saybye
موجود در امیر استفاده میشه
چون بالاتر بهتون گفتم میشه گفت
using
جدی تر گرفته میشه
ولی میشه در بلاک های تو در تو
using
های متفاوت استفاده کرد
پس میشه نقضش کرد
using Ashkan::saybye;
saybye();
{
using Ali::saybye;
saybye();
}
با اینکار تونستیم نقضش کنیم
و حالیش کنیم رییس تو اون بلاک
saybye
علی هست
نه
saybye
امیر
درپایان تنها چیزی که مونده تا بهش اشاره کنم namespace
های تو در تو هست
namespace People
{
namespace Ashkan
{
void sayhello(){cout«"Hello Ashkan!"«endl;}
void saybye(){cout«"Good bye Ashkan!"«endl;}
}
namespace Ali
{
void sayhello(){cout«"Hello Ali!"«endl;}
void saybye(){cout«"Good bye Ali!"«endl;}
}
}
Amir
و
Ali
زیر مجموعه
People
هستن
برای استفاده ازشون حتما باید از نسخه داخلی استفاده کرد
using namespace People::Ashkan;
saybye();
برای دسترسی مستقیم
People::Ashkan::saybye();
این یه آموزش خیلی ابتدایی برای استفاده از
namespace
ها بود
saybye();
{
using Ashkan::saybye;
saybye();
}
این یعنی داخل اون بلاک فقط از تابع در فضای نام امیر استفاده شه و میگیم که داخل اون بلاک اون استثنا هست
اگه ننویسیم تابع موجود در فضای نام علی اجرا میشه چون خبری از استثنا نیست
اما تفاوتش با
using
using Ashkan::saybye;
saybye();
{
using namespace Ali;
saybye();
}
در مثال بالا همیشه از
saybye
موجود در امیر استفاده میشه
چون بالاتر بهتون گفتم میشه گفت
using
جدی تر گرفته میشه
ولی میشه در بلاک های تو در تو
using
های متفاوت استفاده کرد
پس میشه نقضش کرد
using Ashkan::saybye;
saybye();
{
using Ali::saybye;
saybye();
}
با اینکار تونستیم نقضش کنیم
و حالیش کنیم رییس تو اون بلاک
saybye
علی هست
نه
saybye
امیر
درپایان تنها چیزی که مونده تا بهش اشاره کنم namespace
های تو در تو هست
namespace People
{
namespace Ashkan
{
void sayhello(){cout«"Hello Ashkan!"«endl;}
void saybye(){cout«"Good bye Ashkan!"«endl;}
}
namespace Ali
{
void sayhello(){cout«"Hello Ali!"«endl;}
void saybye(){cout«"Good bye Ali!"«endl;}
}
}
Amir
و
Ali
زیر مجموعه
People
هستن
برای استفاده ازشون حتما باید از نسخه داخلی استفاده کرد
using namespace People::Ashkan;
saybye();
برای دسترسی مستقیم
People::Ashkan::saybye();
این یه آموزش خیلی ابتدایی برای استفاده از
namespace
ها بود
#بیشتر_بدانید
در این پست روشی خیلی ساده رو بیان میکنم که میتونید با استفاده از اون براحتی پی ببرید که آیا نوع دو تا متغیر با هم یکی هستن یا نه
برای اینکار فقط کافیه با استفاده از
template
ها یه اورلودینگ ساده انجام بدیم
این کدها با استاندارد سی پلاس پلاس 11 در جی سی سی تست شده هستن
تمپلیت دو نوع میگیره
اولی و دومی و داخل هر دو استراکت یه مقدار استاتیک داریم که مشخص میکنه آیا برابر هستن یانه
template<typename T1,typename T2>
struct issametype
{
const static bool value=0;
};
template<typename T>
struct issametype<T,T>
{
const static bool value=1;
};
یه مثال برای تست کد بالا:
int a;
int b;
double c;
double d;
cout«"a & b is:"«issametype<decltype(a),decltype(b)>::value«endl;
cout«"b & c is:"«issametype<decltype(b),decltype(c)>::value«endl;
cout«"c & d is:"«issametype<decltype(c),decltype(d)>::value«endl;
یک نشون دهنده اینه که یک نوع هستن ولی صفر یعنی نوعشون با هم فرق داره
وقتی که از
auto
استفاده میکنیم این خاصیت خیلی مفیده😄
int a;
auto b =a ;
double c;
auto d = c;
cout«"a & b is:"«issametype<decltype(a),decltype(b)>::value«endl;
cout«"b & c is:"«issametype<decltype(b),decltype(c)>::value«endl;
cout«"c & d is:"«issametype<decltype(c),decltype(d)>::value«endl;
یه باگ کوچولو
فرض کنید که یه نوع المنت آرایه با خود یه متغیر مقایسه شه اونوقت چی میشه
int a;
int b[10];
cout«"a & b is:"«issametype<decltype(a),decltype(b[0])>::value«endl;
میبینید که اشتباه میکنه چون نوع
b[0]
با نوع
a
هیچ فرقی ندارن
ولی خب یه ذره دارن😜
مشکل موقع فراخوانی عضو آرایه هست چون وقتی مینویسیم
b[0]
عضوش به صورت
int&
یعنی رفرنس برمیگرده نه
int
برای همینه که شما میتونید اعضای یه آرایه رو اصلاح کنید(البته میدونم میدونید😄)
برای رفع مشکل باز هم میتونیم بریم سراغ
template
ها و یه استراکت اور لود شده با تملیت هاش
بعد کافیه نوع ورودیش رو داخل به عنوان یه نوع دیگه تعریف کنیم
template<typename T>
struct removeref
{
typedef T type;
};
template<typename T>
struct removeref<T&>
{
typedef T type;
};
یه تست ازش بگیریم ببینیم چه طوره
این در حالت عادی
int b[10];
cout«issametype<decltype(b[0]),int&>::value«endl;
با استفاده از این استراکت میتونیم حالت رفرنس بودنش رو از بین ببریم
cout«issametype<removeref<decltype(b[0])>::type,int&>::value«endl;
دیدید دیگه
int&
نیست 😀
cout«issametype<removeref<decltype(b[0])>::type,int>::value«endl;
اینم مدرک👆🏼
😄
حالا میتونیم اون مثال باگ دار رو تعمیر کنیم
int a;
int b[10];
cout«"a & b is:"«issametype< removeref<decltype(a)>::type,removeref<decltype(b[0])>::type >::value «endl;
اوه یه ذره شلوغ شد
ولی الان دیگه بچه خوبی شده
دیگه باگ نیست😜
cout«"a & b is:"«
issametype<
removeref<decltype(a)>::type,
removeref<decltype(b[0])>::type
>::value
«endl;
موفق باشید
کد هاتون هم بی باگ😉
در این پست روشی خیلی ساده رو بیان میکنم که میتونید با استفاده از اون براحتی پی ببرید که آیا نوع دو تا متغیر با هم یکی هستن یا نه
برای اینکار فقط کافیه با استفاده از
template
ها یه اورلودینگ ساده انجام بدیم
این کدها با استاندارد سی پلاس پلاس 11 در جی سی سی تست شده هستن
تمپلیت دو نوع میگیره
اولی و دومی و داخل هر دو استراکت یه مقدار استاتیک داریم که مشخص میکنه آیا برابر هستن یانه
template<typename T1,typename T2>
struct issametype
{
const static bool value=0;
};
template<typename T>
struct issametype<T,T>
{
const static bool value=1;
};
یه مثال برای تست کد بالا:
int a;
int b;
double c;
double d;
cout«"a & b is:"«issametype<decltype(a),decltype(b)>::value«endl;
cout«"b & c is:"«issametype<decltype(b),decltype(c)>::value«endl;
cout«"c & d is:"«issametype<decltype(c),decltype(d)>::value«endl;
یک نشون دهنده اینه که یک نوع هستن ولی صفر یعنی نوعشون با هم فرق داره
وقتی که از
auto
استفاده میکنیم این خاصیت خیلی مفیده😄
int a;
auto b =a ;
double c;
auto d = c;
cout«"a & b is:"«issametype<decltype(a),decltype(b)>::value«endl;
cout«"b & c is:"«issametype<decltype(b),decltype(c)>::value«endl;
cout«"c & d is:"«issametype<decltype(c),decltype(d)>::value«endl;
یه باگ کوچولو
فرض کنید که یه نوع المنت آرایه با خود یه متغیر مقایسه شه اونوقت چی میشه
int a;
int b[10];
cout«"a & b is:"«issametype<decltype(a),decltype(b[0])>::value«endl;
میبینید که اشتباه میکنه چون نوع
b[0]
با نوع
a
هیچ فرقی ندارن
ولی خب یه ذره دارن😜
مشکل موقع فراخوانی عضو آرایه هست چون وقتی مینویسیم
b[0]
عضوش به صورت
int&
یعنی رفرنس برمیگرده نه
int
برای همینه که شما میتونید اعضای یه آرایه رو اصلاح کنید(البته میدونم میدونید😄)
برای رفع مشکل باز هم میتونیم بریم سراغ
template
ها و یه استراکت اور لود شده با تملیت هاش
بعد کافیه نوع ورودیش رو داخل به عنوان یه نوع دیگه تعریف کنیم
template<typename T>
struct removeref
{
typedef T type;
};
template<typename T>
struct removeref<T&>
{
typedef T type;
};
یه تست ازش بگیریم ببینیم چه طوره
این در حالت عادی
int b[10];
cout«issametype<decltype(b[0]),int&>::value«endl;
با استفاده از این استراکت میتونیم حالت رفرنس بودنش رو از بین ببریم
cout«issametype<removeref<decltype(b[0])>::type,int&>::value«endl;
دیدید دیگه
int&
نیست 😀
cout«issametype<removeref<decltype(b[0])>::type,int>::value«endl;
اینم مدرک👆🏼
😄
حالا میتونیم اون مثال باگ دار رو تعمیر کنیم
int a;
int b[10];
cout«"a & b is:"«issametype< removeref<decltype(a)>::type,removeref<decltype(b[0])>::type >::value «endl;
اوه یه ذره شلوغ شد
ولی الان دیگه بچه خوبی شده
دیگه باگ نیست😜
cout«"a & b is:"«
issametype<
removeref<decltype(a)>::type,
removeref<decltype(b[0])>::type
>::value
«endl;
موفق باشید
کد هاتون هم بی باگ😉
#بیشتر_بدانید
اهمیت هدر گارد ها
header guards
حتما همتون تا حالا هدر گاردهایی مثل بالا رو دیدید
اما اهمیتشون چیه؟
خیلی خلاصه جلوگیری از تعریف چندبار سهوی و ارور های ناخواسته
در مثال زیر خیلی ساده بهتون میگم چرا مفیدن
یه نکته » من برای جلوگیری از تکرار اسم هدر گارد ها از روشی استفاده میکنم که تقریبا اکثر برنامه نویس ها از اون روش استفاده میکنن
که
HEADERNAME
همون نام فایل هدر هست
حالا مثال:
محتویات فایل header2.h
محتویات فایل header1.h
محتویات فایل اصلی سی پلاس پلاس
میبینیدکه ارور نمیده و اجرا میشه
حالا
`
رو به فایل سی پلاس پلاس اضافه کنین
چه قبل اون header1.h بذارید چه بعدش ارور میده چون
دوبار تعریف میشه
حالا بیایید هدرگارد برای هر دو هدر تعریف کنید
محتویات جدید header2.h
محتویات جدید header1.h
و محتوای فایل اصلی
دیگه ارور نمیده
چند بار هم اینکلاد کنم خیالتون راحت شه
امیدوارم پی برده به باشید به اهمیتشون
نکته آخر: نوشته اشکان♻️
در بعضی کامپایلر ها لازم نیست
#pragma once
دقیقا همین کار رو انجام می
بعضی کامپایلر ها
موفق باشید
کدهاتون هم بی باگ
اهمیت هدر گارد ها
header guards
#ifndef HEADER_H
#define HEADER_H
#endif
حتما همتون تا حالا هدر گاردهایی مثل بالا رو دیدید
اما اهمیتشون چیه؟
خیلی خلاصه جلوگیری از تعریف چندبار سهوی و ارور های ناخواسته
در مثال زیر خیلی ساده بهتون میگم چرا مفیدن
یه نکته » من برای جلوگیری از تکرار اسم هدر گارد ها از روشی استفاده میکنم که تقریبا اکثر برنامه نویس ها از اون روش استفاده میکنن
#define HEADERNAME_H
که
HEADERNAME
همون نام فایل هدر هست
حالا مثال:
محتویات فایل header2.h
#include <iostream>
using namespace std;
void helloworld()
{cout<<"Hello World!"<<endl;
}
محتویات فایل header1.h
#include "header2.h"
void sayhello()
{helloworld();
}
محتویات فایل اصلی سی پلاس پلاس
#include "header1.h"
int main(){sayhello();
return 0;
}
میبینیدکه ارور نمیده و اجرا میشه
حالا
#include "header2.h"
`
رو به فایل سی پلاس پلاس اضافه کنین
#include "header1.h"
#include "header2.h"
int main(){sayhello();
return 0;
}
چه قبل اون header1.h بذارید چه بعدش ارور میده چون
`void helloworld()
دوبار تعریف میشه
حالا بیایید هدرگارد برای هر دو هدر تعریف کنید
محتویات جدید header2.h
#ifndef HEADER2_H
#define HEADER2_H
#include <iostream>
using namespace std;
void helloworld()
{cout<<"Hello World!"<<endl;
}
#endif
محتویات جدید header1.h
#ifndef HEADER1_H
#define HEADER1_H
#include "header2.h"
void sayhello()
{helloworld();
}
#endif
و محتوای فایل اصلی
#include "header1.h"
#include "header2.h"
int main(){sayhello();
return 0;
}
دیگه ارور نمیده
چند بار هم اینکلاد کنم خیالتون راحت شه
#include "header1.h"
#include "header2.h"
#include "header1.h"
#include "header2.h"
#include "header1.h"
#include "header2.h"
#include "header1.h"
#include "header2.h"
int main(){sayhello();
return 0;
}
امیدوارم پی برده به باشید به اهمیتشون
نکته آخر: نوشته اشکان♻️
در بعضی کامپایلر ها لازم نیست
این همه تایپکنیم
#pragma once
دقیقا همین کار رو انجام می
دهط
بازم میگم فق
بعضی کامپایلر ها
موفق باشید
کدهاتون هم بی باگ
#آموزش
namespace:
چیسیتی | چرایی | چگونگی
namespace
همون طور که اسمش روشه شما میتونید یه قسمت (فضای) کدتون رو نام بذارید براش
میتونید اسم خودتون رو بذارید
حالا چرا؟
وقتی شما حجم بالایی از کد داشته باشید ممکنه که نام یه تعداد از توابع یا کلاس ها شبیه هم باشن ممکنه ارور پیش بیاد یا خوانایی کد رو کم کنه
برای جلو گیری از این موارد فضای نام تعریف میکنیم
من در مثال زیر از قصد دو تابع هم نام میذارم
اما شما یادتون نره برای کدهاتون فضای نام تعریف کنید
چون انوقت میشه سهوا
مثال
namespace Ashkan
{
void sayhello(){cout«"Hello Ashkan!"«endl;}
}
namespace Ali
{
void sayhello(){cout«"Hello Ali!"«endl;}
}
void sayhello()
{
cout«"I don't know what i need to do"«endl;
}
نحوه فراخونی هر کدام
(تست کنید خودتون پی میبرید)
باید نام فضای نام رو قبلش بنویسیم تا تشخیص بده کدوم رو فراخونی کنه
اگه اینکار رو نکنیم از تابعی که به صورت عمومی تعریف شده استفاده میکنه
Amir::sayhello();
Ali::sayhello();
sayhello();
حالا فرض کنید بخوایم از
using
استفاده کنیم
using
کارش اینه که از فلان جز این فضای نام استفاده میکنیم و دیگه لازم نیست هر بار قبلش فضای نام رو بنویسیم
using namespace
یعنی ما از سیر تا پیاز این فضای نام دیگه لازم نیست از فضای نام قبلش استفاده کنیم
مثلا:
using namespace Ashkan;
میبینید که ارور میده چرا ؟
چون یه تابع هستش که اصلا عضو هیچ کدومشون نیست
sayhello
خالی رو پاک کنید و دوباره کامپایل کنید
حالا یکم بیشتر از
using
استفاده کنیم
namespace Ashkan
{
void sayhello(){cout«"Hello Ashkan!"«endl;}
void saybye(){cout«"Good bye Amir!"«endl;}
}
namespace Ali
{
void sayhello(){cout«"Hello Ali!"«endl;}
void saybye(){cout«"Good bye Ali!"«endl;}
}
این کد ما هستش
حالا این شکلی اگه بنویسیم به کامپایلر میگیم که اون حالا پیش فرض هست و ما از کل فضای نام بهره نمیبریم
مثال
using Ashkan::saybye;
saybye();
{
using Ali::saybye;
saybye();
}
پس این رو هم توجه داشته باشید که در بلاک های مختلف میتونید
using
های مختلف داشته باشید
و توجه داشته باشید اگر در کد بالا
sayhello
خالی رو بنویسید ارور میده
چون ما به کامپایلر گفتیم فقط از اون توابع
namespace
میخوایم استفاده کنیم
اگه بخوایید از همه ش استفاده کنید این شکلی عمل کنید
using namespace Ashkan;
saybye();
{
using namespace Ali;
saybye();
}
یه ارور میده
چرا وقتی از
namespace
استفاده میکنیم به صورت پیش فرض برای بلاک های زیر دستش هم اجرا میشه
این رو ببینید
using Ashkan::saybye;
saybye();
{
using namespace Ali;
saybye();
{
saybye();
}
}
با این کار مشخص میکنیم که هر جا که
saybye
دیده شد از نسخه داخل فضای نام امیر استفاده شه
ولی داخل اون بلاک مشخص کردیم اگر توابعی از
فضای نام علی موجود بود از نسخه علی استفاده کنه
مگر وقتی که از
using
خالی استفاده شده باشه
پس میشه گفت
using
نسبت به
using namespace
جدی تر گرفته میشه
ولی
namespace
وقتی دیده شه تمام اجزا رو برای کامپایلر تعریف میکنه
این مثال ساده رو ببینید
using Ashkan::saybye;
using namespace Ali;
saybye();
sayhello();
پس میشه همزمان با هم استفاده کرد
اون کد بالا بود که بخاطر دوتا
using namespace
ارور داد
حالا میتونید خیلی راحت ارورش رو برطرف کنین
using namespace Ashkan;
saybye();
{
using Ali::saybye;
saybye();
}
وقتی تو دوتا
namespace
چیزهای هم نام باشن
اگه توی یه بلاک
using namespace
تعریف شه یا یکیش زیر دست اون یکی باشه
ارور میده
دلیلش هم واضحه چون کامپایلر نمیدونه از کدوم یکی استفاده کنه
پس میشه ارور بالا رو با دو بلاک جدا از هم حلش کرد
{
using namespace Ashkan;
saybye();
}
{
using namespace Ali;
saybye();
}
فرق
using namespace
و
using
خالی در بلاک های تو در تو
using namespace Ali;
saybye();
{
using namespace Ashkan;
saybye();
}
کد بالا مثل اون کد قبلی هستش
بهتون قبلا گفتم وقتی
از using namespace
استفاده میکنید به صورت پیش فرض برای تمام بلاک های زیر دستش اجرا میشه و مثل این میمونه دو تاش رو پشت سر هم بنویسیم
ولی using
همیشه میتونه نقض ش کنه
در صورتی که using namespace
نمیتونه
namespace:
چیسیتی | چرایی | چگونگی
namespace
همون طور که اسمش روشه شما میتونید یه قسمت (فضای) کدتون رو نام بذارید براش
میتونید اسم خودتون رو بذارید
حالا چرا؟
وقتی شما حجم بالایی از کد داشته باشید ممکنه که نام یه تعداد از توابع یا کلاس ها شبیه هم باشن ممکنه ارور پیش بیاد یا خوانایی کد رو کم کنه
برای جلو گیری از این موارد فضای نام تعریف میکنیم
من در مثال زیر از قصد دو تابع هم نام میذارم
اما شما یادتون نره برای کدهاتون فضای نام تعریف کنید
چون انوقت میشه سهوا
مثال
namespace Ashkan
{
void sayhello(){cout«"Hello Ashkan!"«endl;}
}
namespace Ali
{
void sayhello(){cout«"Hello Ali!"«endl;}
}
void sayhello()
{
cout«"I don't know what i need to do"«endl;
}
نحوه فراخونی هر کدام
(تست کنید خودتون پی میبرید)
باید نام فضای نام رو قبلش بنویسیم تا تشخیص بده کدوم رو فراخونی کنه
اگه اینکار رو نکنیم از تابعی که به صورت عمومی تعریف شده استفاده میکنه
Amir::sayhello();
Ali::sayhello();
sayhello();
حالا فرض کنید بخوایم از
using
استفاده کنیم
using
کارش اینه که از فلان جز این فضای نام استفاده میکنیم و دیگه لازم نیست هر بار قبلش فضای نام رو بنویسیم
using namespace
یعنی ما از سیر تا پیاز این فضای نام دیگه لازم نیست از فضای نام قبلش استفاده کنیم
مثلا:
using namespace Ashkan;
میبینید که ارور میده چرا ؟
چون یه تابع هستش که اصلا عضو هیچ کدومشون نیست
sayhello
خالی رو پاک کنید و دوباره کامپایل کنید
حالا یکم بیشتر از
using
استفاده کنیم
namespace Ashkan
{
void sayhello(){cout«"Hello Ashkan!"«endl;}
void saybye(){cout«"Good bye Amir!"«endl;}
}
namespace Ali
{
void sayhello(){cout«"Hello Ali!"«endl;}
void saybye(){cout«"Good bye Ali!"«endl;}
}
این کد ما هستش
حالا این شکلی اگه بنویسیم به کامپایلر میگیم که اون حالا پیش فرض هست و ما از کل فضای نام بهره نمیبریم
مثال
using Ashkan::saybye;
saybye();
{
using Ali::saybye;
saybye();
}
پس این رو هم توجه داشته باشید که در بلاک های مختلف میتونید
using
های مختلف داشته باشید
و توجه داشته باشید اگر در کد بالا
sayhello
خالی رو بنویسید ارور میده
چون ما به کامپایلر گفتیم فقط از اون توابع
namespace
میخوایم استفاده کنیم
اگه بخوایید از همه ش استفاده کنید این شکلی عمل کنید
using namespace Ashkan;
saybye();
{
using namespace Ali;
saybye();
}
یه ارور میده
چرا وقتی از
namespace
استفاده میکنیم به صورت پیش فرض برای بلاک های زیر دستش هم اجرا میشه
این رو ببینید
using Ashkan::saybye;
saybye();
{
using namespace Ali;
saybye();
{
saybye();
}
}
با این کار مشخص میکنیم که هر جا که
saybye
دیده شد از نسخه داخل فضای نام امیر استفاده شه
ولی داخل اون بلاک مشخص کردیم اگر توابعی از
فضای نام علی موجود بود از نسخه علی استفاده کنه
مگر وقتی که از
using
خالی استفاده شده باشه
پس میشه گفت
using
نسبت به
using namespace
جدی تر گرفته میشه
ولی
namespace
وقتی دیده شه تمام اجزا رو برای کامپایلر تعریف میکنه
این مثال ساده رو ببینید
using Ashkan::saybye;
using namespace Ali;
saybye();
sayhello();
پس میشه همزمان با هم استفاده کرد
اون کد بالا بود که بخاطر دوتا
using namespace
ارور داد
حالا میتونید خیلی راحت ارورش رو برطرف کنین
using namespace Ashkan;
saybye();
{
using Ali::saybye;
saybye();
}
وقتی تو دوتا
namespace
چیزهای هم نام باشن
اگه توی یه بلاک
using namespace
تعریف شه یا یکیش زیر دست اون یکی باشه
ارور میده
دلیلش هم واضحه چون کامپایلر نمیدونه از کدوم یکی استفاده کنه
پس میشه ارور بالا رو با دو بلاک جدا از هم حلش کرد
{
using namespace Ashkan;
saybye();
}
{
using namespace Ali;
saybye();
}
فرق
using namespace
و
using
خالی در بلاک های تو در تو
using namespace Ali;
saybye();
{
using namespace Ashkan;
saybye();
}
کد بالا مثل اون کد قبلی هستش
بهتون قبلا گفتم وقتی
از using namespace
استفاده میکنید به صورت پیش فرض برای تمام بلاک های زیر دستش اجرا میشه و مثل این میمونه دو تاش رو پشت سر هم بنویسیم
ولی using
همیشه میتونه نقض ش کنه
در صورتی که using namespace
نمیتونه
#بیشتر_بدانید
اهمیت هدر گارد ها
header guards
حتما همتون تا حالا هدر گاردهایی مثل بالا رو دیدید
اما اهمیتشون چیه؟
خیلی خلاصه جلوگیری از تعریف چندبار سهوی و ارور های ناخواسته
در مثال زیر خیلی ساده بهتون میگم چرا مفیدن
یه نکته » من برای جلوگیری از تکرار اسم هدر گارد ها از روشی استفاده میکنم که تقریبا اکثر برنامه نویس ها از اون روش استفاده میکنن
که
HEADERNAME
همون نام فایل هدر هست
حالا مثال:
محتویات فایل header2.h
محتویات فایل header1.h
محتویات فایل اصلی سی پلاس پلاس
میبینیدکه ارور نمیده و اجرا میشه
حالا
`
رو به فایل سی پلاس پلاس اضافه کنین
چه قبل اون header1.h بذارید چه بعدش ارور میده چون
دوبار تعریف میشه
حالا بیایید هدرگارد برای هر دو هدر تعریف کنید
محتویات جدید header2.h
محتویات جدید header1.h
و محتوای فایل اصلی
دیگه ارور نمیده
چند بار هم اینکلاد کنم خیالتون راحت شه
امیدوارم پی برده به باشید به اهمیتشون
نکته آخر: نوشته اشکان♻️
در بعضی کامپایلر ها لازم نیست
#pragma once
دقیقا همین کار رو انجام می
بعضی کامپایلر ها
موفق باشید
کدهاتون هم بی باگ
اهمیت هدر گارد ها
header guards
#ifndef HEADER_H
#define HEADER_H
#endif
حتما همتون تا حالا هدر گاردهایی مثل بالا رو دیدید
اما اهمیتشون چیه؟
خیلی خلاصه جلوگیری از تعریف چندبار سهوی و ارور های ناخواسته
در مثال زیر خیلی ساده بهتون میگم چرا مفیدن
یه نکته » من برای جلوگیری از تکرار اسم هدر گارد ها از روشی استفاده میکنم که تقریبا اکثر برنامه نویس ها از اون روش استفاده میکنن
#define HEADERNAME_H
که
HEADERNAME
همون نام فایل هدر هست
حالا مثال:
محتویات فایل header2.h
#include <iostream>
using namespace std;
void helloworld()
{cout<<"Hello World!"<<endl;
}
محتویات فایل header1.h
#include "header2.h"
void sayhello()
{helloworld();
}
محتویات فایل اصلی سی پلاس پلاس
#include "header1.h"
int main(){sayhello();
return 0;
}
میبینیدکه ارور نمیده و اجرا میشه
حالا
#include "header2.h"
`
رو به فایل سی پلاس پلاس اضافه کنین
#include "header1.h"
#include "header2.h"
int main(){sayhello();
return 0;
}
چه قبل اون header1.h بذارید چه بعدش ارور میده چون
`void helloworld()
دوبار تعریف میشه
حالا بیایید هدرگارد برای هر دو هدر تعریف کنید
محتویات جدید header2.h
#ifndef HEADER2_H
#define HEADER2_H
#include <iostream>
using namespace std;
void helloworld()
{cout<<"Hello World!"<<endl;
}
#endif
محتویات جدید header1.h
#ifndef HEADER1_H
#define HEADER1_H
#include "header2.h"
void sayhello()
{helloworld();
}
#endif
و محتوای فایل اصلی
#include "header1.h"
#include "header2.h"
int main(){sayhello();
return 0;
}
دیگه ارور نمیده
چند بار هم اینکلاد کنم خیالتون راحت شه
#include "header1.h"
#include "header2.h"
#include "header1.h"
#include "header2.h"
#include "header1.h"
#include "header2.h"
#include "header1.h"
#include "header2.h"
int main(){sayhello();
return 0;
}
امیدوارم پی برده به باشید به اهمیتشون
نکته آخر: نوشته اشکان♻️
در بعضی کامپایلر ها لازم نیست
این همه تایپکنیم
#pragma once
دقیقا همین کار رو انجام می
دهط
بازم میگم فق
بعضی کامپایلر ها
موفق باشید
کدهاتون هم بی باگ
#بدون_شرح
تا حالا به این فکر افتادین یه چیزی شبیه
cout
بسازین
(بسازین خو 😄 اینم یه نمونش که اهمیت
operator
ها رو نشون میده)
البته
cout
خیلی گسترده تره اما اینجا هدف چیزه دیگه ای هست😉
شب و روزتون بی باگ
تا حالا به این فکر افتادین یه چیزی شبیه
cout
بسازین
(بسازین خو 😄 اینم یه نمونش که اهمیت
operator
ها رو نشون میده)
#include <cstdio>
struct M{
M& operator<<(const char* in)
{
printf("%s",in);
return *this;
}
M& operator<<(int in)
{
printf("%d",in);
return *this;
}
M& operator<<(char in)
{
printf("%c",in);
return *this;
}
}myout;
const char el = '\n';
int main(){
myout<<"Hello! "<<10<<el;
return 0;
}
البته
cout
خیلی گسترده تره اما اینجا هدف چیزه دیگه ای هست😉
شب و روزتون بی باگ
#بیشتر_بدانید
تا حالا فکر کردید چجوری بی نهایت آرگومان داشته باشید؟
دونوعش رو بهتون معرفی میکنم
variadic arguments
بچه ها
بچه ها
variadic arguments
😄
اینم معرفی
یه تابع میخوام باهاش بنویسم که هرتعداد براش عدد بفرستیم جمع کلشون رو برامون برگردونه
جالب بود نه؟
بیایید ازقصد اشتباه کنیم و آرگومان اول که تعداد هست رو اشتباه بفرستیم
اوه
عجب وضعیت باقالی شد😂
برای اینکه دیگه اشتباه نکنیم با استفاده از یه ماکرو آرگومان ها رو میفرستیم که آرگومان اولش رو خودش برامون حساب کنه
جالبه بهتون بگم اونم با آرگومان های بی نهایت کار میکنه😄
اگه
__VA_ARGS__رو جلوش بذاریم تمام آرگومان بی نهایت ها رو عیننا مثل قالب میذاره جلوش
کد بالا تا حداکثر 10 تا آرگومان رو میتونه بشماره اگه بیشتر میخواهید
بیشتر بنویسید
بیایید تستش کنیم
خروجی 5 میده
بذارید اول تحلیلش کنیم
هر خط یه مرحله از ماکرو رو نشون میده
اگه نمیدونید ماکرو چیه
چند تا پست بالاتر یه توضیح ساده ازش آماده کردم
امیدوارم متوجه شده باشید
حالا فقط کافیه یه ماکرو دیگه تعریف کنیم
چیزی که تولید میکنه این باشه
اولش تعداد آرگومان ها رو برامون بشماره با همون ماکرو بالایی
بعد خود آرگومان ها رو دقیق بفرسته
حواستون جمع باشه این ماکرو یی که نوشتم بیشتر از 10 تا نمیونه حساب کنه
شما خودتون میتونید بیشترش کنید
حالا کدمون رو تغییر میدیم
MYARGCOUNTER
کاری که میکنه اینه که
CountArgs(2,3,2,3,4,5) , 2,3,2,3,4,5
این رو برامون میسازه و
CountArgs
هم که میشمره پس میشه
6,2,3,2,3,4,5
که مثل همون کد اولمون میشه
نکته اشکانی😐
CountArgs
به خاطر اینکه در بعضی کامپایلر ها درست کامپایل نمیشه سه بار بازخوانی کردم وگرنه مثلا با 2 بار هم روی جی سی سی عم
ولی کد بالا در ویژوال
2012
درست کامپایل نخواهد شد
نکته دو:
تابع بالا با نام
variadic function
و ماکرو بالا با نام
variadic macro
شناخته میشن
نکته سه:
توابعی مثل
printf
نوعی
variadic function
هستن
اینم یه مبحثی بود هزار ماشالا مخ درگیر کن
امیدوارم لذت برده باشید
♥️تک تک ارور های زندگی تون پرپر
تا حالا فکر کردید چجوری بی نهایت آرگومان داشته باشید؟
دونوعش رو بهتون معرفی میکنم
variadic arguments
بچه ها
بچه ها
variadic arguments
😄
اینم معرفی
یه تابع میخوام باهاش بنویسم که هرتعداد براش عدد بفرستیم جمع کلشون رو برامون برگردونه
#include <iostream>
#include <stdarg.h>
using namespace std;
//! جمع اعدادی که براش میفرستیم رو برمیگردونه
//! count همون تعداد آرگومان هایی که براش میفرستیم
//! ... همون آرگومان بی نهایت
int FOO(int count,...)
{int out=0;//خروجی
va_list args;// باید آرگومان ها رو توی این بریزیم
va_start(args,count);// آرگومان ها رو توی اون میریزیم
//count
// نقطه شروع هست نه تعداد اشتباه نکنید
for(int i=0;i<count;i++)//این حلقه به تعداد آرگومان ها اجرا میشه
{int a = va_arg(args,int);//متغیر بعدی رو به عنوان عدد صحیح لود میکنیم
out+=a;// به خروجی اضافه میکنیم
}
va_end(args);//تموم شد
return out;
}
int main(){cout<<FOO(6,2,3,2,3,4,5)<<endl;//2+3+2+3+4+5=19
return 0;
}
جالب بود نه؟
بیایید ازقصد اشتباه کنیم و آرگومان اول که تعداد هست رو اشتباه بفرستیم
FOO(8,2,3,2,3,4,5)
اوه
عجب وضعیت باقالی شد😂
برای اینکه دیگه اشتباه نکنیم با استفاده از یه ماکرو آرگومان ها رو میفرستیم که آرگومان اولش رو خودش برامون حساب کنه
جالبه بهتون بگم اونم با آرگومان های بی نهایت کار میکنه😄
#define CountArgs(...)
اگه
__VA_ARGS__رو جلوش بذاریم تمام آرگومان بی نهایت ها رو عیننا مثل قالب میذاره جلوش
اول یه ماکرو مینویسیم تا تعدادش رو بشماریم
#define CountArgs(...) CountArgs2( (__VA_ARGS__,10,9,8,7,6,5,4,3,2,1) )
#define CountArgs2(args) CountArgs3 args
#define CountArgs3(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,argsc,...) argsc
کد بالا تا حداکثر 10 تا آرگومان رو میتونه بشماره اگه بیشتر میخواهید
بیشتر بنویسید
بیایید تستش کنیم
cout<<CountArgs(1,1,1,1,1)<<endl;//5
خروجی 5 میده
بذارید اول تحلیلش کنیم
هر خط یه مرحله از ماکرو رو نشون میده
اگه نمیدونید ماکرو چیه
چند تا پست بالاتر یه توضیح ساده ازش آماده کردم
CountArgs(1,1,1,1,1)
CountArgs2( (1,1,1,1,10,9,8,7,6,5,4,3,2,1) ) // دوتا پرانتز
CountArgs2(args) CountArgs3 args
CountArgs2(args) CountArgs3 (1,1,1,1,10,9,8,7,6,5,4,3,2,1) //args جابه جا میشه
CountArgs3(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,argsc,...) argsc
CountArgs3(1,1,1,1,1,10,9,8,7,6,5 ,...) argsc // غیره ش رو ننوشتم دیگه احتیاجی نیست
argc نیتجه ی 5 رو تحویلمون میده
امیدوارم متوجه شده باشید
حالا فقط کافیه یه ماکرو دیگه تعریف کنیم
چیزی که تولید میکنه این باشه
اولش تعداد آرگومان ها رو برامون بشماره با همون ماکرو بالایی
بعد خود آرگومان ها رو دقیق بفرسته
حواستون جمع باشه این ماکرو یی که نوشتم بیشتر از 10 تا نمیونه حساب کنه
شما خودتون میتونید بیشترش کنید
#define MYARGCOUNTER(...) CountArgs(__VA_ARGS__),__VA_ARGS__
حالا کدمون رو تغییر میدیم
cout<<FOO(MYARGCOUNTER(2,3,2,3,4,5))<<endl;
MYARGCOUNTER
کاری که میکنه اینه که
CountArgs(2,3,2,3,4,5) , 2,3,2,3,4,5
این رو برامون میسازه و
CountArgs
هم که میشمره پس میشه
6,2,3,2,3,4,5
که مثل همون کد اولمون میشه
نکته اشکانی😐
CountArgs
به خاطر اینکه در بعضی کامپایلر ها درست کامپایل نمیشه سه بار بازخوانی کردم وگرنه مثلا با 2 بار هم روی جی سی سی عم
ل میکنه4
#define CountArgs(...) CountArgs2(__VA_ARGS__,10,9,8,7,6,5,
,3,2,1).) argsc
#define CountArgs2(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,argsc,..
ولی کد بالا در ویژوال
2012
درست کامپایل نخواهد شد
نکته دو:
تابع بالا با نام
variadic function
و ماکرو بالا با نام
variadic macro
شناخته میشن
نکته سه:
توابعی مثل
printf
نوعی
variadic function
هستن
اینم یه مبحثی بود هزار ماشالا مخ درگیر کن
امیدوارم لذت برده باشید
♥️تک تک ارور های زندگی تون پرپر
#لازمه_بدانید !
لازمه بدانید 😄 که خاصیت استفاده مجدد از کد چیه
خیلی هم لازمه
من در پایین با روش شی گرایی خیلی ابتدایی 😅 این کار رو انجام خواهم داد و با دو روش مختلف ران تایم یا کامپایل تایم اون ها رو لود میکنم
مثالی که میزنم ساده هست
یه استراکت که کارش لود کتاب خانه ریاضی هست
البته نترسید قرار نیست همه چیز داشته باشه😄
برای مثال ما فقط یه تابع برای محاسبه قدر مطلق داره
یه بار با استفاده از کتاب خانه استاندارد سی لود میکنیم و بار دیگه یه تابع که خودمون نوشتیم و مثلا کتابخانه خودمونه (الکی مثلا😅)
یه ماکرو برای کامپایل تایم
#define USING_MY_MATH_LIB
این اگه صفر بود از کتابخانه سی استفاده میکنیم وگرنه اگه 1 بود از کتاب خانه خودمون استفاده میکنیم
#include <iostream>
#include <cmath>
using namespace std;
#define USING_MY_MATH_LIB 0 // الان مثلا میخوایم از کتاب خانه استاندارد استفاده شه
#if USING_MY_MATH_LIB == 1
int myabs(int in) // این همون کتابخونه الکیه هستش
{
cout<<"myabs called!"<<endl;// مطمئن شیم فراخوانی شده
if(in<0)
return -in;
return in;
}
#endif // USING_MY_MATH_LIB
struct themathlib // استراکت بیس برای شی گرایی
{
virtual int Abs(int in)=0;// virtual برای شی گرایی راحت تر شه
};
#if USING_MY_MATH_LIB == 1 // از کتاب خانه خودمون استفاده میکنیم
struct mymathlib: themathlib // یه استراکت فرزند مشتق از بیس کار برای کتاب خانه خودمون
{
int Abs(int in){ return myabs(in); } // همون تابع خودمون رو صدا زدیم
};
#endif // USING_MY_MATH_LIB
#if USING_MY_MATH_LIB == 0// از کتابخانه استاندارد استفاده میکنیم
struct stdmathlib: themathlib
{
int Abs(int in){ return abs(in); }// تابع کتابخانه استاندارد
};
#endif // USING_MY_MATH_LIB
themathlib* intializemathlib()// برای ایجاد کردن یه شی جدید
{
themathlib* out=NULL;// خروجی
#if USING_MY_MATH_LIB ==1
out = new mymathlib; // کتابخانه خودمون
#endif // USING_MY_MATH_LIB
#if USING_MY_MATH_LIB ==0
out = new stdmathlib; // کتاب خانه استاندارد
#endif // USING_MY_MATH_LIB
return out;
}
int main()
{
themathlib* obj = intializemathlib();// یه شی ایجاد میکنیم
int in;
cout<<"Enter Integer:"<<endl;
cin>>in;
cout<<obj->Abs(in)<<endl;// به همین راحتی استفاده میکنیم
delete obj;
return 0;
}
حالا فرض کنیم تصمیمون عوض شد و خواستیم از کتابخانه خودمون استفاده کنیم
چی کار باید کنیم؟
خب خیلی راحته فقط کافیه
#define USING_MY_MATH_LIB 1
باشه
هر دو حالت رو حتما تست کنید
همونطور که میدونید ماکرو ها برای موقع کامپایل هستن و تکلیفشون رو برنامه نویس مشخص میکنه
حالا فرض کنیم کاربر نهایی قراره تکلیف رو مشخص کنه
و به اصطلاح کارها همون موقع اجرا انجام شه
#include <iostream>
#include <cmath>
using namespace std;
int myabs(int in)
{
cout<<"myabs called!"<<endl;
if(in<0)
return -in;
return in;
}
struct themathlib
{
virtual int Abs(int in)=0;
};
struct mymathlib: themathlib
{
int Abs(int in){ return myabs(in); }
};
struct stdmathlib: themathlib
{
int Abs(int in){ return abs(in); }
};
themathlib* intializemathlib(bool usingmylib)// اگه صحیح بود از کتابخانه خودمون استفاده میشه وگرنه از استاندارد
{
themathlib* out=NULL;// خروجی
if(usingmylib)
out = new mymathlib; // کتابخانه خودمون
else
out = new stdmathlib; // کتاب خانه استاندارد
return out;
}
int main()
{
bool type;
int in;
cout<<"Enter the Math library type:\n 0: standard library\n 1: my library\n";// پیام راهنمایی
cin>>type; //نوع کتابخانه
cout<<"Enter Integer:"<<endl;// پیام راهنمایی
cin>>in;//ورودی
themathlib* obj = intializemathlib(type);// ایجاد یک شی
cout<<obj->Abs(in)<<endl;//اجرای تابع
delete obj;
return 0;
}
یه مقایسه سطحی از حالت اول و دوم:
در حالت اول فقط وقتی کلاس ایجاد میشه و کامپایل میشه که استفاده شده باشه ولی حالت دوم اینطور نیست پس مسلما حجم برنامه زیاد خواهد شد و چون شرط ها موقع اجرا استفاده میشن پس سرعت اجرا یه ذره کم میشه
اما در عوض کاربر نهایی تکلیف رو مشخص میکنه و دستش بازتره
در صورتی که تکلیف حالت اول رو فقط برنامه نویس میتونه مشخص کنه
یه چیزی برای اونایی که میخوان یاد بگیرن :
بد نیست خودتون هم یه کتابخانه سوم بنویسید و اضافه کنید تا خوب استفاده مجدد رو بشناسین
1396/5/10
امیدوارم مفید بوده باشه
دنیای بی باگی رو براتون
لازمه بدانید 😄 که خاصیت استفاده مجدد از کد چیه
خیلی هم لازمه
من در پایین با روش شی گرایی خیلی ابتدایی 😅 این کار رو انجام خواهم داد و با دو روش مختلف ران تایم یا کامپایل تایم اون ها رو لود میکنم
مثالی که میزنم ساده هست
یه استراکت که کارش لود کتاب خانه ریاضی هست
البته نترسید قرار نیست همه چیز داشته باشه😄
برای مثال ما فقط یه تابع برای محاسبه قدر مطلق داره
یه بار با استفاده از کتاب خانه استاندارد سی لود میکنیم و بار دیگه یه تابع که خودمون نوشتیم و مثلا کتابخانه خودمونه (الکی مثلا😅)
یه ماکرو برای کامپایل تایم
#define USING_MY_MATH_LIB
این اگه صفر بود از کتابخانه سی استفاده میکنیم وگرنه اگه 1 بود از کتاب خانه خودمون استفاده میکنیم
#include <iostream>
#include <cmath>
using namespace std;
#define USING_MY_MATH_LIB 0 // الان مثلا میخوایم از کتاب خانه استاندارد استفاده شه
#if USING_MY_MATH_LIB == 1
int myabs(int in) // این همون کتابخونه الکیه هستش
{
cout<<"myabs called!"<<endl;// مطمئن شیم فراخوانی شده
if(in<0)
return -in;
return in;
}
#endif // USING_MY_MATH_LIB
struct themathlib // استراکت بیس برای شی گرایی
{
virtual int Abs(int in)=0;// virtual برای شی گرایی راحت تر شه
};
#if USING_MY_MATH_LIB == 1 // از کتاب خانه خودمون استفاده میکنیم
struct mymathlib: themathlib // یه استراکت فرزند مشتق از بیس کار برای کتاب خانه خودمون
{
int Abs(int in){ return myabs(in); } // همون تابع خودمون رو صدا زدیم
};
#endif // USING_MY_MATH_LIB
#if USING_MY_MATH_LIB == 0// از کتابخانه استاندارد استفاده میکنیم
struct stdmathlib: themathlib
{
int Abs(int in){ return abs(in); }// تابع کتابخانه استاندارد
};
#endif // USING_MY_MATH_LIB
themathlib* intializemathlib()// برای ایجاد کردن یه شی جدید
{
themathlib* out=NULL;// خروجی
#if USING_MY_MATH_LIB ==1
out = new mymathlib; // کتابخانه خودمون
#endif // USING_MY_MATH_LIB
#if USING_MY_MATH_LIB ==0
out = new stdmathlib; // کتاب خانه استاندارد
#endif // USING_MY_MATH_LIB
return out;
}
int main()
{
themathlib* obj = intializemathlib();// یه شی ایجاد میکنیم
int in;
cout<<"Enter Integer:"<<endl;
cin>>in;
cout<<obj->Abs(in)<<endl;// به همین راحتی استفاده میکنیم
delete obj;
return 0;
}
حالا فرض کنیم تصمیمون عوض شد و خواستیم از کتابخانه خودمون استفاده کنیم
چی کار باید کنیم؟
خب خیلی راحته فقط کافیه
#define USING_MY_MATH_LIB 1
باشه
هر دو حالت رو حتما تست کنید
همونطور که میدونید ماکرو ها برای موقع کامپایل هستن و تکلیفشون رو برنامه نویس مشخص میکنه
حالا فرض کنیم کاربر نهایی قراره تکلیف رو مشخص کنه
و به اصطلاح کارها همون موقع اجرا انجام شه
#include <iostream>
#include <cmath>
using namespace std;
int myabs(int in)
{
cout<<"myabs called!"<<endl;
if(in<0)
return -in;
return in;
}
struct themathlib
{
virtual int Abs(int in)=0;
};
struct mymathlib: themathlib
{
int Abs(int in){ return myabs(in); }
};
struct stdmathlib: themathlib
{
int Abs(int in){ return abs(in); }
};
themathlib* intializemathlib(bool usingmylib)// اگه صحیح بود از کتابخانه خودمون استفاده میشه وگرنه از استاندارد
{
themathlib* out=NULL;// خروجی
if(usingmylib)
out = new mymathlib; // کتابخانه خودمون
else
out = new stdmathlib; // کتاب خانه استاندارد
return out;
}
int main()
{
bool type;
int in;
cout<<"Enter the Math library type:\n 0: standard library\n 1: my library\n";// پیام راهنمایی
cin>>type; //نوع کتابخانه
cout<<"Enter Integer:"<<endl;// پیام راهنمایی
cin>>in;//ورودی
themathlib* obj = intializemathlib(type);// ایجاد یک شی
cout<<obj->Abs(in)<<endl;//اجرای تابع
delete obj;
return 0;
}
یه مقایسه سطحی از حالت اول و دوم:
در حالت اول فقط وقتی کلاس ایجاد میشه و کامپایل میشه که استفاده شده باشه ولی حالت دوم اینطور نیست پس مسلما حجم برنامه زیاد خواهد شد و چون شرط ها موقع اجرا استفاده میشن پس سرعت اجرا یه ذره کم میشه
اما در عوض کاربر نهایی تکلیف رو مشخص میکنه و دستش بازتره
در صورتی که تکلیف حالت اول رو فقط برنامه نویس میتونه مشخص کنه
یه چیزی برای اونایی که میخوان یاد بگیرن :
بد نیست خودتون هم یه کتابخانه سوم بنویسید و اضافه کنید تا خوب استفاده مجدد رو بشناسین
1396/5/10
امیدوارم مفید بوده باشه
دنیای بی باگی رو براتون