#c #templates
#c #шаблоны
Вопрос:
template <class T>
class ListNode {
public:
T* data;
ListNode<T>* next;
}
Допустим, у меня есть шаблон узла списка, и где-то в коде я хочу получить копию данных — имеется в виду не копия указателя на данные (T *), а новый указатель (T *), который будет указывать на другое место в памяти, содержащее там ту же информацию.
Как я могу это сделать при использовании шаблонов C ? Как я могу скопировать (* данные), если я не знаю, какой тип T.
Комментарии:
1. Как вы это делаете, когда знаете тип? Это работает идентично для типов шаблонов.
2. Вы знаете этот тип …
T
3. @Konrad: Или это в равной степени не работает для не-шаблонов. Все ответы на данный момент вызывают нарезку.
Ответ №1:
Компилятор знает тип T . Чего он не знает, так это на сколько экземпляров T указано. С точки зрения получения практической реализации, коротким ответом было бы не использовать типы указателей. Вместо этого используйте контейнеры. Поскольку вы все равно копируете данные узла, накладные расходы минимальны. Явный пример ниже:
template <class T>
class ListNode {
public:
// use a vector as the container
std::vector<T> data;
ListNode<T>* next;
// initializer from pointer primitive
ListNode(const T* ps,size_t elements)
{
data.assign(ps,ps elements);
}
// copy templated instance
ListNode(const ListNodeamp; arg)
{
data = arg.data;
}
// assignment
ListNodeamp; operator=(const ListNodeamp; arg)
{
if (this != amp;arg)
{
data = arg.data;
}
return *this;
}
};
Фактическое использование было бы похоже на это:
{
const char* ps = "Hello World";
ListNode<char> ln1(ps,strlen(ps));
ListNode<char> ln2 = ln1;
}
Вы, конечно, можете получить гораздо более сложные решения, но все они будут включать отслеживание количества экземпляров типа T, на которые указывает ваш указатель.
Комментарии:
1. 1: несмотря на ограниченность примера, все еще простая альтернатива, и простота хороша.
Ответ №2:
T должен быть копируемым, чтобы вы могли делать
template <class T>
ListNode<T>::ListNode(const ListNode<T>amp; src)
{
...
// given a preexisting copy, src, create a new T to
// perform a copy
T* destT = new T(*srcT);
}
Если у T есть конструктор копирования, это сработает. Если этого не произойдет, компилятор выдаст вам ошибку (вероятно, очень загадочную)
Комментарии:
1. NM … Это связано с кофе, и это не с вашей стороны.
2. почему компилятор должен выдавать ошибку; Не будет ли компилятор генерировать конструктор копирования по умолчанию?
Ответ №3:
Используйте operator= или конструктор копирования. Стандартной практикой является то, что оба из них будут создавать копию объекта.
Так, например:
T *p = other_pointer;
*p = *data;
Или
T* copy = new T(*data);
Комментарии:
1. Для значений «создать копию», которые допускают нарезку.
2. @Ben: Стандартной функции виртуального копирования не существует, поэтому вы берете то, что можете получить.
Ответ №4:
При создании копии шаблонного типа практически вам не нужно беспокоиться о типе как таковом и выполнять эту задачу в конструкторе копирования или операторе присваивания этого типа:
template <class T>
class ListNode {
public:
T* data;
ListNode<T>* next;
T* getCopy() { new T(*data); } // get copy of "data"
};
Предположим, вы используете это ListNode<T>
для class A
, тогда у вас может быть определен конструктор копирования для A (а также оператор присваивания):
class A {
public:
A(const Aamp; copy);
};
Теперь при вызове ListNode<T>::getCopy()
он вызовет конструктор копирования A внутренне.
Комментарии:
1. -1: Потенциальная утечка памяти каждый раз, когда кто-то запрашивает копию, вероятно, не является хорошим дизайном… оберните это в интеллектуальный указатель или, предпочтительно, предоставьте явный конструктор копирования и / или оператор присваивания.
2. @AJG85, я не думаю, что это утечка памяти. Запрашивающий запрашивает копию
T*
, которая может храниться вdata
элементе другого объекта и может быть удалена в деструкторе.3. Вы возвращаете копию следующим образом:
T getCopy() const { *data; }
то, что вы делаете, это выделяете память в куче и возвращаете указатель на нее в общедоступном методе, который может легко вызвать утечки памяти и потерянные указатели … тот, кто вызывает ваш метод, должен сохранить указатель и вызватьdelete
на нем, чтобы избежать утечки памяти без исключений.