C++. Шаблони класів. Повна спеціалізація. Часткова спеціалізація

Шаблони класів. Повна спеціалізація. Часткова спеціалізація

Перед вивченням даної теми рекомендується ознайомитись з наступною темою:

Зміст


Пошук на інших ресурсах:

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;
}

 


Споріднені теми