C++ - 允许通过基类(接口)访问,禁止通过派生类访问(具体实现)?

Posted

技术标签:

【中文标题】C++ - 允许通过基类(接口)访问,禁止通过派生类访问(具体实现)?【英文标题】:C++ - allow access through the base class (interface), forbid access through the derived class (concrete implementation)? 【发布时间】:2013-03-22 13:04:10 【问题描述】:

假设我有纯抽象类 IHandler 和派生自它的类:

class IHandler

public:
   virtual int process_input(char input) = 0;
;

class MyEngine : protected IHandler

public:
   virtual int process_input(char input)  /* implementation */  
;

我想在 MyEngine 中继承该类,以便我可以将 MyEngine* 传递给任何期望 IHandler* 的人,并让他们能够使用 process_input。 但是我不想允许通过MyEngine* 进行访问,因为我不想公开实现细节。

MyEngine* ptr = new MyEngine();
ptr->process_input('a');                           //NOT POSSIBLE
static_cast<IHandler*>(ptr)->process_input('a');   //OK
IHandler* ptr2 = ptr;                              //OK
ptr2->process_input('a');                          //OK

这可以通过受保护的继承和隐式转换来完成吗? 我只设法得到:

存在从“MyEngine *”到“IHandler *”的转换,但无法访问

由于我来自C#背景,这基本上是C#中的显式接口实现。 这是 C++ 中的有效方法吗?

补充:

为了更好地了解我为什么要这样做,请考虑以下几点:

TcpConnection 实现通过TCP 的通信,并且在其构造函数中需要指向接口ITcpEventHandler 的指针。 当TcpConnection 在套接字上获得一些数据时,它使用ITcpEventHandler::incomingData 将该数据传递给它的ITcpEventHandler,或者当它轮询传出数据时它使用ITcpEventHandler::getOutgoingData

我的类HttpClient 使用TcpConnection(聚合)并将自身传递给TcpConnection 构造函数,并在这些接口方法中进行处理。

所以TcpConnection 必须实现这些方法,但我不希望使用HttpClient 的用户直接访问ITcpEventHandler 方法(incomingDatagetOutgoingData)。他们应该无法直接致电incomingDatagetOutgoingData

希望这能澄清我的用例。

【问题讨论】:

我不明白这个问题,'通过 MyEngine* 访问'是什么意思,'暴露实现细节'是什么意思。 我不希望用户在 MyEngine 类中使用 IHandler 方法,除非他们通过 IHandler 接口访问 MyEngine。 【参考方案1】:

使用protected 进行派生使得无法通过指向派生类的指针访问基类的成员,并且不允许隐式转换。

在我看来,您想要的不是禁止通过基类(接口)进行访问,而是通过派生类(具体实现):

class IHandler

public:
   virtual int process_input(char input) = 0;         //pure virtual
   virtual std::string name()  return "IHandler";   //simple implementation
;

class MyEngine : public IHandler
//               ^^^^^^

protected: // <== Make the functions inaccessible from a pointer
           //     or reference to `MyEngine`.

   virtual int process_input(char input)  return 0;    //override pure virtual
   using IHandler::name;                                 //use IHandler version
;

在这里,在派生类中,您基本上覆盖了process_input 函数的可见性,因此客户端只能通过指针或对基类的引用来调用它们。

这样你就不可能了:

MyEngine* ptr = new MyEngine();
ptr->process_input('a');   // ERROR!
std::cout << ptr->name();  // ERROR!

但这将是可能的:

IHandler* ptr = new MyEngine();
ptr->process_input('a');   // OK
std::cout << ptr->name();  // OK

【讨论】:

是的,这就是我想要实现的。另外 IHandler 是一个非常大的接口,所以我不想显式保护每个 IHandler 成员函数。 您的方法有效,但还有其他方法吗,因为我不想将所有基类方法显式声明为受保护? @omittones:我想不出别的办法【参考方案2】:

在 C++ 中,protectedprivate 继承服务于实现的继承。也就是说,你定义了一个带有方法的类,例如一个模板类,当你想使用它的功能而不是它的接口时,你继承protectedprivate。所以实际上你的基类需要定义你想在子类中使用的方法。

Here 是有关此主题的链接。这真的很难,我同意。

【讨论】:

【参考方案3】:

在这里你希望实现的真正目标有点难以理解,因为无论你调用父级或子级上的方法,只要它是虚拟的,都会调用同一个。

也就是说你有几个选择。

您可以通过强制创建返回接口的调用来使用户无法获取子类型的指针(或对象)。那你就不用担心人为的限制了,他们根本就生不了孩子:

class Concrete : public Interface

public:
    static Interface* create()  return new Concrete; 

private:
    Concrete()  
;

您可以将接口覆盖为protected,如另一个答案所示。

您可以利用非虚拟接口模式来制作父级中定义的整个可访问公共接口。然后不管他们有什么对象,他们总是从接口类中获取公共 API:

class Interface

public:
    void foo()  foo_impl(); 

private:
    virtual void foo_impl() = 0;
;

class Concrete

private:
    virtual void foo_impl()  
;

【讨论】:

以上是关于C++ - 允许通过基类(接口)访问,禁止通过派生类访问(具体实现)?的主要内容,如果未能解决你的问题,请参考以下文章

通过基类指针C++访问派生类的成员

c++中的虚函数有啥作用?

关于C++基类、派生类的引用和指针

C++中的派生类,可以不定义对象直接调用基类的成员和调用自己的成员函数嘛???

C++基类和派生类的构造函数

C++ 为啥我可以从派生类调用基类的私有虚函数?