الگو‌ها (Templates) در ++C

Templates in C++

templates-in-cpp

یک الگو (template) ابزاری بسیار قدرتمند و ساده در زبان برنامه‌نویسی ++C به حساب می‌آید. خیلی ساده شما نوع داده را به عنوان پارامتر ارسال می‌کنید، به طوری که نباید کد مشابه برای انواع داده مختلف بنویسیم. به عنوان مثال، یک کُمپانی نرم‌افزاری ممکن است برای انواع داده‌ی مختلف، به تابع ()sort نیاز داشته باشد. به جای این‌که قطعه‌کُد‌های مختلفی بنویسید و نگه‌‌داری کنید،‌ می‌توانید یک‌بار تابع ()sort را نوشته و نوع داده را به عنوان پارامتر ارسال کنید.

زبان ++C دو کلمه کلیدی جدید را برای پشتیبانی از الگو‌ها (‌templates) فراهم کرده است: 'template' و 'typename'. دومین کلمه‌ی کلیدی (typename) می‌تواند همیشه با کلمه‌ی کلیدی 'class' جایگزین شود.

نحوه‌ی کار این الگو‌ها یا همان templates‌ها به چه شکل است؟

الگو‌ها در زمان کامپایل (compiler time) شکل می‌گیرند. این شبیه ماکر‌و‌ها (macros) است. تفاوتش این است که کامپایلر، قبل از اینکه template گسترش یابد، نوع را بررسی می‌کند. این ساده است، کُد منبع (source code) تنها شامل یک کلاس یا تابع است،‌ اما کُد کامپایل شده ممکن است شامل کُپی‌های مختلفی از همان تابع و کلاس باشد.

templates-cpp

الگو‌های تابع (‌Function Templates)

می‌توان یک تابع عمومی نوشت که برای انواع داده‌ِ مختلف مورد استفاده قرار بگیرد. مثال‌های از الگو‌های تابع، ()sort max()، min()، ()printArray است. در این مورد می‌توانید با رجوع به ++Generics in C بیشتر بدانید.

#include <iostream>
using namespace std;
 
// One function works for all data types.  This would work
// even for user defined types if operator '>' is overloaded
template <typename T>
T myMax(T x, T y)
{
   return (x > y)? x: y;
}
 
int main()
{
  cout << myMax<int>(3, 7) << endl;  // Call myMax for int
  cout << myMax<double>(3.0, 7.0) << endl; // call myMax for double
  cout << myMax<char>('g', 'e') << endl;   // call myMax for char
 
  return 0;
}

خروجی قطعه‌کُد بالا به شکل زیر است:

7
7
g

برنامه‌ی زیر Bubble Sort را با استفاده از templates در ++C پیاده‌سازی می‌کند:

// CPP code for bubble sort
// using template function
#include <iostream>
using namespace std;
  
// A template function to implement bubble sort.
// We can use this for any data type that supports
// comparison operator < and swap works for it.
template <class T>
void bubbleSort(T a[], int n) {
    for (int i = 0; i < n - 1; i++)
        for (int j = n - 1; i < j; j--)
            if (a[j] < a[j - 1])
              swap(a[j], a[j - 1]);
}
  
// Driver Code
int main() {
    int a[5] = {10, 50, 30, 40, 20};
    int n = sizeof(a) / sizeof(a[0]);
  
    // calls template function
    bubbleSort<int>(a, n);
  
    cout << " Sorted array : ";
    for (int i = 0; i < n; i++)
        cout << a[i] << " ";
    cout << endl;
  
  return 0;
}

خروجی قطعه‌کُد بالا به این صورت است:

Sorted array : 10 20 30 40 50

الگو‌های کلاس (Class Templates)

الگو‌های کلاس هم مانند الگو‌های تابع هستند. الگو‌های کلاس ( class templates ) زمانی که یک کلاس برای انجام کاری که مستقل از نوع داده است، تعریف شده است، مفید هستند. که می‌تواند برای کلاس‌هایی مانند LinkedList،‌ BinaryTree، Stack ،‌Queue، Array و غیره، مفید باشند.

قطعه‌کُد زیر، مثال ساده‌ای است از کلاس الگوی آرایه:

#include <iostream>
using namespace std;
 
template <typename T>
class Array {
private:
    T *ptr;
    int size;
public:
    Array(T arr[], int s);
    void print();
};
 
template <typename T>
Array<T>::Array(T arr[], int s) {
    ptr = new T[s];
    size = s;
    for(int i = 0; i < size; i++)
        ptr[i] = arr[i];
}
 
template <typename T>
void Array<T>::print() {
    for (int i = 0; i < size; i++)
        cout<<" "<<*(ptr + i);
    cout<<endl;
}
 
int main() {
    int arr[5] = {1, 2, 3, 4, 5};
    Array<int> a(arr, 5);
    a.print();
    return 0;
}

خروجی قطعه‌کُد بالا به این صورت است:

1 2 3 4 5

سوالی که می‌تواند پیش بیاید، این است که آیا templatesها می‌توانند بیش‌از یک آرگومان (arguments) دریافت کنند؟ 

بله! درست مانند پارامتر‌های عادی، الگوه‌ها می‌توانند بیش از یک نوع داده را به عنوان آرگومان بپذیرند. مثالی که در ادامه آورده شده، این موضوع نشان می‌دهد:

#include<iostream>
using namespace std;
 
template<class T, class U>
class A  {
    T x;
    U y;
public:
    A() {    cout<<"Constructor Called"<<endl;   }
};
 
int main()  {
   A<char, char> a;
   A<int, double> b;
   return 0;
}

خروجی قطعه‌کُد بالا به این صورت خواهد بود:

Constructor Called
Constructor Called

آیا می‌توان مقدار پیش‌فرضی (default value) را برای آرگومان‌های الگو یا همان template در نظر گرفت؟ 

بله، مانند پارامتر‌های عادی، می‌توان آرگومان‌های پیش‌فرض برای templates‌ها در نظر گرفت. مثالی که در ادامه آورده شده است، این موضوع را نشان می‌دهد:

#include<iostream>
using namespace std;
 
template<class T, class U = char>
class A  {
public:
    T x;
    U y;
    A() {   cout<<"Constructor Called"<<endl;   }
};
 
int main()  {
   A<char> a;  // This will call A<char, char>  
   return 0;
}

خروجی قطعه‌کُد بالا به این صورت است:

Constructor Called

چه تفاوتی بین سربارگذاری تابع (function overloading) و الگو‌ها (templates) است؟‌ 

هر دوی آن‌ها (function overloading و templates) مثالی‌هایی از ویژگی شی‌ءگرایی (OOP) که پُلی‌مورفیسم (polymorphism) نام دارد، هستند. سربارگذاری تابع، زمانی که توابع مختلف عملیات‌های یکسانی را انجام می‌دهند، استفاده شده است. الگو‌ها یا همان templates‌ها زمانی که توابع مختلف عملیات‌های یکسانی انجام می‌دهند، استفاده می‌شود.

چه اتفاقی می‌افتد، زمانی که یک عضو استاتیک (static member) در یک کلاس یا تابع الگو باشد؟ 

هر‌ نمونه از یک template، حاوی متغییر استاتیک (‌ static‌ ) خودش است. برای جزئیات بیشتر در این‌مورد، به Templates and Static variables رجوع کنید.

الگو‌ی اختصاصی چیست؟ 

الگوی‌ اختصاصی، این اجازه را به ما می‌دهند که برای یک نوع داده‌ی خاص، کُد متفاوتی داشته باشیم. برای جزئیات بیشتر در این‌مورد، به Template Specialization  رجوع کنید.

می‌توان پارامتر‌های  غیرنوع ( nontype ) برای الگو‌ها در نظر گرفت؟ 

می‌توان آرگومان‌های غیرنوع (non-type) برای templates‌ها در نظر گرفت. پارامتر‌های Non-type عمدتا برای مشخص‌ کردن مقادیر max یا min یا هر مقدار ثابت‌ دیگری برای یک نمونه‌ی خاص از یک الگو (template) استفاده شده است. مهم‌ترین چیز درمورد پارمتر‌های non-type این است که باید ثابت یا const باشند. کامپایلر باید مقدار پارمتر‌های non-type را در زمان کامپایل بداند. به این دلیل که کامپایلر باید برای یک مقدار non-type مشخص شده، در زمان کامپایل، کلاس‌ها و توابع را ایجاد کند. در برنامه‌ی زیر، اگر 10000 یا 25 را با یک متغیر جایگزین کنید، یک خطای کامپایلر دریافت خواهید کرد. لطفا اینجا را مشاهده کنید.

#include<iostream>
using namespace std;
 
template<class T, class U = char>
class A  {
public:
    T x;
    U y;
    A() {   cout<<"Constructor Called"<<endl;   }
};
 
int main()  {
   A<char> a;  // This will call A<char, char>  
   return 0;
}

خروجی قطعه‌کُد بالا به این صورت است:

10
1

منبع: وب سایت geeksforgeeks

نویسنده شوید
دیدگاه‌های شما

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.