循环包含在C+中隐藏实现细节的技巧+;标头档 我试图找到一种干净的方法来分离大型项目中C++头文件中的实现细节,以实现更好的信息隐藏和减少构建时间。C++的问题是每次更改私有成员声明时,必须重建依赖类。

循环包含在C+中隐藏实现细节的技巧+;标头档 我试图找到一种干净的方法来分离大型项目中C++头文件中的实现细节,以实现更好的信息隐藏和减少构建时间。C++的问题是每次更改私有成员声明时,必须重建依赖类。,c++,include,implementation,C++,Include,Implementation,这是我想出的解决办法。有什么好处吗 基本思想是在头文件中有条件地包含cpp文件的一部分。此部分包含实现声明,仅当实现文件包含头时才包含。对于外部类,此详细信息将从标题中排除。所以客户端和实现可以看到头文件的两个不同版本。内部声明更改不会影响客户端(不编译依赖类),并且标头不会包含私有详细信息 以下是实施方案: 标题 #pragma once class Dependency { public: Dependency(void); ~Dependency(void); v

这是我想出的解决办法。有什么好处吗

基本思想是在头文件中有条件地包含cpp文件的一部分。此部分包含实现声明,仅当实现文件包含头时才包含。对于外部类,此详细信息将从标题中排除。所以客户端和实现可以看到头文件的两个不同版本。内部声明更改不会影响客户端(不编译依赖类),并且标头不会包含私有详细信息

以下是实施方案:

标题

#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
看看这个(又名pImpl)

该模式通常用于类希望隐藏内部实现的情况,但也有一个好处,即对内部和私有结构的更改不会创建重新编译,因为二进制调用兼容性得到了维护

以其他方式执行此操作的问题是,当您更改类定义中的任何内容时,可能无法保持二进制兼容性,因此所有软件都必须重新编译


看起来您的解决方案正是试图做到这一点,但是您应该使用(void*)而不是int,以确保软件在不同平台上的32位和64位编译器上正确编译,并且只使用cook book中不透明指针的示例。

这不起作用。如果在private
.cpp
文件中向类添加任何内容,则该类的用户将看到与实现所认为的不同的类

这是不合法的,在很多情况下都不起作用。KDE有一篇关于C++中可以改变的和不能改变的ABI兼容性的文章。如果你用你的“隐藏”实现破坏了其中的任何一个,你将破坏用户


请查看,寻找一种相当常见的方法来完成您试图实现的目标。

这行不通。您可以很容易地看到它,因为实现和客户端的
sizeof(Dependency)
是不同的。客户端基本上看到了一个不同的类,访问了内存中的不同位置,一切都搞砸了

不幸的是,如果更改类,则无法阻止重建依赖文件。但是,您可以像这样隐藏实现细节:

标题: cpp文件
#包括
类私有数据
{
/*你的数据在这里*/
};
依赖项::依赖项()
{
pd=新的私有数据;
}
依赖项::~Dependency()
{
如果(pd)
删除pd;
}
void依赖项::Proc()
{
/*你的代码*/
}

请注意,这不是供您复制粘贴的。这只是给你一个想法。此用法可能暗示缺少错误检查或代码。其中一个是防止浅拷贝的拷贝构造函数。

这是一个非常有趣的问题,真的。管理依赖关系对于大型项目非常重要,因为构建时间的增加甚至会使最简单的更改变得令人望而生畏。。。当它发生的时候,人们会试图破解它,以避免死亡的重建(tm)

不幸的是,它不起作用

该标准明确规定,出现在不同翻译单位(大致为文件)中的类定义应遵守一个定义规则(见§3.2一个定义规则[basic.def.odr]

为什么?

在某种程度上,问题是阻抗的问题。类的定义包含关于类ABI(应用程序二进制接口)的信息,最显著的是,此类类在内存中的布局方式。如果同一个类在不同的翻译单元中有不同的布局,那么当把它放在一起时,它就不起作用了。好像一个屠人说德语,另一个说韩语。他们可能试图说同样的话,他们只是不理解对方

那么

有几种方法可以管理依赖关系。主要思想是,您应该尽可能努力提供“轻”标题:

  • 包括尽可能少的东西。您可以向前声明:显示为参数或函数返回声明的类型,通过引用或指针传递但未使用的类型
  • 隐藏实现细节
哼。。。它是什么意思: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
    是必需的,因为
    \u类型
    a
    是类的属性
  • b.hpp
    不是必需的,我们只参考了
    b
  • c.hpp
    不是必需的,我们只有一个指向
    c
  • d.hpp
    是必需的,我们调用
    d
  • e.hpp
    不是必需的,它只显示为返回
好的,让我们把它清理干净

#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()), _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
我们能做得更好吗

首先,我们可以看到,我们只在类的构造函数中调用
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, B& b, 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/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()), _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
    // no need to illustrate right now ;)
    
    #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, B& b, 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, B& b, C* c): _a(a), _b(b), _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, B& b, C* c): _impl(new Impl(a, b, c)) {}
    MyClass::~MyClass() {} // nothing to do here, it'll be automatic
    
    E MyClass::e() { /* ... */ }