BoolStack< T >::isFull () const
{
return( m_pTop - m_pData ) == m_size;
}
//*****************************************************************************
#endif // _STACK_HPP_
Создадим тестовую программу на основе разработанного шаблона. Однажды записав обобщенную структуру данных, можно применять ее для практически любого типа. Требования, которые накладываются к типу T такой реализацией состоят в возможности копирования значения, присвоения, а также наличия конструктора по умолчанию (для выделения массива).
test_stack.cpp
//*****************************************************************************
#include"stack.hpp"
#include<string>
#include<cassert>
//*****************************************************************************
intmain ()
{
// Стек целых чисел
Stack< int> s1;
s1.push( 5 );
assert( ! s1.isEmpty() && s1.top() == 5 );
// Стек действительных чисел
Stack< double> s2;
s2.push( 2.5 );
assert( ! s2.isEmpty() && s2.top() == 2.5 );
// Стек объектов-строк
Stack< std::string > s3;
s3.push( "Hello" );
assert( ! s3.isEmpty() && s3.top() == "Hello" );
// Даже стек стеков целых чисел!
Stack< Stack< int> > s4;
s4.push( Stack< int >() );
s4.top().push( 5 );
assert( ! s4.isEmpty() && !s4.top().isEmpty() && s4.top().top() == 5 );
}
//*****************************************************************************
В таком варианте реализации невозможно присвоение между экземплярами Stack< int> и Stack< double>, поскольку после инстанцирования они являются разными несвязанными классами. Однако для контейнеров значений такое поведение может быть весьма полезным на практике, разумеется, с преобразованием типа хранящихся значений. Чтобы разрешить такое копирование, нужно усовершенствовать конструктор копий, сделав его шаблоном-членом (member template). Аналогичный прием можно применить к конструктору, принимающему список инициализаторов, чтобы получить возможность создания стека на основе конвертируемых данных другого типа, например, создать стек целых чисел по массиву действительных.
В заголовочной части объявление конструктора копий и оператора копирующего присвоения следует видоизменить, а также объявить любой другой экземпляр того же класса другом для удобного доступа к его private-части:
template< typenameT >
classStack
{
//------------------------------------------------------------------------
public:
// ...
// Объявляем любой экземпляр Stack другом любого другого экземпляра Stack
template< typename> friend classStack;
// Конструктор по обобщенному списку инициалиазторов
template< typenameU >
Stack ( std::initializer_list< U > _l );
// Конструктор копий - шаблон-член, ожидает тип U, потенциально U != T
template< typenameU >
Stack ( constStack< U >& _s );
// ...
// Оператор копирующего присвоения - также шаблон-член
template< typenameU >
Stack< T > & operator= ( constStack< U >& _s );
// ...
};
Видоизмененная реализация будет выглядеть следующим образом:
template< typenameT >
template< typenameU > // Да, два раза template, это не ошибка!
Stack< T >::Stack ( std::initializer_list< U > _l )
: Stack( _l.size() )
{
// Перебираем список инициализаторов данных типа U
for( constU & x : _l )
// Помещаем в стек данные типа T путем преобразования U к T
push( ( constT & ) x );
}
template< typenameT >
template< typenameU > // Да, два раза template, это не ошибка!
Stack< T >::Stack ( constStack< U > & _s )
: m_size( _s.m_size )
{
// Выделяем массив для хранения данных стека
m_pData = newT[ m_size ] ;
m_pTop = m_pData;
// Поочередно вставлем элементы
intnActual = _s.m_pTop - _s.m_pData;
for( inti = 0; i < nActual ; i++ )
push( _s.m_pData[ i ] ); // неявное преобразование типа от U к T
}
//*****************************************************************************
template< typenameT >
template< typenameU > // Да, два раза template, это не ошибка!
Stack< T >& Stack< T >::operator = ( constStack< U >& _s )
{
// Защита от присвоения на самого себя - несколько усложняется,
// т.к. нельзя просто сравнивать адреса двух разных классов Stack<T> и Stack<U>!
if( (const void* )( this) ==( const void* )( & _s ) )
return* this;
// Освобождаем старый блок и выделяем новый
delete[] m_pData;
m_size = _s.m_size;
m_pData = newT[ m_size ];
// Копируем полезные данные из другого стека
intnActual = _s.m_pTop - _s.m_pData;
for( inti = 0; i < nActual; i++ )
m_pData[ i ] = _s.m_pData[ i ]; // неявное преобразование типа от U к T
// Выставляем вершину стека в аналогичную другому стеку позицию
m_pTop = m_pData + nActual;
// Возвращаем ссылку на себя
return* this;
}
Теперь желаемое преобразование становится возможным:
test_stack_conversions.cpp
//*****************************************************************************
#include "stack.hpp"
#include <string>
#include <cassert>
//*****************************************************************************
intmain ()
{
// Создаем стек целых чисел на основе массива действительных чисел
Stack< int> s = { 1.5, 2.5, 3.5 };
assert( ! s.isEmpty() && s.top() == 3 ); // 3.5 округлится до 3
// Стек действительных чисел
Stack< double> s1;
s1.push( 2.5 );
// Создаем стек целых чисел на основе стека действительных чисел!
Stack< int> s2 = s1;
assert( !s2.isEmpty() && s2.top() == 2 ); // 2.5 округлится до 2
// Стек строк в стиле C
Stack< const char* > s3;
s3.push( "Hello" );
// Присваиваем стеку строк std::string,
// неявно конструируя такие объекты из строкового литерала
Stack< std::string > s4;
s4 = s3;
assert( !s4.isEmpty() && s4.top() == "Hello" );
}
//*****************************************************************************
Следует отметить, что такой трюк не подходит для конструкторов перемещения и оператора перемещающего присвоения. Внутреннее представление стеков для разных типов-аргументов отличается, и нельзя просто “забрать” внутреннее представление другого не связанного объекта.
При необходимости, шаблоны-члены могут существовать в обычных классах:
#include <iostream>
classTest
{
public:
template< typenameT >
voidf ( constT & _val )
{
std::cout << _val << std::endl;
}
};
intmain ()
{
Test t;
t.f( 5 ); // вызов Test::f< int>
t.f( 2.5 ); // вызов Test::f< double>
}
Каждый уникальный тип аргумент породит новый метод в классе Test.