【问题标题】:Encapsulting an "include" so only selected classes are exposed封装“包含”,因此只公开选定的类
【发布时间】:2012-11-03 04:08:59
【问题描述】:

问题:是否可以从标头访问特定类,同时隐藏该标头中定义的其他命名空间/类?

比如说我有头文件——outcome.hpp:

// outcome.hpp
namespace sports { namespace outcome {
  class Injury {};
  class Success {};
}}

在另一个头文件中——api.hpp——我希望使用sports::outcome::Injury,而不让包含api.hpp的文件访问sports::outcome::Success。那可能吗?如果是这样,我该如何实现?

附:在实际代码中,api.hpp 包含将调用 Injury 类的方法的模板化方法,因此前向声明是不够的。


我的尝试:

在我的无知中,我试图通过将include 放在内部命名空间中来实现这一点。这是SSCCE

// api.hpp
namespace sports { namespace api {
  namespace internal {
    #include "outcome.hpp"  // I'm trying to hide symbols within this header
    using sports::outcome::Injury;
  } 

  class Boxing {
    private:
      internal::Injury sustained;
  };
}

当它奏效时,我过早地庆祝了:

// This cpp file compiles file   \o/
#include "api.hpp"
int main(int argc, char *argv[]) {
  sports::api::Boxing b;  
  // sports::outcome not accessible
}

如何失败:

如果将标准库中的容器用作outcome.hpp 中的类成员,事情就会崩溃。例如,使用这个版本:

// outcome.hpp
#include <vector>
namespace sports { namespace outcome {

  class Injury {
    private:
      std::vector x;
  };

// ...
}}

编译失败并出现以下错误:

在 /usr/include/c++/4.6/ext/new_allocator.h:34:0 包含的文件中, 来自 /usr/include/c++/4.6/x86_64-linux-gnu/./bits/c++allocator.h:34, 来自 /usr/include/c++/4.6/bits/allocator.h:48, 来自 /usr/include/c++/4.6/vector:62, 从结果.hpp:1, 来自 api.hpp:5, 来自 main.cpp:1: /usr/include/c++/4.6/new:93:54: 错误:'void* sports::api::internal::operator new(sports::api::internal::std::size_t)' 可能不是在命名空间中声明 /usr/include/c++/4.6/new:94:56: 错误:'void* sports::api::internal::operator new [](sports::api::internal::std::size_t)' 可能不在命名空间内声明 /usr/include/c++/4.6/new:95:35: 错误:'void sports::api::internal::operator delete(void*)' 可能未在命名空间中声明 /usr/include/c++/4.6/new:96:37: 错误:'void sports::api::internal::operator delete [](void*)' 不能在命名空间中声明 /usr/include/c++/4.6/new:97:62: 错误: 'void* sports::api::internal::operator new(sports::api::internal::std::size_t, const sports:: api::internal::std::nothrow_t&)' 不能在命名空间中声明 /usr/include/c++/4.6/new:98:64: 错误:'void* sports::api::internal::operator new [](sports::api::internal::std::size_t, const sports ::api::internal::std::nothrow_t&)' 不能在命名空间中声明 /usr/include/c++/4.6/new:99:58: 错误:'void sports::api::internal::operator delete(void*, const sports::api::internal::std::nothrow_t&)'不能在命名空间中声明 /usr/include/c++/4.6/new:100:60: 错误:'void sports::api::internal::operator delete [](void*, const sports::api::internal::std::nothrow_t& )' 不能在命名空间中声明 /usr/include/c++/4.6/new:103:57: 错误: 'void* sports::api::internal::operator new(sports::api::internal::std::size_t, void*)'不能在命名空间中声明 /usr/include/c++/4.6/new:104:59: 错误: 'void* sports::api::internal::operator new [](sports::api::internal::std::size_t, void* )' 不能在命名空间中声明 /usr/include/c++/4.6/new:107:52: 错误:'void sports::api::internal::operator delete(void*, void*)' 不能在命名空间中声明 /usr/include/c++/4.6/new:108:52: 错误:'void sports::api::internal::operator delete [](void*, void*)' 不能在命名空间中声明

我显然做错了。非常感谢您的建议和善意的告白。

【问题讨论】:

  • “暴露命名空间”是什么意思?
  • 使用上面的示例,我不希望在包含 api.hpp 的文件中访问 sports::outcome::*

标签: c++ namespaces scope information-hiding


【解决方案1】:

您可以使用 using 声明将名称从命名空间导入到不同的命名空间。

namespace sports {
  using sports::outcome::Injury;
};

在您的示例中,很多事情都是错误的。尤其是 include 位置不好,而且你缺少 include 守卫。

// file1.h
#ifndef FILE1_h
#define FILE1_h

namespace sports { namespace outcome {
  class Injury;
}}
#endif

// api.h   
#ifndef API_H
#define API_H

#include <file1.h>

namespace sports {
  using sports::outcome::injury;
}

#endif

包含api.h 的文件现在可以访问:

sports::injury x;

仅包含file1.h 的文件只能访问:

sports::outcome::injury x;

【讨论】:

  • 难道我不需要首先包含定义Injury 的头文件,并且不会包含该头文件中的所有内容吗?请原谅我的无知,我还在接受 C++。
  • @ShawnChin 是的,你需要。我添加了一个例子。没有办法在该标题中包含所有内容。尝试分开你的标题,有前向声明等。
  • 为了简洁起见,我省略了包括守卫(它们在实际代码中)。放置不当的包含是一种蹩脚的尝试,它隐藏了所包含的内容——它当然感觉不对,但我看不出还有什么方法可以实现这一点,因此提出了这个问题。
  • @ShawnChin 你不能简单地在 C++ 包含系统的工作方式上做到这一点。不幸的是,它的封装为零。
  • 这是有道理的。感谢您的耐心等待。
【解决方案2】:

如果我正确理解您的意图,那么您想要“私有”或“内部”命名空间之类的东西吗?恕我直言,这在 C++ 中是不可能的。

您可以使用一个类来执行此操作并将内部类定义作为嵌套类放置在那里,您的 api 命名空间可以类似地实现,将其他类公开为公共类型定义,前提是 api 是内部类的朋友。

namespace sports
{
    class api;

    class outcome
    {
        friend class api;

        // Internal class definitions
        class Boxing
        {
        };
    };

    class api
    {
    public:
        typedef outcome::Boxing Boxing;
    }
}

【讨论】:

    猜你喜欢
    • 2014-06-18
    • 1970-01-01
    • 2020-01-16
    • 2013-10-15
    • 1970-01-01
    • 2012-10-17
    • 2010-10-13
    • 2011-05-28
    • 2012-02-02
    相关资源
    最近更新 更多