我可以写C++吗? 没有标题的代码(重复的函数声明)?

发布于 2024-07-23 17:19:17 字数 70 浏览 8 评论 0原文

在使用 C++ 编程时,有什么方法可以不必编写两次函数声明(头文件),并且仍然保留相同的编译可扩展性、调试清晰度和设计灵活性?

Is there any way to not have to write function declarations twice (headers) and still retain the same scalability in compiling, clarity in debugging, and flexibility in design when programming in C++?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(25

请恋爱 2024-07-30 17:19:17

使用Lzz。 它需要一个文件并自动为您创建一个 .h 和 .cpp,并将所有声明/定义放在正确的位置。

Lzz 确实非常强大,可以处理 99% 的完整 C++ 语法,包括模板、专业化等。

更新 150120:

较新的 C++ '11/14 语法只能在 Lzz 函数体内使用。

Use Lzz. It takes a single file and automatically creates a .h and .cpp for you with all the declarations/definitions in the right place.

Lzz is really very powerful, and handles 99% of full C++ syntax, including templates, specializations etc etc etc.

Update 150120:

Newer C++ '11/14 syntax can only be used within Lzz function bodies.

南冥有猫 2024-07-30 17:19:17

我开始写C的时候也有同样的感觉,所以我也研究了这个。 答案是,是的,这是可能的,但不,你不想这样做。

首先是。

在 GCC 中,您可以这样做:

// foo.cph

void foo();

#if __INCLUDE_LEVEL__ == 0
void foo() {
   printf("Hello World!\n");
}
#endif

这达到了预期的效果:您将标头和源代码合并到一个可以包含和链接的文件中。

然后使用 no:

只有当编译器可以访问整个源代码时,这才有效。 当编写想要分发但保持闭源的库时,不能使用此技巧。 要么分发完整的 .cph 文件,要么必须编写一个单独的 .h 文件来配合 .lib。 尽管也许您可以使用宏预处理器自动生成它。 但它会变得毛茸茸的。

第二个原因是你不想要这个,这可能是最好的一个:编译速度。 通常,C 源文件仅在文件本身发生更改或其包含的任何文件发生更改时才需要重新编译。

  • C 文件可以经常更改,但更改仅涉及重新编译更改的一个文件。
  • 头文件定义接口,因此不应经常更改。 然而,当它们这样做时,它们会触发包含它们的每个源文件的重新编译。

当所有文件都合并头文件和源文件时,每次更改都会触发所有源文件的重新编译。 即使现在,C++ 也不以其快速编译时间而闻名,想象一下当整个项目每次都必须重新编译时会发生什么。 然后将其推断为一个包含数百个具有复杂依赖关系的源文件的项目......

I felt the same way when I started writing C, so I also looked into this. The answer is that yes, it's possible and no, you don't want to.

First with the yes.

In GCC, you can do this:

// foo.cph

void foo();

#if __INCLUDE_LEVEL__ == 0
void foo() {
   printf("Hello World!\n");
}
#endif

This has the intended effect: you combine both header and source into one file that can both be included and linked.

Then with the no:

This only works if the compiler has access to the entire source. You can't use this trick when writing a library that you want to distribute but keep closed-source. Either you distribute the full .cph file, or you have to write a separate .h file to go with your .lib. Although maybe you could auto-generate it with the macro preprocessor. It would get hairy though.

And reason #2 why you don't want this, and that's probably the best one: compilation speed. Normally, C sources files only have to be recompiled when the file itself changes, or any of the files it includes changes.

  • The C file can change frequently, but the change only involves recompiling the one file that changed.
  • Header files define interfaces, so they shouldn't change as often. When they do however, they trigger a recompile of every source file that includes them.

When all your files are combined header and source files, every change will trigger a recompile of all source files. C++ isn't known for its fast compile times even now, imagine what would happen when the entire project had to be recompiled every time. Then extrapolate that to a project of hundreds of source files with complicated dependencies...

弱骨蛰伏 2024-07-30 17:19:17

抱歉,在 C++ 中不存在消除标头的“最佳实践”:这是一个坏主意,句号。 如果您非常讨厌它们,您有三个选择:

  • 熟悉 C++ 内部结构和您正在使用的任何编译器; 您将遇到与普通 C++ 开发人员不同的问题,并且您可能需要在没有大量帮助的情况下解决这些问题。
  • 选择一种你可以“正确”使用而不会感到沮丧的语言
  • 获取一个为你生成它们的工具; 您仍然会有标题,但可以节省一些打字工作

Sorry, but there's no such thing as a "best practice" for eliminating headers in C++: it's a bad idea, period. If you hate them that much, you have three choices:

  • Become intimately familiar with C++ internals and any compilers you're using; you're going to run into different problems than the average C++ developer, and you'll probably need to solve them without a lot of help.
  • Pick a language you can use "right" without getting depressed
  • Get a tool to generate them for you; you'll still have headers, but you save some typing effort
佞臣 2024-07-30 17:19:17

在他的文章C++ 中对契约设计的简单支持,佩德罗·格雷罗说道:

通常,C++ 类有两个
文件:头文件和
定义文件。 我们应该写在哪里
断言:在头文件中,
因为断言是规范?
或者在定义文件中,因为它们
是可执行的吗? 或者两者都运行
不一致的风险(以及
重复工作)? 我们推荐,
相反,我们放弃
传统风格,并废除
定义文件,仅使用
头文件,就好像所有函数都是
内联定义,非常像 Java
埃菲尔铁塔也是如此。

这真是太激烈了
改变了 C++ 的常态
冒着扼杀这一努力的风险
一开始。 另一方面,维持
每个类有两个文件是这样
尴尬,那迟早是C++
开发环境就会出现
这对我们隐藏了这一点,让我们
专注于我们的课程,没有
不必担心他们在哪里
已存储。

那是2001年。我同意了。 现在已经是 2009 年了,仍然没有出现“向我们隐藏这一点、让我们能够专注于课堂的开发环境”。 相反,较长的编译时间是常态。


注意:上面的链接现在似乎已失效。 这是对该出版物的完整引用,它出现在作者网站的 出版物 部分:

Pedro Guerreiro ,C++ 中对契约设计的简单支持,TOOLS USA 2001,会议记录,第 24-34 页,IEEE,2001。

In his article Simple Support for Design by Contract in C++, Pedro Guerreiro stated:

Usually, a C++ class comes in two
files: the header file and the
definition file. Where should we write
the assertions: in the header file,
because assertions are specification?
Or in the definition file, since they
are executable? Or in both, running
the risk of inconsistency (and
duplicating work)? We recommend,
instead, that we forsake the
traditional style, and do away with
the definition file, using only the
header file, as if all functions were
defined inline, very much like Java
and Eiffel do.

This is such a drastic
change from the C++ normality that it
risks killing the endeavor at the
outset. On the other hand, maintaining
two files for each class is so
awkward, that sooner or later a C++
development environment will come up
that hides that from us, allowing us
to concentrate on our classes, without
having to worry about where they are
stored.

That was 2001. I agreed. It is 2009 now and still no "development environment that hides that from us, allowing us to concentrate on our classes" has come up. Instead, long compile times are the norm.


Note: The link above seems to be dead now. This is the full reference to the publication, as it appears in the Publications section of the author's website:

Pedro Guerreiro, Simple Support for Design by Contract in C++, TOOLS USA 2001, Proceedings, pages 24-34, IEEE, 2001.

撩动你心 2024-07-30 17:19:17

没有实用的方法来绕过标头。 您唯一能做的就是将所有代码放入一个大的 C++ 文件中。 这最终会导致难以维护的混乱,所以请不要这样做。

目前,C++ 头文件是一种必要的邪恶。 我不喜欢他们,但没有办法绕过他们。 不过,我很乐意看到有关该问题的一些改进和新想法。

顺便说一句 - 一旦你习惯了它,它就不再那么那么糟糕了。C++(以及任何其他语言)有更多令人讨厌的事情。

There is no practical way to get around headers. The only thing you could do is to put all code into one big c++ file. That will end up in an umaintainable mess, so please don't do it.

At the moment C++ header-files are a nessesary evil. I don't like them, but there is no way around them. I'd love to see some improvements and fresh ideas on the problem though.

Btw - once you've got used to it it's not that bad anymore.. C++ (and any other language as well) has more anoying things.

も让我眼熟你 2024-07-30 17:19:17

我见过像你这样的人所做的就是在标题中写入所有内容 。 这提供了您所需的属性,只需编写一次方法配置文件。

就我个人而言,我认为有很好的理由说明为什么最好将声明和定义分开,但是如果这让您感到苦恼,那么有一种方法可以做您想做的事情。

What I have seen some people like you do is write everything in the headers. That gives your desired property of only having to write the method profiles once.

Personally I think there are very good reasons why it is better to separate declaration and definition, but if this distresses you there is a way to do what you want.

红ご颜醉 2024-07-30 17:19:17

有头文件生成软件。我从未使用过它,但它可能值得研究一下。 例如,查看mkhdr! 据称它会扫描 C 和 C++ 文件并生成适当的头文件。

(但是,正如 Richard 指出的,这似乎限制了您使用某些 C++ 功能。请参阅 Richard 的答案 就在这个线程中。)

There's header file generation software. I've never used it, but it might be worth looking into. For instance, check out mkhdr! It supposedly scans C and C++ files and generates the appropriate header files.

(However, as Richard points out, this seems to limit you from using certain C++ functionality. See Richard's answer instead here right in this thread.)

偷得浮生 2024-07-30 17:19:17

实际上,您必须编写函数声明两次(一次在头文件中,一次在实现文件中)。 函数的定义(也称为实现)将在实现文件中写入一次。

您可以将所有代码写入头文件中(这实际上是 C++ 泛型编程中非常常用的做法),但这意味着包含该头文件的每个 C/CPP 文件都意味着从这些头文件重新编译实现。

如果您正在考虑类似于 C# 或 Java 的系统,那么在 C++ 中这是不可能的。

You have to write function declaration twice, actually (once in header file, once in implementation file). The definition (AKA implementation) of the function will be written once, in the implementation file.

You can write all the code in header files (it is actually a very used practice in generic programming in C++), but this implies that every C/CPP file including that header will imply recompilation of the implementation from those header files.

If you are thinking to a system similar to C# or Java, it is not possible in C++.

玩套路吗 2024-07-30 17:19:17

C++ 20 模块解决了这个问题。 不再需要复制粘贴! 只需将代码写入单个文件并使用“导出”导出内容即可。

export module mymodule;

export int myfunc() {
    return 1
}

在这里阅读有关模块的更多信息: https://en.cppreference.com/w/cpp/语言/模块

在撰写此答案时(2022 年 2 月),这些编译器支持它:
module support

请参阅此处了解支持的编译器:
https://en.cppreference.com/w/cpp/compiler_support

查看此答案如果你想在 CMake 中使用模块:
https://stackoverflow.com/a/71119196/7910299

C++ 20 modules solve this problem. There is no need for copy-pasting anymore! Just write your code in a single file and export things using "export".

export module mymodule;

export int myfunc() {
    return 1
}

Read more about modules here: https://en.cppreference.com/w/cpp/language/modules

At the time of writing this answer (2022 Feb), these compilers support it:
module support

See here for the supported compilers:
https://en.cppreference.com/w/cpp/compiler_support

See this answer if you want to use modules with CMake:
https://stackoverflow.com/a/71119196/7910299

好倦 2024-07-30 17:19:17

实际上...您可以将整个实现写入一个文件中。 模板类全部定义在头文件中,没有 cpp 文件。

您还可以使用任何您想要的扩展名进行保存。 然后在 #include 语句中,您将包含您的文件。

/* mycode.cpp */
#pragma once
#include <iostreams.h>

class myclass {
public:
  myclass();

  dothing();
};

myclass::myclass() { }
myclass::dothing()
{
  // code
}

然后在另一个文件中

/* myothercode.cpp */
#pragma once
#include "mycode.cpp"

int main() {
   myclass A;
   A.dothing();
   return 0;
}

您可能需要设置一些构建规则,但它应该可以工作。

Actually... You can write the entire implementation in a file. Templated classes are all defined in the header file with no cpp file.

You can also save then with whatever extensions you want. Then in #include statements, you would include your file.

/* mycode.cpp */
#pragma once
#include <iostreams.h>

class myclass {
public:
  myclass();

  dothing();
};

myclass::myclass() { }
myclass::dothing()
{
  // code
}

Then in another file

/* myothercode.cpp */
#pragma once
#include "mycode.cpp"

int main() {
   myclass A;
   A.dothing();
   return 0;
}

You may need to setup some build rules, but it should work.

伤痕我心 2024-07-30 17:19:17

还没有人提到 Visual Studio 2012 下的 Visual-Assist X。

它有一堆菜单和热键,您可以使用它们来减轻维护标头的痛苦:

  • “创建声明”将函数声明从当前函数复制到 .hpp 文件中。
  • “重构..更改签名”允许您用一个命令同时更新.cpp和.h文件。
  • Alt-O 允许您立即在 .cpp 和 .h 文件之间切换。

Nobody has mentioned Visual-Assist X under Visual Studio 2012 yet.

It has a bunch of menus and hotkeys that you can use to ease the pain of maintaining headers:

  • "Create Declaration" copies the function declaration from the current function into the .hpp file.
  • "Refactor..Change signature" allows you to simultaneously update the .cpp and .h file with one command.
  • Alt-O allows you to instantly flip between .cpp and .h file.
塔塔猫 2024-07-30 17:19:17

可以避免标头。 完全地。 但我不推荐它。

您将面临一些非常具体的限制。 其中之一是您将无法拥有循环引用(您将无法让 Parent 类包含指向 ChildNode 类实例的指针,并且 ChildNode 类也包含指向 Parent 类实例的指针。它必须是其中之一。)

还有其他限制最终会让您的代码变得非常奇怪。 坚持标题。 您将学会真正喜欢它们(因为它们提供了一个类可以做什么的快速概要)。

You can avoid headers. Completely. But I don't recommend it.

You'll be faced with some very specific limitations. One of them is you won't be able to have circular references (you won't be able to have class Parent contain a pointer to an instance of class ChildNode, and class ChildNode also contain a pointer to an instance of class Parent. It'd have to be one or the other.)

There are other limitations which just end up making your code really weird. Stick to headers. You'll learn to actually like them (since they provide a nice quick synopsis of what a class can do).

稀香 2024-07-30 17:19:17

提供 rix0rrr 的流行答案的变体:

// foo.cph

#define INCLUDEMODE
#include "foo.cph"
#include "other.cph"
#undef INCLUDEMODE

void foo()
#if !defined(INCLUDEMODE)
{
   printf("Hello World!\n");
}
#else
;
#endif

void bar()
#if !defined(INCLUDEMODE)
{
    foo();
}
#else
;
#endif

我不推荐这样做,我认为这种结构证明了以死记硬背为代价消除内容重复。 我想这会让复制意大利面变得更容易? 这并不是真正的美德。

与所有其他这种性质的技巧一样,对函数体的修改仍然需要重新编译所有文件,包括包含该函数的文件。 非常仔细的自动化工具可以部分避免这种情况,但它们仍然必须解析源文件进行检查,并仔细构建以在没有不同的情况下不重写其输出。

对于其他读者:我花了几分钟试图找出这种格式的包含守卫,但没有想出任何好的办法。 评论?

To offer a variant on the popular answer of rix0rrr:

// foo.cph

#define INCLUDEMODE
#include "foo.cph"
#include "other.cph"
#undef INCLUDEMODE

void foo()
#if !defined(INCLUDEMODE)
{
   printf("Hello World!\n");
}
#else
;
#endif

void bar()
#if !defined(INCLUDEMODE)
{
    foo();
}
#else
;
#endif

I do not recommend this, bit I think this construction demonstrates the removal of content repetition at the cost of rote repetition. I guess it makes copy-pasta easier? That's not really a virtue.

As with all the other tricks of this nature, a modification to the body of a function will still require recompilation of all files including the file containing that function. Very careful automated tools can partially avoid this, but they would still have to parse the source file to check, and be carefully constructed to not rewrite their output if it's no different.

For other readers: I spent a few minutes trying to figure out include guards in this format, but didn't come up with anything good. Comments?

九局 2024-07-30 17:19:17

我理解你的问题。 我想说,C++ 的主要问题是它从 C 继承的编译/构建方法。C/C++ 头文件结构是在编码涉及较少定义和较多实现的时代设计的。 别向我扔瓶子,但事情就是这样的。

从那时起,面向对象编程已经征服了世界,世界更多的是定义而不是实现。 结果,包含头文件使得使用这样一种语言变得非常痛苦,其中基本集合(例如 STL 中的集合)是用模板制作的,这对于编译器来说是非常困难的工作。 当涉及到 TDD、重构工具和通用开发环境时,预编译头的所有这些魔力并没有多大帮助。

当然,C 程序员并没有受到太多的困扰,因为他们没有大量编译器的头文件,因此他们对非常简单的低级编译工具链感到满意。 对于 C++ 来说,这是一段痛苦的历史:无休止的前向声明、预编译头、外部解析器、自定义预处理器等。

然而,许多人没有意识到 C++ 是唯一为高低端提供强大而现代的解决方案的语言。水平问题。 说你应该选择具有适当反射和构建系统的其他语言很容易,但我们必须牺牲低级编程解决方案并且我们需要使用低级语言混合来使事情变得复杂,这是毫无意义的使用一些基于虚拟机/JIT 的解决方案。

我有这个想法已经有一段时间了,拥有一个基于“单元”的 C++ 工具链将是地球上最酷的事情,类似于 D 中的工具链。问题出现在跨平台部分:对象文件能够存储任何信息,这没有问题,但是由于在 Windows 上目标文件的结构与 ELF 不同,因此实现一个跨平台解决方案来存储和处理中途文件将是一件很痛苦的事情- 编译单元。

I understand your problems. I would say that the C++ main problem is the compilation/build method that it inherited from the C. The C/C++ header structure has been designed in times when coding involved less definitions and more implementations. Don't throw bottles on me, but that's how it looks like.

Since then the OOP has conquered the world and the world is more about definitions then implementations. As the result, including headers makes pretty painful to work with a language where the fundamental collections such as the ones in the STL made with templates which are notoriously difficult job for the compiler to deal with. All those magic with the precompiled headers doesn't help so much when it comes to TDD, refactoring tools, the general development environment.

Of course C programmers are not suffering from this too much since they don't have compiler-heavy header files and so they are happy with the pretty straightforward, low-level compilation tool chain. With C++ this is a history of suffering: endless forward declarations, precompiled headers, external parsers, custom preprocessors etc.

Many people, however, does not realize that the C++ is the ONLY language that has strong and modern solutions for high- and low-level problems. It's easy to say that you should go for an other language with proper reflection and build system, but it is non-sense that we have to sacrifice the low-level programming solutions with that and we need to complicate things with low-level language mixed with some virtual-machine/JIT based solution.

I have this idea for some time now, that it would be the most cool thing on earth to have a "unit" based c++ tool-chain, similar to that in D. The problem comes up with the cross-platform part: the object files are able to store any information, no problem with that, but since on windows the object file's structure is different that of the ELF, it would be pain in the ass to implement a cross-platform solution to store and process the half-way-compilation units.

神妖 2024-07-30 17:19:17

阅读完所有其他答案后,我发现缺少正在进行的工作来添加对 C++ 标准中模块的支持。 它不会进入 C++0x,但目的是在以后的技术审查中解决它(而不是等待新标准,这需要很长时间)。

正在讨论的提案是 N2073

不好的部分是,即使使用最新的 c++0x 编译器,您也无法得到这一点。 你必须等待。 同时,您必须在仅标头库中定义的唯一性和编译成本之间进行折衷。

After reading all the other answers, I find it missing that there is ongoing work to add support for modules in the C++ standard. It will not make it to C++0x, but the intention is that it will be tackled in a later Technical Review (rather than waiting for a new standard, that will take ages).

The proposal that was being discussed is N2073.

The bad part of it is that you will not get that, not even with the newest c++0x compilers. You will have to wait. In the mean time, you will have to compromise between the uniqueness of definitions in header-only libraries and the cost of compilation.

独闯女儿国 2024-07-30 17:19:17

据我所知,没有。 标头是 C++ 作为语言的固有部分。 不要忘记,前向声明允许编译器仅包含指向已编译对象/函数的函数指针,而不必包含整个函数(您可以通过声明内联函数来解决这个问题(如果编译器喜欢的话)。

如果你真的,真的,真的讨厌制作标题,而是编写一个 perl 脚本来自动生成它们,但我不确定我是否会推荐它。

As far as I know, no. Headers are an inherent part of C++ as a language. Don't forget that forward declaration allows the compiler to merely include a function pointer to a compiled object/function without having to include the whole function (which you can get around by declaring a function inline (if the compiler feels like it).

If you really, really, really hate making headers, write a perl-script to autogenerate them, instead. I'm not sure I'd recommend it though.

以为你会在 2024-07-30 17:19:17

您可以不使用标题。 但是,为什么要花精力试图避免专家多年来精心制定的最佳实践呢?

当我写 basic 的时候,我非常喜欢行号。 但是,我不会考虑尝试将它们塞入 C++ 中,因为那不是 C++ 的方式。 标题也是如此......我确信其他答案解释了所有的推理。

You can do without headers. But, why spend effort trying to avoid carefully worked out best practices that have been developed over many years by experts.

When I wrote basic, I quite liked line numbers. But, I wouldn't think of trying to jam them into C++, because that's not the C++ way. The same goes for headers... and I'm sure other answers explain all the reasoning.

楠木可依 2024-07-30 17:19:17

完全可以不用头文件进行开发。 可以直接包含源文件:

#include "MyModule.c"

这样做的主要问题是循环依赖关系之一(即:在 C 中,您必须在调用函数之前声明它)。 如果您完全自上而下地设计代码,这不是问题,但如果您不习惯这种设计模式,可能需要一些时间才能理解这种设计模式。

如果您绝对必须具有循环依赖项,则可能需要考虑专门为声明创建一个文件,并将其包含在其他所有内容之前。 这有点不方便,但仍然比为每个 C 文件都有一个标头污染要少。

我目前正在使用这种方法来开发我的一个主要项目。 以下是我经历过的优点的详细说明:

  • 源树中的文件污染少得多。
  • 更快的构建时间。 (编译器只生成一个目标文件,main.o)
  • 更简单的 make 文件。 (编译器只生成一个目标文件,main.o)
  • 无需“make clean”。 每个构建都是“干净的”。
  • 更少的样板代码。 更少的代码=更少的潜在错误。

我发现 Gish(Cryptic Sea 的一款游戏,Edmund McMillen)在其自己的源代码中使用了该技术的变体。

It's completely possible to develop without header files. One can include a source file directly:

#include "MyModule.c"

The major issue with this is one of circular dependencies (ie: in C you must declare a function before calling it). This is not an issue if you design your code completely top-down, but it can take some time to wrap ones head around this sort of design pattern if you're not used to it.

If you absolutely must have circular dependencies, one may want to consider creating a file specifically for declarations and including it before everything else. This is a little inconvenient, but still less pollution than having a header for every C file.

I am currently developing using this method for one of my major projects. Here is a breakdown of advantages I've experienced:

  • Much less file pollution in your source tree.
  • Faster build times. (Only one object file is produced by the compiler, main.o)
  • Simpler make files. (Only one object file is produced by the compiler, main.o)
  • No need to "make clean". Every build is "clean".
  • Less boiler plate code. Less code = less potential bugs.

I've discovered that Gish (a game by Cryptic Sea, Edmund McMillen) used a variation on this technique inside its own source code.

卖梦商人 2024-07-30 17:19:17

您可以仔细布局函数,以便所有依赖函数在其依赖关系之后进行编译,但正如 Nils 所暗示的那样,这是不切实际的。

Catalin(请原谅缺少变音符号)还建议了一种更实用的替代方法,即在头文件中定义方法。 这实际上在大多数情况下都可以工作......特别是如果你的头文件中有保护措施以确保它们只包含一次。

我个人认为头文件+声明函数对于“理解”新代码更可取,但我认为这是个人偏好......

You can carefully lay out your functions so that all of the dependent functions are compiled after their dependencies, but as Nils implied, that is not practical.

Catalin (forgive the missing diacritical marks) also suggested a more practical alternative of defining your methods in the header files. This can actually work in most cases.. especially if you have guards in your header files to make sure they are only included once.

I personally think that header files + declaring functions is much more desirable for 'getting your head around' new code, but that is a personal preference I suppose...

燃情 2024-07-30 17:19:17

出于实际的目的,不,这是不可能的。 从技术上讲,是的,可以。 但是,坦率地说,这是对语言的滥用,你应该适应这种语言。 或者转向 C# 之类的东西。

For practical purposes no, it's not possible. Technically, yes, you can. But, frankly, it's an abuse of the language, and you should adapt to the language. Or move to something like C#.

笑红尘 2024-07-30 17:19:17

最好的做法是使用头文件,一段时间后它就会适合你。
我同意只有一个文件更容易,但它也可能导致糟糕的编码。

其中有些东西,虽然感觉别扭,却能让你得到更多的收获。

举个例子,考虑一下指针、按值/按引用传递参数...等等,

对我来说,头文件允许我保持项目结构正确

It is best practice to use the header files, and after a while it will grow into you.
I agree that having only one file is easier, but It also can leed to bad codeing.

some of these things, althoug feel awkward, allow you to get more then meets the eye.

as an example think about pointers, passing parameters by value/by reference... etc.

for me the header files allow-me to keep my projects properly structured

烈酒灼喉 2024-07-30 17:19:17

学会认识到头文件是一件好事。 它们将代码对其他用户的显示方式与其实际执行操作的实现方式分开。

当我使用某人的代码时,我现在确实希望必须费力地完成所有实现以查看类上的方法。 我关心代码做了什么,而不是它是如何做的。

Learn to recognize that header files are a good thing. They separate how codes appears to another user from the implementation of how it actually performs its operations.

When I use someone's code I do now want to have to wade through all of the implementation to see what the methods are on a class. I care about what the code does, not how it does it.

旧时光的容颜 2024-07-30 17:19:17

由于重复,这已经“复活”了......

无论如何,标头的概念都是有价值的,即将接口与实现细节分开。 标题概述了如何使用类/方法,而不是它是如何工作的。

缺点是标题中的详细信息以及所有必要的解决方法。 这些是我认为的主要问题:

  • 依赖项生成。 当头文件被修改时,任何包含该头文件的源文件都需要重新编译。 当然,问题在于找出哪些源文件实际使用它。 当执行“干净”构建时,通常需要将信息缓存在某种依赖关系树中以供以后使用。

  • 包括警卫。 好吧,我们都知道如何编写这些,但在一个完美的系统中,这是没有必要的。

  • 私人详细信息。 在类中,您必须将私有详细信息放入标头中。 是的,编译器需要知道类的“大小”,但在完美的系统中,它能够在稍后的阶段绑定它。 这会导致各种解决方法,例如 pImpl 和使用抽象基类,即使您只有一个实现,只是因为您想隐藏依赖项。

完美的系统将使用

  • 单独的类定义和声明
  • 两者之间的明确绑定,以便编译器知道类声明及其定义在哪里,并且知道类的大小。
  • 您使用类声明,而不是预处理器#include 声明。 编译器知道在哪里找到一个类。 完成“使用类”后,您可以使用该类名而无需限定它。

我很想知道D是怎么做到的。

关于是否可以在没有标头的情况下使用 C++,我会说不,您需要它们作为抽象基类和标准库。 除此之外,没有它们你也可以过得去,尽管你可能不想这样做。

This has been "revived" thanks to a duplicate...

In any case, the concept of a header is a worthy one, i.e. separate out the interface from the implementation detail. The header outlines how you use a class / method, and not how it does it.

The downside is the detail within headers and all the workarounds necessary. These are the main issues as I see them:

  • dependency generation. When a header is modified, any source file that includes this header requires recompilation. The issue is of course working out which source files actually use it. When a "clean" build is performed it is often necessary to cache the information in some kind of dependency tree for later.

  • include guards. Ok, we all know how to write these but in a perfect system it would not be necessary.

  • private details. Within a class, you must put the private details into the header. Yes, the compiler needs to know the "size" of the class, but in a perfect system it would be able to bind this in a later phase. This leads to all kinds of workaround like pImpl and using abstract base classes even when you only have one implementation just because you want to hide a dependency.

The perfect system would work with

  • separate class definition and declaration
  • A clear bind between these two so the compiler would know where a class declaration and its definition are, and would know what the size of a class.
  • You declare using class rather than pre-processor #include. The compiler knows where to find a class. Once you have done "using class" you can use that class name without qualifying it.

I'd be interested to know how D does it.

With regards to whether you can use C++ without headers, I would say no you need them for abstract base classes and standard library. Aside from that you could get by without them, although you probably would not want to.

紫罗兰の梦幻 2024-07-30 17:19:17

我可以编写没有标头的 C++ 代码吗

阅读更多关于 C++,例如 使用 C++ 编程 书,然后是 C+11 标准 n3337

是的,因为预处理器(概念上)生成没有标头的代码。

如果您的 C++ 编译器是 GCC 并且您正在编译 翻译单元 foo.cc 考虑运行g++ -O -Wall -Wextra -C -E foo .cc> foo.ii; 发出的文件 foo.ii 不包含任何预处理器指令,可以使用 g++ -O foo.ii -o foo-bin 编译为 foo- bin 可执行文件(至少在 Linux 上)。 另请参阅高级 Linux 编程

在 Linux 上,以下 C++ 文件

// file ex.cc
extern "C" long write(int fd, const void *buf, size_t count);
extern "C" long strlen(const char*);
extern "C" void perror(const char*);
int main (int argc, char**argv)
{
   if (argc>1) 
     write(1, argv[1], strlen(argv[1]);
   else 
     write(1, __FILE__ " has no argument",
              sizeof(__FILE__ " has no argument"));
   if (write(1, "\n", 1) <= 0) {
     perror(__FILE__);
     return 1;
   }
   return 0;
}

可能是使用 GCC 作为 g++ ex.cc -O ex-bin 编译为可执行文件ex-bin 执行时会显示一些内容。

在某些情况下,值得使用另一个程序

(可能是 SWIG 生成一些 C++ 代码, ANTLR, BisonRefPerSysGPP,或您自己的 C++ 代码生成器)并配置您的 构建自动化工具(例如ninja-buildGNU make) 来处理这种情况。 请注意,GCC 10 的源代码有十几个 C++ 代码生成器。

对于 GCC,您有时可能会考虑编写自己的 GCC 插件 来分析您(或其他人)的 C++ 代码(例如,在 GIMPLE 级别)。 另请参阅(2020 年秋季)CHARIOTDECODER 欧洲项目。 您还可以考虑使用 Clang 静态分析器Frama-C++

Can I write C++ code without headers

Read more about C++, e.g. the Programming using C++ book then the C+11 standard n3337.

Yes, because the preprocessor is (conceptually) generating code without headers.

If your C++ compiler is GCC and you are compiling your translation unit foo.cc consider running g++ -O -Wall -Wextra -C -E foo.cc > foo.ii; the emitted file foo.ii does not contain any preprocessor directive, and could be compiled with g++ -O foo.ii -o foo-bin into a foo-bin executable (at least on Linux). See also Advanced Linux Programming

On Linux, the following C++ file

// file ex.cc
extern "C" long write(int fd, const void *buf, size_t count);
extern "C" long strlen(const char*);
extern "C" void perror(const char*);
int main (int argc, char**argv)
{
   if (argc>1) 
     write(1, argv[1], strlen(argv[1]);
   else 
     write(1, __FILE__ " has no argument",
              sizeof(__FILE__ " has no argument"));
   if (write(1, "\n", 1) <= 0) {
     perror(__FILE__);
     return 1;
   }
   return 0;
}

could be compiled using GCC as g++ ex.cc -O ex-bin into an executable ex-bin which, when executed, would show something.

In some cases, it is worthwhile to generate some C++ code with another program

(perhaps SWIG, ANTLR, Bison, RefPerSys, GPP, or your own C++ code generator) and configure your build automation tool (e.g. ninja-build or GNU make) to handle such a situation. Notice that the source code of GCC 10 has a dozen of C++ code generators.

With GCC, you might sometimes consider writing your own GCC plugin to analyze your (or others) C++ code (e.g. at the GIMPLE level). See also (in fall 2020) CHARIOT and DECODER European projects. You could also consider using the Clang static analyzer or Frama-C++.

李不 2024-07-30 17:19:17

历史上使用听者文件有两个原因。

  1. 在编译想要使用的程序时提供符号
    库或附加文件。

  2. 隐藏部分实现; 将事情保密。

例如,假设您有一个不想暴露给其他人的功能
程序的一部分,但想在您的实现中使用。 在那里面
在这种情况下,您可以将函数写入 CPP 文件中,但将其省略
头文件的。 你可以用变量和任何东西来做到这一点
想要在您不想要的浸渍中保密
暴露于该源代码的 conumbers。 在其他编程中
lanugases 有一个“public”关键字,允许模块部分
避免暴露于程序的其他部分。 在 C 和 C++ 中
文件级别不存在这样的设施,因此使用头文件
相反。

头文件并不完美。 使用“#include”只是复制内容
您提供的任何文件。 当前工作的单引号
树和< 和> 对于系统安装的标头。 在系统的 CPP 中
安装的 std 组件省略了“.h”; 只是另一种方式 C++
喜欢做自己的事。 如果你想给'#include'任何类型的
文件,它将被包含在内。 它确实不是像 Java 那样的模块系统,
Python 和大多数其他编程语言都有。 由于标头是
不是模块,需要采取一些额外的步骤才能获得类似的功能
从他们当中。 Prepossesser(与所有
#keywords)将盲目地包含您所说的每一个内容
在该文件中使用,但 C 或 C++ 希望拥有您的符号或
含义在编译中仅定义了一个。 如果您使用图书馆,则不会
它是main.cpp,但是在main包含的两个文件中,那么你只需
希望该库包含一次而不是两次。 标准库
组件经过特殊处理,因此您无需担心使用
相同的 C++ 无处不在。 为了使第一次
Prepossesser 看到你的图书馆,它不再包含它,你需要
使用听力防护装置。

听守卫是最简单的事情。 它看起来像这样:

#ifndef LIBRARY_H
#define LIBRARY_H

// 在这里写下你的定义。

#endif

像这样注释 ifndef 被认为是好的:

#endif // LIBRARY_H

但如果你不做注释,编译器不会关心,也不会
伤害任何东西。

#ifndef 所做的只是检查 LIBRARY_H 是否等于 0;
不明确的。 当 LIBRARY_H 为 0 时,它提供之前的内容
#万一。

然后 #define LIBRARY_H 将 LIBRARY_H 设置为 1,因此下次
预处理器看到#ifndef LIBRARY_H,它不会提供相同的内容
再次。

(LIBRARY_H 应该是文件名,然后是 _ 和
扩大。 如果你不写的话,这不会破坏任何东西
同样的事情,但你应该保持一致。 至少输入文件名
对于#ifndef。 否则可能会让人混淆守卫的用途
什么。)

这里真的没什么好看的。


现在您不想使用头文件。

很好,假设您不关心:

  • 通过从头文件中排除某些内容来将其设为私有

  • 您不打算在库中使用此代码。 如果你曾经这样做过,它
    现在使用标题可能更容易,因此您不必重新组织
    稍后将您的代码放入标头中。

  • 您不想在头文件中重复一次,然后在
    一个 C++ 文件。

听者文件的目的可能看起来不明确,如果您不关心的话
人们出于虚构的原因告诉他们这是错误的,然后保存
你的手,不用费心重复自己。

如何仅包含听者文件

为 thing.cpp执行

#ifndef THING_CPP
#define THING_CPP

#include <iostream>

void drink_me() {
  std::cout << "Drink me!" << std::endl;
}

#endif  // THING_CPP

此操作。

然后对 main.cpp 进行

#include "thing.cpp"

int main() {
  drink_me();
  return 0;
}

编译。

基本上只需使用 CPP 扩展名命名包含的 CPP 文件,然后
然后将其视为头文件,但写出实现
那一个文件。

Historically hearder files have been used for two reasons.

  1. To provides symbols when compiling a program that wants to used a
    library or a additional file.

  2. To hide part of the implementing; keep things private.

For example say you have a function you don't want exposed to other
parts of your program, but want to use in your implementation. In that
case, you would write the function in the CPP file, but leave it out
of the header file. You can do this with variables and anything that
would want to keep private in the impregnation that you don't want
exposed to conumbers of that source code. In other programming
lanugases there is a "public" keyword that allows module parts to be
kept from being exposed to other parts of your program. In C and C++
no such facility exists at afile level, so header files are used
intead.

Header files are not perfect. Useing '#include' just copies the contents
of what ever file you provide. Single quotes for the current working
tree and < and > for system installed headers. In CPP for system
installed std components the '.h' is omitted; just another way C++
likes to do its own thing. If you want to give '#include' any kind of
file, it will be included. It really isn't a module system like Java,
Python, and most other programming lanuages have. Since headers are
not modules some extra steps need to be taken to get similar function
out of them. The Prepossesser (the thing that works with all the
#keywords) will blindly include what every you state is needed to be
consumed in that file, but C or C++ want to have your symbals or
implications defined only one in compilation. If you use a library, no
it main.cpp, but in the two files that main includes, then you only
want that library included once and not twice. Standard Library
components are handled special, so you don't need to worry about using
the same C++ include everywhere. To make it so that the first time the
Prepossesser sees your library it doesn't include it again, you need
to use a heard guard.

A heard guard is the simplest thing. It looks like this:

#ifndef LIBRARY_H
#define LIBRARY_H

// Write your definitions here.

#endif

It is considered good to comment the ifndef like this:

#endif // LIBRARY_H

But if you don't do the comment the compiler wont care and it wont
hurt anthing.

All #ifndef is doing is checking whether LIBRARY_H is equal to 0;
undefined. When LIBRARY_H is 0, it provides what comes before the
#endif.

Then #define LIBRARY_H sets LIBRARY_H to 1, so the next time the
Preprocessor sees #ifndef LIBRARY_H, it wont provide the same contents
again.

(LIBRARY_H should be what ever the file name is and then _ and the
extension. This is not going break anything if you don't write the
same thing, but you should be consistent. At least put the file name
for the #ifndef. Otherwise it might get confusing what guards are for
what.)

Really nothing fancy going on here.


Now you don't want to use header files.

Great, say you don't care about:

  • Having things private by excluding them from header files

  • You don't intend to used this code in a library. If you ever do, it
    may be easier to go with headers now so you don't have to reorganise
    your code into headers later.

  • You don't want to repeat yourself once in a header file and then in
    a C++ file.

The purpose of hearder files can seem ambiguous and if you don't care
about people telling out it's wrong for imaginary reasons, then save
your hands and don't bother repeating yourself.

How to include only hearder files

Do

#ifndef THING_CPP
#define THING_CPP

#include <iostream>

void drink_me() {
  std::cout << "Drink me!" << std::endl;
}

#endif  // THING_CPP

for thing.cpp.

And for main.cpp do

#include "thing.cpp"

int main() {
  drink_me();
  return 0;
}

then compile.

Basically just name your included CPP file with the CPP extension and
then treat it like a header file but write out the implementations in
that one file.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文