【问题标题】:Prevent public uses of a class used as a private base class防止公共使用用作私有基类的类
【发布时间】:2018-05-14 12:13:29
【问题描述】:

如何确保只有“我的”代码才能使用一个类,即使它使用了一个类? (如果它不用作基类,我可以将其设为 privateprotected 我的一个类的嵌套类)

如果我想表明对我的一个类使用基类仅仅是实现细节,我可以使用私有基类:

 class Base
 {
      ...
 }

 class Derived: private Base
 {
 public:

      Derived(...): Base{...} {... };

      ...
 }

对于我的Derived 类的客户,我使用Base 类并不明显:

 #include "Derived.h"

 void client() {
    Derived d{...};
    Base *b = static_cast< Base * >(&d);// error
    ...
 }

但想象一下 Base 类是如此专业化、令人困惑或难以使用,以至于我不希望我的代码的客户可以将它用作基类或创建它的对象班级。在某种意义上,我希望它对我的某些代码是“私有的”,所以这样的客户端代码会失败:

 #include "Derived.h"

 class Client: Base// error wanted here
 {
 public:
    Client(...): Base{...} {...};

    ...
 }

 void client()
 {
    Derived d{...};// OK
    Base b{...};// error wanted here
    Client c{...};// error wanted here
 }

我该怎么做?

实际上,我在问如何实现Java's package-private classes 之类的东西,只有同一个“包”(模块)中的其他类可以访问,但不能被“包”之外的代码使用。

【问题讨论】:

    标签: c++ inheritance private


    【解决方案1】:

    如果你想 100% 强制执行,并且不喜欢“请不要使用以 '_' 开头的东西”的 python 方法,那么我相信这是你的停靠港:

    class Dave;
    
    class MyPrivateBaseClasses {
      private:
        MyPrivateBaseClasses();    // ensure nothing can use this class
        class BaseClassA {};
    
        friend Dave;
    };
    
    class Dave : public/private MyPrivateBaseClasses::BaseClassA
    {};
    

    当然——这意味着你必须将所有想要使用它的东西都加为好友,但它确实给了你想要的东西; 100% 保护使用 BaseClassA 的人。

    【讨论】:

    • 除了它仍然不能防止恶意(很少能做到这一点),例如最终用户程序员将他们的类型添加到列表中。老实说,我能想到的唯一真正的保护是不分发隐藏基类的源,但这可能不可行,并且仍然可能不够,具体取决于您需要防范的恶意程度。当然,我会说,如果您需要防止这种程度的恶意,您在做什么分发源代码供其他人编译?
    • 嗯,它不会阻止人们编辑代码 - 当然 - 但它会阻止人们在不编辑代码的情况下使用它。根据其他答案使用命名空间不需要编辑代码来使用类。
    【解决方案2】:

    这不能像在 Java 中那样直接完成。如果只是为了避免混淆,您可以将 Base 移动到一个名称空间中,这意味着您的代码的客户端会忽略该名称空间,例如:

    namespace hidden {
      class Base {
        ..
      };
    }
    
    class Derived : private hidden::Base {
      ...
    };
    

    如果您真的想避免使用Base 的可能性,那么如果您打算使用Base 作为多个类的父类(数量可能随时间变化),这将是一个相当困难的故事。你可以给Base一个private构造函数,并指出你的每个派生类都是friendBase

    class Hider {
      private:
        Hider() = delete;
        class Base {
          ..
        };
    
      friend class Derived;
    };
    
    class Derived : Hider::Base {
      ..
    };
    

    当然,这需要对您想要从Base 派生的每个新类进行手动维护。

    【讨论】:

    • 任何你不能使基类成为另一个类的私有成员的原因;然后交友要使用它的东西?
    • @UKMonkey 你怎么隐藏那个类呢?
    • @Ivan 你为什么关心那个“容器”类是否隐藏?如果有人试图创建它的实例,你可以让它出现编译错误
    【解决方案3】:

    您可以通过将“私有”实体放入detail 命名空间来按照惯例“强制执行”这一点。许多流行的库(例如 Boost) 这样做:

    namespace detail
    {
        class Base { /* ... */ };
    }
    
    class Derived : private detail::Base
    {
        /* ... */
    };
    

    当模块标准化时,这个问题将得到妥善解决,因为您将能够控制导出哪些实体以及哪些是实现细节。

    【讨论】:

    • 绝对是这个。没有人能阻止人们表现得愚蠢。最好让他们知道这是愚蠢的,并让他们成为负责任的成年人。
    • 这无济于事;客户可以写detail::Base b = {...};
    • @Raedwald:没有更好的解决方案。 C++ 开发人员应该知道,按照惯例,如果你写detail::,所有的赌注都没有。今天没有什么能阻止使用来自libstdc++的一些奇怪的实现实体。
    • @Raedwald - 你没有抓住重点。可以使用大量的马基雅维利图来打破封装。没有真正的保护。这可以防止您的客户意外使用实施细节,而这正是他们最终应该保护的。
    • 为什么客户应该知道 detail 命名空间不受限制,使用它会破坏封装?通过在头文件中写注释?我可以很容易地写一条评论说Base 类是禁区。
    猜你喜欢
    • 2020-09-11
    • 2011-05-27
    • 2015-08-09
    • 2016-10-08
    • 1970-01-01
    • 2011-11-06
    • 2018-05-18
    • 2014-01-03
    • 2017-02-27
    相关资源
    最近更新 更多