Я использую два стека для реализации класса очереди. Мой заголовочный файл выглядит так:
#ifndef _MyQueue_h
#define _MyQueue_h
using namespace std;
template <typename T>
class MyQueue {
public:
MyQueue();
~MyQueue();
void enqueue(T element);
T peek();
void dequeue();
int size();
bool empty();
private:
int count;
stack<T> stk1;
stack<T> stk2;
};
# include "MyQueue.cpp"# endif
И мой файл cpp (реализация) выглядит так:
#include <stack>
#include "MyQueue.h"using namespace std;
template <typename T>
MyQueue<T>::MyQueue()
{
count = 0;
}
template <typename T>
MyQueue<T>::~ MyQueue()
{
}
template <typename T>
void MyQueue<T>::enqueue(T element)
{
stk1.push(element);
count ++;
}
(другие функции опущены).
Однако, используя Xcode 4.5, он продолжает говорить, что мои функции (MyQueue, ~ MyQueue, enqueue, peek и т. Д.) Переопределены. Может ли кто-нибудь помочь мне уточнить, где я их переопределил?
Спасибо
Вы пытаетесь что-то, что мне действительно не нравится. Это притворство.
Удалить #include "MyQueue.cpp"
, замените его содержимым MyQueue.cpp, удалите файл MyQueue.cpp. Теперь все будет работать.
Вы пытаетесь сделать вид, что шаблон кода можно разбить на заголовочный файл и файл реализации. Но поскольку этого нельзя обмануть, включив файл реализации в заголовочный файл. Это менее запутанно, если вы не обманываете и не притворяетесь, и у вас есть только один файл, заголовочный файл, в котором есть все.
Точная причина, по которой вы получаете переопределение, заключается в том, что вы компилируете свой файл cpp, который включает в себя ваш заголовочный файл, который снова включает ваш файл cpp. Таким образом, содержимое файла cpp компилируется дважды.
Проблема в том, что при компиляции файла cpp файл cpp включает в себя .h
файл, а затем .h
файл включает в себя .cpp
файл. Тогда у вас есть два копии кода cpp в том же «модуле перевода» одновременно.
Но есть несколько разных решений, это зависит от вашей конечной цели.
Самое простое и гибкое решение — просто удалить все элементы шаблона из .cpp
файл и положить его в .h
файл вместо. Вы можете подумать, что это плохой дизайн, вас, вероятно, учили хранить объявления и определения в отдельных файлах, но именно так обычно реализуются шаблоны. (Добро пожаловать в странный и прекрасный мир шаблонов C ++!)
Но, возможно, это должны быть «частные» шаблоны, которые будут использоваться только от одного .cpp
файл. В этом случае лучше всего просто переместить все из .h
подать в .cpp
файл.
Есть третий подход, который, на мой взгляд, не получает достаточного внимания. Сначала удалите #include "MyQueue.cpp"
от твоего .h
файл и перекомпилировать. Вполне возможно, что это просто сработает для вас. Тем не мение, если ваш проект имеет несколько .cpp
файлы, вы можете получить ошибки компоновщика о undefined reference to MyQueue<string> :: MyQueue()
, (где string
заменяется тем, что вы помещаете в свою очередь. Эти ошибки компоновщика могут быть исправлены путем размещения template MyQueue<string>;
на конец файла, который имеет определения шаблонов (ваш MyQueue.cpp
). Это означает, что вы должны сделать это один раз для каждого типа, который вы планируете хранить в своей очереди, но вы можете увидеть это как преимущество, так как оно поможет вам вспомнить, какие типы поддерживаются вашей очередью.
В C и C ++ #include ведет себя как копирование и вставка.
Каждый раз, когда вы видите
#inclue "file"
к нему следует относиться так, как будто вы буквально перепечатали весь файл в этом месте.
Поэтому, если вы скомпилируете MyQueue.cpp, препроцессор добавит содержимое MyQueue.h,
что само по себе дублирует MyQueue.cpp, о чем свидетельствует
# include "MyQueue.cpp"
а затем следует родной контент MyQueue.cpp.
Итак, результат
# include "MyQueue.cpp"
внутри MyQueue.h, так же, как если бы вы написали один большой файл с содержимым
MyQueue.h, MyQueue.cpp и MyQueue.cpp снова. (конечно, с включением стека)
Вот почему компилятор жаловался на переопределение функций.
Дубликат вставлен из
# include "MyQueue.cpp"
может также содержать строку
#include "MyQueue.h"
но я думаю, что включенные охранники (ifndef, endif) защищены от рекурсивного расширения, так как это
не кажется проблемой.
Я хотел бы отметить, что размещение всего кода реализации и кода объявления в одном файле для шаблонов — не единственное решение, как предлагают другие.
Вам просто нужно помнить, что шаблоны генерируются во время компиляции и включать их везде, где они необходимы. подобно Аарон указал, Вы даже можете принудительно сгенерировать шаблон для определенного типа или функции, чтобы он был доступен для всех модулей.
Таким образом, определение функции может быть встроено в произвольный модуль, а остальные модули не будут жаловаться, что функция не определена.
Мне нравится объявлять небольшие шаблоны и шаблоны интерфейсов в заголовочных файлах
и помещать большие реализации в специальные файлы, которые являются просто прославленными заголовками. Вы можете добавить какое-то специальное расширение, например .tpp .cppt или что-то еще, чтобы напомнить себе, что это код, который вы должны где-то включить (что я и делаю).Это подходящая альтернатива хранению больших реализаций в заголовочных файлах, которые должны быть вставлены просто для ссылки на тип (или сигнатуру функции). И это работает абсолютно нормально, в течение многих лет.
Так, например, когда я буду готов скомпилировать свою большую программу, у меня может быть файл с именем структура.cpp, который я назначу для реализации множества небольших структур, которые я использую, а также для создания экземпляров всех шаблонов для моего проекта.
все остальные .cpp файлы в проекте должны включать «mylib / template_structs.h», чтобы создавать экземпляры шаблонов и вызывать с ними функции. тогда как структура.cpp должна включать только «mylib / template_structs.cppt», который, в свою очередь, может включать в себя template_structs.h
в противном случае структуры .cpp должен будет включить это в первую очередь.Если структура.cpp вызывает все функции, которые будут вызывать любые другие файлы .cpp для этого шаблона, тогда мы закончим, если нет, то вам понадобится дополнительный шаг, например
template class mynamespace::queue<int> ;
чтобы сгенерировать все остальные определения, понадобятся остальные модули проекта.
когда вы что-то включаете, он заменяет включенный файл кодом внутри, поэтому при вызове
#include «MyQueue.cpp» заменяет его на файл cpp, затем ваш файл cpp переопределяет его.
Избавление от линии исправит это.