Xiè*_*léi 0 c++ programming-languages
这可能不是一个真正的问题,请关闭它,如果它不合适.
在C++中,你不能混用在一个源文件的标题和实施,例如,类将被两次,如果它是由包括定义a.cc
和b.cc
:
// foo.h
#pragma once
class Foo {
void bar() { ... }
};
// a.cc
#include "foo.h"
class A {};
// b.cc
#include "foo.h"
class B {};
// Link all
$ gcc -c -o a.o a.cc
$ gcc -c -o b.o b.cc
$ gcc -o main main.cc foo.o a.o b.o
Run Code Online (Sandbox Code Playgroud)
然后,它是三个目标文件中的模糊Foo :: bar()!
相反,我们必须将实现分成另一个文件:
// foo.h
#pragma once
class Foo {
void bar();
};
// foo.cc
# include "foo.h"
void Foo::bar() { ... }
Run Code Online (Sandbox Code Playgroud)
虽然可能不是一个大问题,因为通常foo :: bar()的二进制文件a.o
和b.o
它们是相同的.但至少有一些冗余的声明,不是吗?还有一些由此多余造成的混淆:
在哪里给出可选参数的默认值?
class Foo {
void bar(int x = 100);
};
Run Code Online (Sandbox Code Playgroud)
要么,
void Foo::bar(int x = 100) { ... }
Run Code Online (Sandbox Code Playgroud)
或两者?
在内联和内联之间移动......?
如果要将非内联函数切换为内联函数,则应将代码从中foo.cc
移至foo.h
,并添加inline
关键字前缀.也许两秒钟之后,你会对你所做的事感到遗憾,然后,你将内联的内容移foo.h
回到foo.cc
并inline
再次删除关键字.
但如果声明和定义是一起的话,你不需要这样做.
还有更多这种轻微的头痛.
这个想法是,如果你只是随着声明一起编写一个函数的定义,那么编译器就无法推断函数的原型.
例如,通过使用单个source.cc
,并仅导入类型信息,例如,
#include_typedef "source.cc"
Run Code Online (Sandbox Code Playgroud)
事情会变得更简单.编译器很容易忽略变量分配和函数定义,只需在构造AST之前在解析器时进行过滤,不是吗?
我习惯于在单独的源/头文件中编程,我当然能够进行分离.你可以争论编程风格,但是,这会降低问题是什么是将逻辑表示为更好的编程风格的正确方法.我不认为Java是源/头分离上下文中更好的编程风格.但Java提供了正确的方法.
你介意把标题与类分开吗?如果可能的话,你会如何将它们混合成一个?是否有任何C++前端可以将声明和定义分离为混合源中的单独文件?或者我怎么写这样的?(我的意思是海湾合作委员会.)
编辑
无论如何我不是在抨击C++,如果你从这个问题中得到错误的信息,我很抱歉.
由于C++是一种多范式语言,您可以通过使用魔术宏来了解MFC如何设置消息绑定,以及boost库如何使用模板实现所有内容.当分离进入问题域时,(感谢ONeal指出该域属于包装系统),人们可以尝试找出解决方法.对我来说,有很多种编程风格,因为我花了很多时间编写C++,所以任何小的方便都可以很方便地累积.写入实现以及声明是方便的.我想如果我不需要将它们分开,我可以将源线减少至少10%.您可能会问,如果方便是如此重要,为什么不使用Java呢?显然C++与Java不同,它们不可互换.
内联函数可能解决问题,但它根本改变了语义.
有了这个问题,你就会提出通常的C++编译系统的根本缺点.但是,让这种情况有所帮助,你的第一个例子似乎就会出现问题.事实上,这样一种风格,你把你所有的课程完全内联,完美地运作.当您开始更深入地使用模板时,它甚至是使事情有效的唯一方法.例如,boost库大多只是标题,即使是涉及最多的技术细节都是内联编写的,因为它不会起作用.
我的猜测是你错过了所谓的标题守卫 - 从而获得了重新定义警告.
// foo.h --------
#ifndef FOO_H
#define FOO_H
class Foo {
void bar() { ... }
};
#endif // FOO_H
Run Code Online (Sandbox Code Playgroud)
这应该工作正常