C++ Сообщение об ошибке переопределение функций

Я использую два стека для реализации класса очереди. Мой заголовочный файл выглядит так:

#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 и т. Д.) Переопределены. Может кто-нибудь помочь мне уточнить, где я их переопределил?

Спасибо

4 ответа

Вы пытаетесь что-то, что мне действительно не нравится. Это притворство.

Удалить #include "MyQueue.cpp", замените его содержимым MyQueue.cpp, удалите файл MyQueue.cpp. Теперь все будет работать.

Вы пытаетесь сделать вид, что шаблон кода можно разбить на заголовочный файл и файл реализации. Но поскольку этого нельзя обмануть, включив файл реализации в заголовочный файл. Это менее запутанно, если вы не обманываете и не притворяетесь, и у вас есть только один файл, заголовочный файл со всем этим.

Точная причина, по которой вы получаете переопределение, заключается в том, что вы компилируете свой файл cpp, который включает ваш заголовочный файл, который снова включает ваш файл cpp. Таким образом, содержимое файла 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"

но я думаю, что include guard (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> ;

чтобы сгенерировать все остальные определения, понадобятся остальные модули проекта.

Проблема в том, что при компиляции файла cpp файл cpp включает в себя .h файл, а затем .h файл включает в себя .cpp файл. Затем у вас есть две копии кода cpp в одном "модуле перевода" одновременно.

Но есть несколько различных решений, это зависит от вашей конечной цели.

  1. Самое простое и гибкое решение - просто удалить все элементы шаблона из .cpp файл и положить его в .h файл вместо. Вы можете подумать, что это плохой дизайн, вас, вероятно, учили хранить объявления и определения в отдельных файлах, но так обычно реализуются шаблоны. (Добро пожаловать в странный и прекрасный мир шаблонов C++!)

  2. Но, возможно, это должны быть "частные" шаблоны, которые будут использоваться только от одного .cpp файл. В этом случае лучше всего просто переместить все из .h подать в .cpp файл.

  3. Есть третий подход, который, на мой взгляд, не привлекает достаточного внимания. Сначала удалите #include "MyQueue.cpp" от твоего .h файл и перекомпилировать. Вполне возможно, что это просто сработает для вас. Однако, если ваш проект имеет несколько .cpp файлы, вы можете получить ошибки компоновщика о undefined reference to MyQueue<string> :: MyQueue(), (где string заменяется тем, что вы помещаете в свою очередь. Эти ошибки компоновщика могут быть исправлены путем размещения template MyQueue<string>; в конце файла, который имеет определения шаблонов (ваш MyQueue.cpp). Это означает, что вы должны сделать это один раз для каждого типа, который вы планируете хранить в своей очереди, но вы можете увидеть это как преимущество, так как оно поможет вам вспомнить, какие типы поддерживаются вашей очередью.

Когда вы что-то включаете, он заменяет включенный файл кодом внутри, поэтому, когда вы вызываете #include "MyQueue.cpp", он заменяет это на файл cpp, тогда ваш файл cpp переопределяет его. Избавление от линии исправит это.

Другие вопросы по тегам