这种方式是否可以安全地扩展具有虚拟保护方法的库?

时间:2015-02-03 08:29:12

标签: c++ protected

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

#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使用BC的{​​{1}},具体取决于运行时可用的信息。我无法修改库,f()继承C是不切实际的,因为BB复杂得多。这就是我想出的:

C

我在MacOSX上测试了它,它可以正常使用g ++和clang ++。但它通常安全吗?如果没有,有没有更好的方法呢?

3 个答案:

答案 0 :(得分: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;
};

这个想法是保持最多一个指针非空(或找到另一种方法来确定你是BB还是C - boost::variant也可能有用)。


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

答案 1 :(得分:1)

不,不是。

您正在将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();

答案 2 :(得分:0)

@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;
};

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