这种使用虚拟保护方法扩展库的方法安全吗?

Posted

技术标签:

【中文标题】这种使用虚拟保护方法扩展库的方法安全吗?【英文标题】:Is this way to extend a library with virtual protected methods safe? 【发布时间】:2015-04-02 09:08:56 【问题描述】:

我正在使用的外部库具有以下结构:

#include <stdio.h>

struct A 
protected:
    virtual void f() 
;

struct B : A 
    void f() printf("B\n");
;

我现在已经扩展了这个库

struct C : A 
    void f() printf("C\n");
;

现在我想要一个struct D : A,它使用BCf(),具体取决于运行时可用的信息。我无法修改库,并且从B 继承C 是不切实际的,因为BC 复杂得多。这就是我想出的:

struct _A : A 
    // bypass protected
    inline void _f() f();
;

struct D : A 
    D(char x) 
        switch (x) 
        case 'B': p = (_A*) new B(); break;
        case 'C': p = (_A*) new C(); break;
        
    

    ~D() delete p;

    void f() p->_f();
    _A* p;
;

int main() 
    D b('B'), c('C');

    b.f();
    c.f();

我在 MacOSX 上对其进行了测试,它在 g++ 和 clang++ 上都能正常工作。但是一般来说安全吗?如果没有,有更好的方法吗?

【问题讨论】:

BC_A 不相关。试图将一个视为其他之一是未定义的行为。 【参考方案1】:

不,你所拥有的并不安全。 BC 不继承自 _A,因此将它们视为未定义行为。它可能会起作用,它可能会崩溃,它可能会在线订购披萨,这一切都取决于当前的月相。所以不要这样做。

而且我相信您不必这样做。以下应该有效:

struct BB : B

  using B::f;  // Make it public
;


struct D : A

    D(char x) 
        switch (x) 
        case 'B': b.reset(new BB()); break;
        case 'C': c.reset(new C()); break;
        
    

    void f()
    
      if (b) b->f();
      else c->f();
    

    std::unique_ptr<BB> b;
    std::unique_ptr<C> c;
;

这个想法是最多保持一个指针非空(或者找到另一种方法来确定您是否有 BBCboost::variant 也可能有用)。


另请注意,名称_A 对于用户代码是非法的。以下划线后跟大写字母开头的标识符是为编译器和标准库保留的。

【讨论】:

【参考方案2】:

不,不是。

您正在将 B 转换为 _A,这可能会在此过程中发生变化。当前 _A 与 A 相同的事实只是巧合,您不能依赖。

如果您的目标是访问受保护的函数,您可以使用 pImpl 方法:

struct _Abstract 
     virtual void doF()=0;

struct _B : B, _Abstact 
     void doF()f();;

struct _C : C, _Abstract 
     void doF()f();;


struct D 

     D (_C* impl)
     
          pImpl = impl;
     
     D (_B* impl)
     
          pImpl = impl;
     
     void f()  pImpl->dooF();;

     private:
        _Abstract* pImpl;

那么你可以拥有

D* b = new D(new _B());
D* c = new D(new _C());

b->f();
c->f();

【讨论】:

【参考方案3】:

@MichaelCMS 的回答似乎是最好的,因为从_Abstract 继承使我粗略的演员表正式正确。此外,与@Angew 的回答相反,如果有很多类,如BC,它可以很好地扩展。我不得不稍微修改它以适用于我的示例:

struct _Abstract 
    virtual void _f() = 0;
    virtual ~_Abstract() 
;

template <class T>
struct _A : T, _Abstract 
    // bypass protected
    void _f() T::f();
;

struct D : A 
    D(char x) 
        switch (x) 
        case 'B': p = new _A<B>(); break;
        case 'C': p = new _A<C>(); break;
        
    

    ~D() delete p;

    void f() p->_f();
    _Abstract* p;
;

显然,如果图书馆设计师将f() 公开,整个问题就会消失...所以,图书馆设计师,请公开您的方法!您无法预测所有用例,您只是在强迫我们通过或多或少粗略的方法绕过您的受保护(甚至是私人)......

编辑

在实践中,双重继承解决方案效果不佳,因为它在我的案例中导致了钻石继承问题,这使事情变得非常复杂。受到@Charles Bailey 对Accessing protected member of template parameter 的回答的启发,我想出了这个:

struct U : A 
    typedef void (A::*f_t)();
    static inline f_t _f() return &U::f;
;

struct D : A 
    D(char x) 
        switch (x) 
        case 'B': p = new B(); break;
        case 'C': p = new C(); break;
        
    

    ~D() delete p;

    void f() (p->*U::_f())();
    A* p;
;

这直接从根本上解决了问题,即方法被设置为受保护而不是公共的,同时不会不必要地使继承情况复杂化。由于这只是通过成员函数指针删除受保护属性的技巧,因此它应该是安全的:-)

【讨论】:

以上是关于这种使用虚拟保护方法扩展库的方法安全吗?的主要内容,如果未能解决你的问题,请参考以下文章

python--标准库与扩展库的导入与使用

Python扩展库的安装方法

使用pybind11来快速开发python程序扩展库

Boost库的分类

不用pip官方源安装Python扩展库的方法(使用国内的镜像源)

不用pip官方源安装Python扩展库的方法(使用国内的镜像源)