【问题标题】:Why shouldn't C++ operator new/delete/variants be in header files?为什么 C++ 运算符 new/delete/variants 不应该在头文件中?
【发布时间】:2017-01-12 10:50:41
【问题描述】:

有人能解释一下这个 C++ 编译错误的性质吗?我正在涉足/学习重载全局运算符 new、delete 及其变体。我读过coupleofarticlesonthesubject,但我找不到一个似乎专门解决这个问题的。

守则

foo.h:

#ifndef foo_h
#define foo_h

void* operator new(size_t);
void* operator new[](size_t);

void operator delete(void*);
void operator delete[](void*);

#endif // foo_h

foo.cpp:

#include <foo.h>
#include <iostream>

void* operator new(size_t size) { return NULL; }
void* operator new[](size_t size) { return NULL; }

void operator delete(void* p) { }
void operator delete[](void* p) { }

编译错误

>g++ -g -std=c++14 -I./ -c foo.cpp -o foo.o
In file included from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/ext/new_allocator.h:33:0,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/x86_64-pc-cygwin/bits/c++allocator.h:33,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/bits/allocator.h:46,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/string:41,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/bits/locale_classes.h:40,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/bits/ios_base.h:41,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/ios:42,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/ostream:38,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/iostream:39,
                 from foo.cpp:2:
/usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/new:116:41: error: declaration of ‘void operator delete(void*) noexcept’ has a different exception specifier
   __attribute__((__externally_visible__));
                                         ^
In file included from foo.cpp:1:0:
./foo.h:8:6: error: from previous declaration ‘void operator delete(void*)’
 void operator delete(void* p);
      ^
In file included from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/ext/new_allocator.h:33:0,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/x86_64-pc-cygwin/bits/c++allocator.h:33,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/bits/allocator.h:46,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/string:41,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/bits/locale_classes.h:40,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/bits/ios_base.h:41,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/ios:42,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/ostream:38,
                 from /usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/iostream:39,
                 from foo.cpp:2:
/usr/lib/gcc/x86_64-pc-cygwin/5.4.0/include/c++/new:118:41: error: declaration of ‘void operator delete [](void*) noexcept’ has a different exception specifier
   __attribute__((__externally_visible__));
                                         ^
In file included from foo.cpp:1:0:
./foo.h:9:6: error: from previous declaration ‘void operator delete [](void*)’
 void operator delete[](void* p);
      ^

我认为与此问题相关的一些奇怪之处:

  • 如果我在foo.cpp中注释掉#include &lt;iostream&gt;,编译成功
  • 如果我注释掉foo.h 中的函数声明,并且只在foo.cpp 中保留它们的定义(同时还保留#include &lt;iostream&gt;),则编译成功。

我有一些模糊的怀疑;也许回答者会通过他们的回答来确认:

  • 这个错误提到了exception specifier,所以我想也许通过覆盖这些运算符中的任何一个,我有义务覆盖它们的兄弟姐妹的整个套件。但是,添加operator delete(void*, const std::nothrow_t&amp;) 声明和定义并没有改变编译错误。我也不认为重写这些运算符中的任何一个会迫使编码人员实现所有这些运算符,但我错了吗?
  • 我在 StackOverflow 之外阅读了一篇文章,其中提到这些运算符只能包含在一个“翻译单元”中,因此不应包含在头文件中。我不明白什么是翻译单元,那篇文章也没有解释它是什么。如果这与这个问题有关,请解释什么是“翻译单元”以及为什么需要从头文件中排除函数声明 - 这似乎与我之前的所有 C++ 编码经验背道而驰。

感谢您提供任何见解。

【问题讨论】:

    标签: c++ gcc delete-operator translation-unit


    【解决方案1】:

    您看到的问题是由于以下声明的差异造成的。

    库将operator delete 函数声明为:

    void operator delete(void*) noexcept;
    void operator delete [](void*) noexcept;
    

    当您将它们声明为:

    void operator delete(void*);
    void operator delete [](void*);
    

    您应该使用

    ,而不是在您的 .h 文件中声明它们
    #include <new>
    

    如果您有权访问 C++11 标准的 18.6 动态内存管理部分以获取有关该主题的更多信息。

    翻译单元通常是一个 .cpp 文件。延伸阅读:What is a "translation unit" in C++.

    【讨论】:

    • 您的回答让我意识到编译器错误实际上是在告诉我到底是什么问题。我没有仔细检查它,但谢谢你;你的回答帮助我缩小了差距。
    • 这很奇怪,因为我无法重现相同的错误,并且我复制了完全相同的代码,该代码已发布在问题中。如果这确实是问题所在,为什么我没有收到错误消息?顺便说一句,我正在使用 VS 2015。
    • @MuhammadAhmad,我不知道你为什么没有收到任何错误。值得寻找。
    • @MuhammedAhmad,我怀疑您看到的差异是因为您的编译器(cl.exe?)。编译器(和链接器)行为肯定存在细微差别(例如,参见stackoverflow.com/questions/39159681/…)。不幸的是,我没有 VS 2015 进行交叉验证,但 R Sahu 似乎能够验证我的观察。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-04
    • 2011-05-27
    • 2013-03-12
    • 2014-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多