简体   繁体   English

C++错误信息重定义函数

[英]C++ Error message redefinition of functions

I am using two stacks to implement a queue class.我正在使用两个堆栈来实现一个队列类。 My header file looks like:我的头文件看起来像:

#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

And my cpp (implementation) file looks like:我的 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 ++;
}

(other functions omitted). (省略其他功能)。

However, using Xcode 4.5, it keeps saying that my functions (MyQueue, ~MyQueue, enqueue, peek, etc.) are redefined.但是,使用 Xcode 4.5,它一直说我的函数(MyQueue、~MyQueue、enqueue、peek 等)被重新定义。 Can anyone help me to clarify where have I redefined them?谁能帮我澄清一下我在哪里重新定义了它们?

Thank you谢谢

You're trying something which I really don't like.你正在尝试我真的不喜欢的东西。 It's a pretence.这是一个幌子。

Remove #include "MyQueue.cpp" , replace it with the content of MyQueue.cpp, delete the file MyQueue.cpp.删除#include "MyQueue.cpp" ,替换为 MyQueue.cpp 的内容,删除文件 MyQueue.cpp。 Now everything will work.现在一切都会好起来的。

You are trying to pretend the template code can be split into header file and implementation file.您试图假装模板代码可以拆分为头文件和实现文件。 But because it can't you have to cheat by including the implementation file in the header file.但是因为它不能通过在头文件中包含实现文件来作弊。 It's less confusing if you don't cheat or pretend and just have one file, the header file, with everything in it.如果您不作弊或假装并且只有一个文件,即头文件,其中包含所有内容,则不会那么令人困惑。

The precise reason that you get a redefinition is that you are compiling your cpp file, which includes your header file, which includes your cpp file again.您获得重新定义的确切原因是您正在编译您的 cpp 文件,其中包括您的头文件,其中又包括您的 cpp 文件。 So the content of the cpp file gets compiled twice.所以 cpp 文件的内容被编译了两次。

In C and C++, #include behaves like a copy and paste.在 C 和 C++ 中,#include 的行为类似于复制和粘贴。 Everytime you see每次看到

#include "file" 

it should be treated as if you literally retyped the whole file in that one spot.应该将其视为您真的在那个位置重新输入了整个文件。 So if you compile MyQueue.cpp, the preprocessor will prepend the contents of MyQueue.h, which itself tacks on a duplicate of MyQueue.cpp evidenced by因此,如果您编译 MyQueue.cpp,预处理器将预先添加 MyQueue.h 的内容,该内容本身附加在 MyQueue.cpp 的副本上

#include "MyQueue.cpp" 

and then follows the native content of MyQueue.cpp.然后遵循 MyQueue.cpp 的本机内容。

So the result of所以结果是

#include "MyQueue.cpp"

inside MyQueue.h, is the same as if you had written one big file with the contents of MyQueue.h, MyQueue.cpp and MyQueue.cpp again.在 MyQueue.h 中,就像您再次使用 MyQueue.h、MyQueue.cpp 和 MyQueue.cpp 的内容编写了一个大文件一样。 (with the include of stack in there as well of course) That is why the compiler complained about functions getting redefined. (当然也包含堆栈)这就是编译器抱怨函数被重新定义的原因。

The Duplicate inserted from the从插入的重复项

#include "MyQueue.cpp" 

might also contain the line也可能包含该行

#include "MyQueue.h"

but I think the include guards (ifndef,endif) protected against a recursive expansion since that did not seem to be an issue.但我认为包含保护 (ifndef,endif) 可以防止递归扩展,因为这似乎不是问题。

I would like to point out that putting all the implementation code and declaration code in the same file for templates is not the only solution, as others suggest.我想指出,正如其他人所建议的那样,将所有实现代码和声明代码放在同一个模板文件中并不是唯一的解决方案。

You just have to remember that templates are generated at compile time and include them wherever they are needed.您只需要记住模板是在编译时生成的,并将它们包含在需要的地方。 Like Aaron has pointed out , you can even force generate a template for a specific type or function so it's accessible to all units.就像Aaron 指出的那样,您甚至可以强制为特定类型或功能生成模板,以便所有单位都可以访问它。

In this way, the definition of a function can be embedded in an arbitrary module and the rest of the modules won't complain that a function isn't defined.通过这种方式,函数的定义可以嵌入到任意模块中,而其余模块不会抱怨函数未定义。

I like to declare small templates and template interfaces in header files and put large implementations in special files that are just glorified headers.我喜欢在头文件中声明小模板和模板接口,并将大实现放在只是美化头文件的特殊文件中。 You could put some special extension like .tpp .cppt or whatever to remind yourself that it is code you have to include somewhere (which is what I do).你可以放一些特殊的扩展名,比如 .tpp .cppt 或其他任何东西来提醒自己这是你必须在某处包含的代码(这就是我所做的)。

It is a suitable alternative to storing large implementations in header files that must be pasted around just to refer to the type (or function signature).它是将大型实现存储在头文件中的合适替代方案,这些头文件必须粘贴以引用类型(或函数签名)。 And it works absolutely fine, for years now.它工作得很好,多年来。

So for example, when I am ready to compile my big program, I might have a file called structures.cpp that I designate to implement lots of small structures I use, as well as instantiate all the templates for my project.例如,当我准备编译我的大程序时,我可能有一个名为structures.cpp 的文件,我指定它来实现我使用的许多小结构,并为我的项目实例化所有模板。

all the other .cpp files in the project need to include "mylib/template_structs.h" in order to create instances of templates and call functions with them.项目中的所有其他 .cpp 文件都需要包含“mylib/template_structs.h”,以便创建模板实例并使用它们调用函数。 whereas structures.cpp only needs to include "mylib/template_structs.cppt" which in turn may include template_structs.h or else structures.cpp would have to include that as well first.而structures.cpp 只需要包含“mylib/template_structs.cppt”,而后者又可能包含template_structs.h,否则structures.cpp 也必须首先包含它。

If structures.cpp calls all the functions that any other .cpp files would call for that template then we are done, if not, then you'd need the extra step of something like如果 Structures.cpp 调用任何其他 .cpp 文件为该模板调用的所有函数,那么我们就完成了,如果没有,那么您需要执行类似的额外步骤

template class mynamespace::queue<int> ;

to generate all the other definitions the rest of the project's modules would need.生成项目其余模块所需的所有其他定义。

The problem is that, when compiling the cpp file, the cpp file includes the .h file and then the .h file includes the .cpp file.问题是,在编译cpp文件时,cpp文件包含.h文件,然后.h文件包含.cpp文件。 Then you have two copies of the cpp code in the same 'translation unit' at the same time.然后,您在同一“翻译单元”中同时拥有两个cpp 代码副本。

But there are a few different solutions to this, it depends what your ultimate goal is.但是对此有几种不同的解决方案,这取决于您的最终目标是什么。

  1. The simplest, and most flexible solution is simply to remove all the template stuff from the .cpp file and put it into the .h file instead.最简单、最灵活的解决方案是简单地从.cpp文件中删除所有模板内容并将其放入.h文件中。 You might think this is bad design, you've probably been taught to keep declarations and definitions in separate files, but this is how templates are usually implemented.您可能认为这是糟糕的设计,您可能已经被教导将声明和定义保存在单独的文件中,但这就是模板通常的实现方式。 (Welcome to the weird and wonderful world of C++ templates!) (欢迎来到奇妙而奇妙的 C++ 模板世界!)

  2. But, perhaps these are to be 'private' templates, only to be used from one .cpp file.但是,也许这些是“私有”模板,只能在一个.cpp文件中使用。 In that case, the best thing to do is simply to move everything from the .h file into the .cpp file.在这种情况下,最好的办法就是将.h文件中的所有内容移动到.cpp文件中。

  3. There is a third approach, which doesn't get enough attention in my opinion.还有第三种方法,我认为它没有得到足够的关注。 First, remove the #include "MyQueue.cpp" from your .h file, and recompile.首先,从.h文件中删除#include "MyQueue.cpp" ,然后重新编译。 It's quite possible that will just work for you.这很可能只适合你。 However, if your project has multiple .cpp files, you might get linker errors about undefined reference to MyQueue<string> :: MyQueue() .但是,如果您的项目有多个.cpp文件,您可能会收到有关undefined reference to MyQueue<string> :: MyQueue()链接器错误。 (where string is replaced with whatever you are putting in your queue. These linker errors can be fixed by placing template MyQueue<string>; at the end of the file that has the definitions of the templates (your MyQueue.cpp ). This means you have to do this once for each type that you plan to store in your queue, but you might see this as an advantage as it helps you remember which types are supported by your queue. (其中string被替换为您放入队列中的任何内容。可以通过将template MyQueue<string>;放置在具有模板定义的文件(您的MyQueue.cpp )的末尾来修复这些链接器错误。这意味着您必须为计划存储在队列中的每种类型执行一次此操作,但您可能认为这是一个优势,因为它可以帮助您记住队列支持哪些类型。

when you include something it replaces the included file with the code within so when you call #include "MyQueue.cpp" it replaces that with the cpp file, then your cpp file redefines it.当您包含某些内容时,它会将包含的文件替换为其中的代码,因此当您调用 #include "MyQueue.cpp" 时,它会将其替换为 cpp 文件,然后您的 cpp 文件会重新定义它。 Getting rid of the line will fix it.摆脱这条线将解决它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM