如何隐藏C++头文件中的实现

嗯,先从一个问题说起,游戏引擎中的贴图管理模块该如何实现?我们可以分别想象一下这个模块在C和C++中的大体实现。注意,为了简化,下面的代码仅仅是示意一下而已。

一. C 中的实现

C 通过头文件来暴露贴图模块的操作函数,texture.h 头文件代码如下:

#pragma once

//接口1:初始化贴图管理模块
void texture_init();

//接口2:加载一个贴图
void texture_load(int id);

//接口3:销毁一个贴图
void texture_unload(int id);

第一感觉如何,是不是特别的干净利落!而至于在实现层面上,贴图到底是如何被管理的,这些信息在头文件中已经完全的透明化了,是的,实现完全的被隐藏了,我们用固定数组也好,用指针也罢,已经和模块用户完全的没关系了,无论我们如何修改实现,只要我们保证这个头文件不改变,那么依赖(#include)这个头文件的用户代码也就不需要重新编译。嗯,接下来看看C++中对象风格的实现。

二. C++对象风格的实现

假设C++通过一个TextureManager类来管理贴图,TextureManager.h 头文件代码如下:

#pragma once

//Texture 类声明。
class Texture;

//TextureManager 类声明。
class TextureManager {
public:
    TextureManager() {}
    ~TextureManager() {}
public:
    //接口1:初始化贴图管理模块
    void init();

    //接口2:加载一个贴图
    void load(int id);

    //接口3:销毁一个贴图
    void unload(int id);
private:
    //请注意这里的纠结:这里已经暴露了我们是如何管理贴图的。
    //我们用一个vector来做了贴图池(texture pool)。
    std::vector<Texture> m_TexturesPool;

    //其他的一些私有实现层面的变量
    //。。。。。。
};

看到上述的红色粗体警告了吗?明白问题的痛点在哪了吗?

额,可能有人说,暴露就暴露了呗,你也忒小气了吧,人家Linux,GCC,Emacs都开源了,你这点Low逼代码还想要敝帚自珍? 额。。。。。。好吧,我承认刚开了个玩笑来试图幽默一下。

这里真正的痛点是前面提过的【编译依赖】的问题。我们把贴图的管理机制给暴露了!是的,我们用了一个std::vector,但如果以后,我们发现用 std::list 来做贴图池会更适合一些,那怎么办?改呗,然后我们这一改,所有依赖(#include)这个头文件的代码,都需要重新编译了。好的,说到这里,我想我们应该已经明白了本文标题所说的隐藏C++头文件中的实现,是什么意思和目的了。好的,但依然需要总结一下。

三. 为什么要隐藏 C++ 头文件中的实现?

这里就不啰嗦了,前面都剧透过了,就两点:

  • 为了让头文件看起来干脆利落,心情愉悦。
  • 为了尽可能的消除【编译依赖】。

下面进入下一波内容。

四. 如何隐藏 C++ 头文件的实现?

某个著名的计算机科学家说过一句话:“计算机科学领域内的几乎所有问题都可以通过引入一个额外的中间层来解决”。

嗯,是的,这里我们也需要一个中间层,C++设计中的惯用手法:Impl 类

嗯,通过引入一个额外的 Impl 类,我们可以做到让用户依赖的头文件中不再暴露实现上的细节。直接看如下的代码片段,引入了TextureManagerImpl 类:

//注意:TextureManagerImpl 类真正的实现了贴图管理逻辑。
class TextureManagerImpl;

//引入Impl类后,TextureManager的类声明。
class TextureManager {
public:
    TextureManager() {}
    ~TextureManager() {}
public:
    //接口1: 初始化贴图管理模块
    void init();

    //接口2:加载一个贴图
    void load(int id);

    //接口3:销毁一个贴图
    void unload(int id);
private:
    //请注意这里:我们已经隐藏了实现!!

// 上述的三个公共接口都通过m_Impl指针来调用真正的实现,同时上一版这里的std::vector贴图池也转移到了TextureManagerImpl类中实现

    TextureManagerImpl *m_Impl;
};

嗯,至此我们应该理解了 Impl 类的价值所在了,与 Impl 类结对出现的总会是 Wrapper 类。

  • 上述的 TextureManagerImpl 是 Impl 类,负责具体的功能实现,同时会对用户隐藏,用户不知道 Impl 类的存在。
  • 上述的 TextureManager       是 Wrapper 类,负责包装 Impl 类的接口,是模块对用户暴露的接口。

全文结束。

时间: 2024-10-14 08:11:34

如何隐藏C++头文件中的实现的相关文章

[C++]关于头文件中的防卫式声明(#ifndef...#pragma once)

大家知道,我们写.h文件时,通常会加上防卫式声明,有以下两种方式: 1. 宏定义 #ifndef _FILENAME_ #define _FILENAME_ //... #endif 2. 编译器指令 #pragma once 但是,为什么头文件中需要添加这种防卫式声明呢?如果没有这样的声明,会出现怎样的问题.这里,先看一个例子. -- "Car.h",代码如下(并没有添加防卫式声明): // Car.h class Car { // ... }; -- "Person.h&

第2条:在类的头文件中尽量少引入其他头文件

@class (向前声明) #import 注意:如果在各自头文件中引入对方的头文件,则会导致“循环引用 ”. 虽然#import(而非#inculde指令)不会导致死循环,但却意味着两个类里有一个无法被正确编译.

OC高效率52:(二)类的头文件中尽量少引用其他头文件

// //  EOCPerson.h //  OC高效率52:类的头文件中尽量少引用其他头文件 // //  Created by Zoujie on 15/10/8. //  Copyright ? 2015年 Zoujie. All rights reserved. // #import <Foundation/Foundation.h> //#import "EOCEmployer.h" @class EOCEmployer;//向前申明该类,将引入头文件的时机尽量延

头文件中不可以放变量的定义

注意头文件中不可以放变量的定义!!!一般情况下头文件中只放变量的声明,因为头文件 要被其他文件包含(即#include),如果把定义放到头文件的话,就不能避免多次定义变量, C++不允许多次定义变量,一个程序中对指定变量的定义只有一次,声明可以无数次. 不过有三个例外,一下三中实体的定义也可放到头文件中 1.值在编译时就已知的const 变量的定义可以放到头文件中 如:const int num(10); 2.类的定义可以放到头文件中 3.inline 函数 4.C++11的新特性 conste

浅谈c/c++头文件中#ifndef/#define/#endif的用法

想必很多人都看过“头文件中用到的 #ifndef/#define/#endif 来防止该头文件被重复引用”.但是是否能理解“被重复引用”是什么意思?头文件被重复引用了,会产生什么后果?是不是所有的头文件中都要加入#ifndef/#define/#endif 这些代码? 1. 其实“被重复引用”是指一个头文件在同一个cpp文件中被include了多次,这种错误常常是由于include嵌套造成的.如:存在a.h文件#include "c.h"而此时b.cpp文件导入了#include &q

关于在头文件中定义变量的问题

如果在一个头文件中定义了一个变量 A.h: int i; 在A.cpp,B.cpp...文件中包含了这个头文件,编译时就会产生重复定义的错误.问题所在是因为int i;这句代码是定义了一个i变量,包含一次头文件就定义了一次i,包含多次肯定引起错误.如果改成extern int i;这句代码就变成了一个声明了.并不会定义i这个变量.不过在多个cpp文件里初始化一样会出现错误.只能在一个cpp文件中进行初始化.

c语言头文件中定义全局变量的问题

问题是这么开始的: 最近在看一个PHP的扩展源码,编译的时候的遇到一个问题: ld: 1 duplicate symbol for architecture x86_64 仔细看了一下源码,发现在头文件中 出现了全局变量的定义. 简化一下后,可以这么理解: // t1.h #ifndef T1_H #define T1_H int a = 0; #endif //------------------ //t1.c #include "t1.h" #include "t2.h&

c语言头文件中定义变量

最近在看一个PHP的扩展源码,编译的时候的遇到一个问题: ld: 1 duplicate symbol for architecture x86_64 仔细看了一下源码,发现在头文件中 出现了全局变量的定义 ZEND_DECLARE_MODULE_GLOBALS(xx) 简单开来,可以这么理解 // t1.h #ifndef T1_H #define T1_H int a = 0; #endif //------------------ //t1.c #include "t1.h" #

内部链接和外部链接以及const对象在头文件中的若干问题

首先我们看一段示例代码: t1.h [cpp] view plaincopyprint? #ifndef T1_H_ #define T1_H_ #include "t2.h" #ifndef HHH #define HHH int da = 5; #endif #endif #ifndef T1_H_ #define T1_H_ #include "t2.h" #ifndef HHH #define HHH int da = 5; #endif #endif t2