在C ++和Objective-C中,我养成了向前声明任何不需要在头文件中定义的必要类的习惯,然后根据需要导入在源文件中定义这些类的头文件。
是否存在这不是一个好主意的情况?
(我知道前向声明的一大缺点是不完整类型的可用性有限。出于这个问题的目的,假设在头文件中我只需要使用前向声明的类作为不完整的类型。)< / p>
答案 0 :(得分:7)
有时您可以巧妙地更改程序的语义而不会引发任何错误
class Foo;
template < typename T>
struct Trait
{
static const int MY_TYPE = -1;
};
// Lets say this is Foo.h
//class Foo
//{
//};
//template<>
//struct Trait<Foo>
//{
// static const int MY_TYPE = 1;
//};
void TestFunction(Foo& f)
{
std::cout << Trait<Foo>::MY_TYPE << std::endl;
}
考虑上面的代码,注释掉的代码存在于标题中。如果包含标题,TestFunction将打印1,否则为-1
答案 1 :(得分:3)
一个缺点可能是缺乏信息隐藏/封装。
我更喜欢拥有最小的头文件。这意味着我没有义务继续支持这么多功能。如果我想要更改某些内容,并且某些内容未在公共标题中公开,那么我可以更好地将其内部更改为类,而不会影响其他任何人。
修改强>
卢克问了一个例子,所以你走了:
假设您有一个名为Car
的类。并且,你唯一建立它的是从A点到B点。我个人而言,我更喜欢将我的头文件保存到:一个名为Car
的类,以及一个名为{{1的方法}}。从您提出问题的方式(“我可以使用的所有类”),我希望在您的头文件中找到类似“DieselEngine”,“PetrolEngine”,“HybidEngine”等类。这个问题是其他人在你的项目上工作(或者,你,随着时间的推移)开始使用这些暴露的类。现在,两年后,你决定“嗯...... PetrolEngine课真的给我带来了麻烦。我想我只是要将它删除并用我的Car类中的HybridEngine替换它” - 好吧,现在是PetrolEngine由于你不理解的原因,它被包含在100个其他文件中 - 现在你被迫保留PetrolEngine(和以前一样工作)所有那些使用它的人,有些人说你不太确定 - 因为你没有一个可靠的工作“合同”,因为你想要首先使用这个类。这是你真正想要完成的一项实施细节 - 建造一辆汽车。
编辑讨论有关信息隐藏的评论:
如果您所做的只是严格向前声明类/结构名称 - 好吧,我想我会再次问“为什么”。如果我是你的头文件和类的消费者,并且我实际上无法对该类做任何事情 - 并且它不作为主要类API的参数或返回类型公开 - 那么为什么要公开它?
如果您正在使用它进行不透明数据结构的编译时类型安全检查 - 很好 - 这是一回事。但是,你提出问题的方式让我听起来像所有当然是在头文件中。
答案 2 :(得分:2)
前向声明可以应用于很多东西:类,函数,全局变量/常量(使用extern
)和C ++ 11中的枚举。
据我所知,主要的缺点是冗余,因为冗余会为错误带来更多的范围,但这取决于您向前发布的内容,这种情况会有很大不同。
如果你对类,全局或枚举的前向声明有错误,编译器应该把它拿起来(是的!)。
如果你对函数声明有错误,那么在C ++中你只是创建了一个重载(oups!)。
因此,我会说转发声明类,全局或枚举没有真正的缺点;但是当谈到功能时,如果你坚持使用#include
会更好,你总是可以创建对相关功能进行分组的标题,以避免创建太多文件。
答案 3 :(得分:0)
没有,据我所知。您已经涵盖的唯一缺点是使用不完整的类型。既然你说不会有任何需要完全定义类型的用法,我猜你就没问题了。
答案 4 :(得分:0)
如果您不需要标头中类的完整定义,则前向声明优先于完整文件包含。我能想到的唯一缺点就是@Stephen Darlington的评论,如果可以的话,我会给出10个赞成票。 :)
答案 5 :(得分:0)
一个缺点是前向声明类型中可能存在内存泄漏。有关详细信息,请参阅this question。在这种情况下,Microsoft编译器会发出warning C4150: deletion of pointer to incomplete type 'SomeType'; no destructor called
。
答案 6 :(得分:0)
这可能主要是你的同事的可读性问题,这将继续你的编码工作。