循环包括隐藏C头文件中的实现细节的技巧
我正在尝试找到一种干净的方法来分离大型项目中C头文件中的实现细节,以实现更好的信息隐藏并减少构建时间. C的问题是每次更改私有成员声明时,必须重建依赖类.
这是我提出的解决方案.这有什么好处吗?我不是C专家,所以我很有可能完全错过了一些东西,所以请跟我一起去. 基本的想法是在标题中有条件地包含cpp文件的一部分.此部分包含实现声明,仅在实现文件包含标头时包含.在外部类的情况下,此详细信息将从标题中排除.所以客户端和实现看到两个不同版本的头文件.内部声明更改不会影响客户端(不编译依赖类),标头不会包含私有详细信息. 这是实施: HEADER #pragma once class Dependency { public: Dependency(void); ~Dependency(void); void Proc(void); //PRIVATE Implementaion details stays private #ifdef Dependency_PRIVATE_IMPELEMENTATION #define Dependency_PRIVATE_MODE 1 #include "Dependency.cpp" #undef Dependency_PRIVATE_MODE #endif }; CPP #define Dependency_PRIVATE_IMPELEMENTATION #include "Dependency.h" #undef Dependency_PRIVATE_IMPELEMENTATION #ifdef Dependency_PRIVATE_MODE private: int _privateData; #else #include <iostream> Dependency::Dependency(void) { //This line causes a runtime exception,see client Dependency::_privateData = 0; } Dependency::~Dependency(void) { } void Dependency::Proc(void) { std::cout << "Shiny happy functions."; } #endif 客户 #include "stdafx.h" #include "Dependency.h" #pragma message("Test.Cpp Compiled") int _tmain(int argc,_TCHAR* argv[]) { Dependency d; d.Proc(); return 0; //and how I have a run time check error #2,stack around d ?!! } 解决方法
真的,这是一个非常有趣的问题.管理依赖项对于大型项目非常重要,因为构建时间的增加甚至可以使最简单的更改变得令人生畏……当它发生时,人们会尝试破解它以避免重建(tm).
不幸的是,它不起作用. 标准明确指出,出现在不同翻译单元(粗略地说,文件)中的类定义应遵循一个定义规则(参见§3.2一个定义规则[basic.def.odr]). 为什么? 在某种程度上,问题是阻抗问题.类的定义包含有关类ABI(应用程序二进制接口)的信息,最值得注意的是,这样的类是如何在内存中布局的.如果您在各种翻译单元中具有相同类的不同布局,那么在完全放置它时,它将无法工作.就像一个TU说德语和另一个韩语.他们可能试图说同样的话,他们只是不会互相理解. 那么? 有几种方法可以管理依赖项.主要想法是你应该尽可能地提供“轻”标题: >包括尽可能少的东西.您可以转发声明:显示为参数的类型或返回函数声明,通过引用或指针传递但未使用的类型. 嗯……这是什么意思:x? 让我们选一个简单的例子,好吗? #include "project/a.hpp" // defines class A #include "project/b.hpp" // defines class B #include "project/c.hpp" // defines class C #include "project/d.hpp" // defines class D #include "project/e.hpp" // defines class E namespace project { class MyClass { public: explicit MyClass(D const& d): _a(d.a()),_b(d.b()),_c(d.c()) {} MyClass(A a,B& b,C* c): _a(a),_b(b),_c(c) {} E e() const; private: A _a; B& _b; C* _c; }; // class MyClass } // namespace project 这个标题包含5个其他标题,但实际需要多少个? > a.hpp是必要的,因为类型A的_a是类的属性 好的,让我们清理一下吧! #include "project/a.hpp" // defines class A #include "project/d.hpp" // defines class D namespace project { class B; } namespace project { class C; } namespace project { class E; } namespace project { class MyClass { public: explicit MyClass(D const& d): _a(d.a()),_c(c) {} E e() const; private: A _a; B& _b; C* _c; }; // class MyClass } // namespace project 我们可以做得更好吗? 好吧,首先我们可以看到我们仅在类的构造函数中调用D上的方法,如果我们将D的定义移出标题,并将其放在.cpp文件中,那么我们将不需要包含d .hpp更长! // no need to illustrate right now ;) 但是…… A是什么? 通过注意仅仅握住指针不需要完整的定义,可以“作弊”.这被称为实现指针(简称pimpl).它会减少运行时间以获得更轻的依赖性,并为类增加了一些复杂性.这是一个演示: #include <memory> // don't really worry about std headers,// they are pulled in at one time or another anyway namespace project { class A; } namespace project { class B; } namespace project { class C; } namespace project { class D; } namespace project { class E; } namespace project { class MyClass { public: explicit MyClass(D const& d); MyClass(A a,C* c); ~MyClass(); // required to be in the source file now // because for deleting Impl,// the std::unique_ptr needs its definition E e() const; private: struct Impl; std::unique_ptr<Impl> _impl; }; // class MyClass } // namespace project 和相应的源文件,因为这是有趣的事情发生: #include "project/myClass.hpp" // good practice to have the header included first // as it asserts the header is free-standing #include "project/a.hpp" #include "project/b.hpp" #include "project/c.hpp" #include "project/d.hpp" #include "project/e.hpp" struct MyClass::Impl { Impl(A a,_c(c) {} A _a; B& _b; C* _c; }; MyClass::MyClass(D const& d): _impl(new Impl(d.a(),d.b(),d.c())) {} MyClass::MyClass(A a,C* c): _impl(new Impl(a,b,c)) {} MyClass::~MyClass() {} // nothing to do here,it'll be automatic E MyClass::e() { /* ... */ } 好吧,那就是低调和坚韧不拔.进一步阅读: > Law of Demeter:避免必须在序列中调用多个方法(a.b().c().d()),这意味着你有漏洞抽象,并强迫你包括整个世界做任何事情.相反,您应该调用a.bcd()来隐藏您的详细信息. 有许多方法可以封装和隐藏信息,您的任务才刚刚开始! (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |