Шаблони класів. Повна спеціалізація. Часткова спеціалізація
Перед вивченням даної теми рекомендується ознайомитись з наступною темою:
Зміст
- 1. Поняття повної та часткової спеціалізації. Загальна форма
- 2. Приклад реалізації повної та часткової спеціалізації. Клас Value<T>
- Споріднені теми
Пошук на інших ресурсах:
1. Поняття повної та часткової спеціалізації. Загальна форма
У найпростішому випадку, оголошення шаблонного класу, який оперує узагальненим типом T, має наступний вигляд:
template <class T> class ClassName { // тіло класу // ... }
тут
- ClassName – ім’я шаблонного класу;
- T – узагальнене ім’я типу, що може бути використане в класі ClassName. Це ім’я може бути й іншим.
На основі вищевказаного шаблону можна оголошувати конкретні екземпляри класу, які безпосередньо прив’язані до деякого типу
ClassName<int> objInt; // формується клас для типу int ClassName<double> objDouble; // формується клас для типу double
Екземпляри класу objInt та objDouble формуються на етапі компіляції. Такий спосіб утворення коду є частковим випадком статичного поліморфізму.
У мові C++ допускається оголошувати так звану “спеціалізацію” шаблонного класу. Спеціалізація дозволяє відредагувати створення екземпляру узагальненого класу для особливих випадків. Такими особливими випадками можуть бути:
- заборона використання класу для деяких типів. Наприклад, якщо клас оперує числами, то рядкові типи тут недоречні (неможливо виконувати математичні обчислення над символами);
- передбачена реалізація для покажчика на узагальнений тип. Наприклад, для узагальненого типу T потрібно реалізувати оголошення покажчика на цей тип (T*) з врахуванням, що тип char* може бути рядком символів і обробляється по особливому;
- інші випадки.
Спеціалізація шаблонного класу може бути двох видів:
- повна спеціалізація;
- часткова спеціалізація.
Повна спеціалізація має наступну загальну форму:
template <> class ClassName<type> { // тіло класу // ... }
тут
- ClassName – ім’я класу, для якого оголошується повна спеціалізація. Важливо: перед оголошенням повної спеціалізації клас повинен бути оголошений як шаблонний;
- type – тип, який визначає спеціалізацію шаблонного типу, який оголошений в шаблоні класу ClassName. Тип type може бути будь-який стандартний тип (int, float, double тощо) чи тип, визначений у програмі.
Враховуючи вищенаведену загальну форму, оголошення екземпляру класу при повній спеціалізації може бути таким
ClassName<type> obj;
тут
- obj – ім’я екземпляру (об’єкту) класу;
- type – тип, що визначений при оголошенні повної спеціалізації класу.
Часткова спеціалізація має таку загальну форму:
template <class T> class ClassName<specialization_T> { // тіло класу // ... }
тут
- ClassName – ім’я шаблонного класу, для якого визначається часткова спеціалізація. Важливо: шаблон класу ClassName повинен бути обов’язково оголошений в програмі;
- T – тип класу, що визначений в шаблоні;
- specialization_T – спеціалізація типу T. Це може бути, наприклад, покажчик на тип T, який визначений як T*.
Враховуючи вищенаведену загальну форму часткової спеціалізації, оголошення об’єкту класу може бути наступним:
ClassName<specialization_T> obj;
тут
- obj – ім’я об’єкту класу;
- specialization_T – спеціалізація типу T (наприклад T*, T** тощо);
- ClassName – ім’я шаблонного класу.
⇑
2. Приклад реалізації повної та часткової спеціалізації. Клас Value<T>
У прикладі демонструються:
- оголошення шаблонного класу Value<T>;
- повна спеціалізація класу Value<T> для типу char*;
- повна спеціалізація класу Value<T> для типу void*;
- часткова спеціалізація класу Value<T> для типу T* – покажчик на будь-який тип крім char*.
#include <iostream> using namespace std; // Тема: Шаблонні класи. Повна спеціалізація. Часткова спеціалізація. // 1. Звичайне оголошення шаблонного класу // Задано шаблонний клас Value<T>, // який зберігає деяке значення. template <class T> class Value { private: T value; public: // Конструктор з 1 параметром Value(T value = 0) : value(value) { } // Метод тестування void Print(string msg) { cout << msg << " = " << value << endl; } // Метод, що повертає значення T Get() { return value; } }; // 2. Повна спеціалізація для типу char* // Повна спеціалізація для типу char*, // ознакою повної спеціалізації є рядок template<> template <> class Value<char*> { private: char* s; // Копіювання s <= _s void Copy(const char* _s) { int len = 0; while (_s[len++] != '\0'); s = new char[len + 1]; for (int i = 0; i <= len; i++) s[i] = _s[i]; } public: // Конструктор Value(const char* _s = nullptr) { // Копіювання s <= _s if (_s != nullptr) Copy(_s); } // Метод тестування void Print(string msg) { cout << msg << " => " << s << endl; } // Деструктор ~Value() { delete[] s; } // Конструктор копіювання - делегує повноваження конструктору з 1 параметром Value(const Value<char*>& obj) : Value<char*>(obj.s) { } // Оператор копіювання Value& operator=(const Value<char*>& obj) { // 1. Знищити пам'ять, виділену під попередній рядок delete[] s; // 2. Виділити пам'ять під новий рядок, копіювання s <= obj.s Copy(obj.s); // 3. Повернути поточний об'єкт return *this; } // Метод Get() - повертає покажчик на char char* Get() { return s; } }; // 3. Повна спеціалізація для void*, // тут параметри шаблону відсутні <> так само template <> class Value<void*> { private: void* p; public: Value() { p = nullptr; } Value(void* _p) : p(_p){ } // Метод Get() void* Get() { return p; } }; // 4. Часткова спеціалізація, // яка використовується для усіх покажчиків на тип T template <class T> class Value<T*> //: private Value<void*> // усі покажчики void* можна привести до будь-якого T* { private: T* p; public: Value(T* p) { // робимо копію this->p = new T; *(this->p) = *p; } void Print(string msg) { cout << msg << " = " << *p << endl; } // Конструктор копіювання Value(const Value& obj) : Value(obj.p) {} // Оператор копіювання Value& operator=(const Value& obj) { *p = *obj.p; return *this; } // Деструктор ~Value() { delete p; } // Метод Get() - не повертає копію T* Get() { return p; } }; int main() { // Тест класу Value<T> // 1. Звичайне оголошення шаблонного класу Value<int> vi1 = 22; vi1.Print("vi1"); // 2. Повна спеціалізація для типу char* // 2.1. Оголосити екземпляр Value<char*> vc1 = "Hello, world!"; vc1.Print("vc1"); // 2.2. Тест конструктора копіювання Value<char*> vc2 = vc1; vc2.Print("vc2"); // 2.3. Тест оператора копіювання Value<char*> vc3; vc3 = vc1; vc3.Print("vc3"); // 2.4. Метод Get() char* vc4 = vc2.Get(); cout << "vc4 = " << vc4 << endl; // 3. Повна спеціалізація для типу void* // Тут викликається повна спеціалізація для void* Value<void*> p2; void* pvoid = p2.Get(); // 4. Часткова спеціалізація для типу int* // 4.1. Оголосити екземпляр Value<int*> pi1 = new int(50); pi1.Print("pi1"); // 4.2. Тест конструктора копіювання Value<int*> pi2 = pi1; pi2.Print("pi2"); // 4.3. Тест оператора копіювання pi2 = pi1; pi2.Print("pi2"); // 4.4. Метод Get() int* pi = pi2.Get(); cout << "pi = " << *pi << endl; }
Споріднені теми
- Шаблон класу. Ключове слово template. Переваги використання шаблонів. Аргументи в шаблонах. Приклади
⇑