آموزش کار با Template در سی پلاس پلاس
قالب (template)، یکی از قابلیت های کاربردی و قدرتمند زبان سی پلاس پلاس است که با استفاده از آن می توانید توابع را صرف نظر از نوع داده ورودی بنویسید. با این شرایط کدنویسی راحت تر و توسعه نرم افزارها سریعتر اتفاق می افتد.
قالب چیست؟
در روند طراحی نرم افزار، گاهی لازم است یک تابع یا عضو تابعی (member function) با انواع داده های مختلف کار کند. قالب ها به ما کمک می کنند این کار را به راحتی انجام دهیم. برای درک بهتر موضوع فرض کنید می خواهیم تابعی طراحی کنیم که یک داده را به عنوان پارامتر ورودی دریافت کند و سپس آن را چاپ کند. اگر بخواهیم این تابع را برای همه انواع داده های اصلی (primitive data types) در سی پلاس پلاس پیاده سازی کنیم؛ نیاز داریم تا چندین تابع مختلف برای این کار بنویسیم. در واقع برای هر نوع داده ای باید یک تابع مجزا تعریف کنیم در حالی که کدهای نوشته شده برای همه این توابع یکسان هستند. اینجاست که استفاده از قالب ها می توانند ما را در حل این مسائل کمک کنند.
قالب های تابعی (function templates)
حال بیایید مسئله ای که در بالا مطرح شد را با دو روش مختلف پیاده سازی کنیم:
روش اول- بدون استفاده از قالب تابعی:
[cpp] void printInput(int x){
cout << x; // printing integer data
}
void printInput(float x)
{
cout << x; // printing float data
}
void printInput(char x)
{
cout << x; // printing character data
}
void printInput(string x)
{
cout << x; // printing string data
}[/cpp]
همانطور که در کد بالا مشاهده می شود، ما در این شرایط مجبور هستیم برای هر نوع داده یک تابع تعریف کنیم و این در حالی است که این کد برای همه توابع یکسان است و ما مجبور هستیم این کدهای تکراری را بارها و بارها استفاده کنیم. این موضوع با اصل DRY، یا Don’t Repeat Yourself (خودت را تکرار نکن) مغایرت دارد. جدا از این مسئله؛ نگهداری و خطایابی برنامه نیز دشوار خواهد شد.
روش دوم- با استفاده از قالب تابعی:
حال همین تابع را با استفاده از قالب پیاده سازی می کنیم :
[cpp] template ;void printInput(T x)
{
cout << x; // printing T data type
}
[/cpp]
در خط اول، با استفاده از کلمه template به کامپایلر اعلام می کنیم که می خواهیم از قالب استفاده کنیم. سپس درون علامت های بزرگتر و کوچکتر ورودی های مورد نیاز تابع را قرار می دهیم. class T بدین معناست که می خواهیم یک ورودی از نوع T داشته باشیم. حال این نوع می تواند بسته به شرایط مختلف int، float، double، char و یا حتی یک نوع داده ای خود ساخته باشد. مانند کلاسی به نام StudentClass که توسط برنامه نویس ساخته شده است.
توجه داشته باشید که اگر خواستیم بیش از یک نوع داده ای غیر ثابت در تابع خود داشته باشیم کافی است علامت , را بعد از class T قرار داده و نوع داده بعدی را تعریف نماییم. همچنین نام T به صورت اختیاری انتخاب شده است و معمولا از این نام استفاده می شود اما شما می توانید از هر نام دلخواه دیگری استفاده نمایید.
بعد از نوشتن اولین خط، هرجایی که لازم شد به جای نوع داده از T استفاده می کنیم و کامپایلر در زمان اجرا بسته به نوع در خواست برنامه نویس کد مورد نظر را تولید می کنید. به عنوان مثال اگر برنامه نویس در ساختار برنامه خود این تابع را برای نوع داده ای عدد صحیح (integer) استفاده کرده باشد؛ به جای T در تمام تابع عبارت int قرار داده می شود و بدین صورت تابع مورد نظر ساخته می شود.
همانطور که مشاهده می شود، کد برنامه از ۱۹ خط به ۵ خط کاهش یافت و این کار را برای برنامه نویس بسیار راحت می کند. از طرفی استفاده از قالب ها می تواند به خوانایی کد کمک کند به طوری که تحلیلگر برنامه می تواند بدون در نظر گرفتن نوع داده؛ الگوریتم را تحلیل کند.
قالب های کلاسی (class templates)
کاربرد دیگر قالب ها در کلاس ها می باشد. به طوریکه برنامه نویس می توانید صرف نظر از نوع داده ای کلاس خود را طراحی و کد نویسی کند. برای مثال فرض کنید می خواهیم کلاسی به نام Calculator طراحی کنیم که در سازنده (Constructor) خود دو ورودی دریافت می کند و متدهایی برای انجام چهار عمل اصلی (جمع، تفریق، ضرب و تقسیم) را برای این داده ها ارائه می دهد. اگر ما بخواهیم این کلاس را برای انواع داده های عدد صحیح، اعشاری و کاراکتری پیاده سازی کنیم مجبور می شویم که سه کلاس مجزا تعریف کرده و کدهای یکسان را تکرار کنیم. شاید به این موضوع فکر کنید که با استفاده از وراثت (Inheritance) می توانیم کار را راحتتر کنیم اما باید در نظر داشته باشیم که ورودی متدها برای این کلاس ها یکسان نیست در نتیجه چنین امکانی فراهم نیست.
همانند قسمت قبلی مسئله فوق را با دو روش حل می کنیم :
روش اول- بدون استفاده از قالب کلاسی:
class Calculator { private: int firstNumber, secondNumber; public: Calculator(int a, int b) : firstNumber(a), secondNumber(b) { }; int sum() { return firstNumber + secondNumber; } int divide() { return firstNumber / secondNumber; } int multiple() { return firstNumber * secondNumber; } int subtract() { return firstNumber - secondNumber; } void showResult() { cout << "sum = " << setprecision(5) << sum() << endl << "subtract = " << subtract() << endl << "multiple = " << multiple() << endl; } };
لازم به ذکر است که کد فوق تنها مربوط به نوع داده int می باشد و برای طولانی نشدن پست، کدهای مربوط به float و char قرار داده نمی شود اما دقیقا به شکل کد بالاست با این تفاوت که به جای int از float یا char استفاده می شود. همانطور که مشاهده می شود کد بسیار طولانی خواهد شد و این موضوع اصلا خوب نیست.
روش دوم- با استفاده از قالب کلاسی:
کد فوق را می توانیم با استفاده از قالب ها، خیلی ساده تر و خواناتر به شکل زیر پیاده سازی کنیم:
template &amp;lt;class T&amp;gt; class Calculator { private: int firstNumber, secondNumber; public: Calculator(T a, T b) : firstNumber(a), secondNumber(b) { }; T sum() { return firstNumber + secondNumber; } T divide() { return firstNumber / secondNumber; } T multiple() { return firstNumber * secondNumber; } T subtract() { return firstNumber - secondNumber; } void showResult() { cout << "sum = " << setprecision(5) << sum() << endl << "subtract = " << subtract() << endl << "multiple = " << multiple() << endl << "divide = " << divide() << endl << endl; } };
همانطور که مشاهده می کنید کد خوانایی بهتری پیدا کرد و تحلیل آن صرف از نوع داده قابل انجام است.
نحوه ساختن شی از قالب های کلاسی
ممکن است برای شما این سوال پیش بیاید که وقتی یک کلاس را با استفاده از قالب پیاده سازی می کنیم، چگونه می توانیم از آن شی تولید کنیم ؟ برای پاسخ به سوال متدهای کلاس Calculator را در تابع main برنامه فراخوانی می کنیم :
int main() { Calculator <int> intCalc(2, 3); intCalc.showResult(); Calculator <float> floatCalc(2.0, 3.0); floatCalc.showResult(); Calculator <char> charCalc('a', 'b'); charCalc.showResult(); return 0; }
در کد بالا سه شی مختلف به ترتیب با نوع داده ای عدد صحیح، عدد اعشاری و کاراکتر ساختیم. همانطور که مشاهده می شود بعد از نوشتن نام کلاس باید در داخل علامت های کوچکتر و بزرگتر نوع داده را ذکر کنیم که می تواند یک نوع داده اساسی و یا یک نوع داده ساخته شده توسط کاربر (user defined) باشد.
اگر سوال، انتقاد یا نظری در مورد این مطلب داشتید می توانید آن را از طریق نظرات با ما مطرح نمایید.