如何在C++中伪造"类的可见性"(而不是函数)?

jav*_*ver 8 c++ class-visibility visual-studio-2015

没有功能,控制能见度C级的/无障碍++.

有没有办法伪造它?
是否有任何可以模拟最接近行为的C++宏/模板/魔术?

情况就是这样

Util.h (图书馆)

class Util{   
    //note: by design, this Util is useful only for B and C
    //Other classes should not even see "Util"
    public: static void calculate(); //implementation in Util.cpp
};
Run Code Online (Sandbox Code Playgroud)

Bh (图书馆)

#include "Util.h"
class B{ /* ... complex thing */  };
Run Code Online (Sandbox Code Playgroud)

Ch (图书馆)

#include "Util.h"
class C{ /* ... complex thing */  };
Run Code Online (Sandbox Code Playgroud)

Dh (用户)

#include "B.h"    //<--- Purpose of #include is to access "B", but not "Util"
class D{ 
    public: static void a(){
        Util::calculate();   //<--- should compile error     
        //When ctrl+space, I should not see "Util" as a choice.
    }
};
Run Code Online (Sandbox Code Playgroud)

我的解决方案不好

让所有成员Util都是私人的,然后声明: -

friend class B;
friend class C;
Run Code Online (Sandbox Code Playgroud)

(编辑:感谢ASH "此处不需要前瞻声明".)

坏处 :-

  • 这是一种修改,Util以某种方式识别BC.
    在我看来这没有意义.
  • 现在B和C可以访问每个成员Util,打破任何private访问控制.
    一种方法可以只为一些成员启用朋友,但它不是那么可爱,并且对于这种情况无法使用.
  • D只是不能使用Util,但仍然可以看到它.
    Util在使用自动完成(例如ctrl+space)时仍然是一个选择D.h.

(编辑)注:编码方便; 防止一些错误或错误的使用/更好的自动完成/更好的封装.这不是反黑客攻击,也不是防止未经授权访问该功能.

(编辑,接受):

可悲的是,我只能接受一种解决方案,因此我主观地选择了需要较少工作并提供很大灵活性的解决方案.

对于未来的读者来说,Preet Kukreti(和评论中的texasbruce)和Shmuel H.(以及ASH评论)也提供了值得一读的好解决方案.

Shm*_* H. 5

我认为最好的办法就是不要Util.h在公共标题中加入.

为此,#include "Util.h"仅在实现cpp文件中:

Lib.cpp:

#include "Util.h"

void A::publicFunction() 
{
    Util::calculate();
}
Run Code Online (Sandbox Code Playgroud)

通过这样做,您可以确保更改Util.h只会在库文件中产生影响,而不会在库的用户中产生影响.

这种方法的问题是无法Util在您的公共标头(A.h,B.h)中使用.前向声明可能是此问题的部分解决方案:

// Forward declare Util:
class Util;

class A {
private:
    // OK;
    Util *mUtil;

    // ill-formed: Util is an incomplete type
    Util mUtil;
}
Run Code Online (Sandbox Code Playgroud)

  • 另请注意,在此解决方案中,Util.h标头不需要与用户的库一起重新分发,因此您作为库作者可以确保您想要的任何有限用途,并且您的库的用户永远不会知道关于它. (2认同)

Sam*_*hik 3

一种可能的解决方案是将其推Util入名称空间,并将typedef其放在BC类中:

namespace util_namespace {

    class Util{
    public:
        static void calculate(); //implementation in Util.cpp
    };
};

class B {

    typedef util_namespace::Util Util;

public:

    void foo()
    {
        Util::calculate(); // Works
    }
};

class C {

    typedef util_namespace::Util Util;

public:

    void foo()
    {
        Util::calculate(); // Works
    }
};

class D {

public:

    void foo()
    {
        Util::calculate(); // This will fail.
    }
};
Run Code Online (Sandbox Code Playgroud)

如果该类Util是在 中实现的util.cpp,则需要将其包装在namespace util_namespace { ... }. 就BC而言,它们的实现可以引用名为 的类Util,没有人会更明智。如果没有启用typedefD将找不到该名称的类。

  • C++ 的可见性/可访问性并不是为了具有军事强度的安全性而设计的。设置这种 typedef 别名后,尝试从 B 或 C 以外的任何地方直接引用 `Util` 类将导致编译错误。当然,使用完整的命名空间限定名称,完整的类在任何地方都是可见的。但如果你的目标是捕获这种情况,而不需要丑陋的朋友声明,那么这是一个简单的方法。 (2认同)