2012-08-04 60 views
3

可能重复:
Accessing protected members in a derived class来自接口的模板 - 多态性停止工作?

如果我有一个抽象基类和从它派生的混凝土模板类,它具有一个使用指针的基类的方法 - 似乎派生类停止将其本身视为派生自它:

class AbstractBase 
{ 
protected: 
    virtual void test() = 0; 
}; 

template < class T > 
class Derived : public AbstractBase 
{ 
public: 
    virtual void call(AbstractBase* d) { d->test(); } // Error! 
protected: 
    virtual void test() {} 
}; 

int main() 
{ 
    Derived<int> a; 
    Derived<int> b; 

    b.call(&a); 

    return EXIT_SUCCESS; 
} 

此错误与:

'虚拟无效AbstractBase ::测试()' 被保护

编译器的没有错,这是绝对protected - 但如果从AbstractBaseDerived<T>继承,它为什么抱怨?

+3

这是不相关的模板。使派生一个非模板类,你会得到相同的错误。 – Mat 2012-08-04 14:45:37

+0

+1你是对的,但在我的无知中,当我遇到问题时,我搜索了这个问题,所以我会把问题留给其他做同样事情的人。 – cmannett85 2012-08-04 15:24:27

回答

3

不允许的原因是因为AbstractBase作为类型声明test被保护。除非当前班级是AbstractBase的直系后裔,否则这将使其对所有人都是私人的。即使如此,该类只能通过同一类的对象访问成员,而不是其他类的对象,而不是直接从AbstractBase本身访问成员。

template < class T > 
class Derived : public AbstractBase 
{ 
public: 
    virtual void call(Derived * d) { 
     d->test(); // ok, d has same type as this 
     AbstractBase *b = this; 
     b->test(); // not ok 
    } 
protected: 
    virtual void test() {} 
}; 

如上所示,您可以将其指定为相同类型的指针。或者,您可以为Derived创建代理基类,以实施您的virtual方法,以调用test。这将允许从不同的Derived类型访问。

class DerivedBase : public virtual AbstractBase 
{ 
public: 
    virtual void call(DerivedBase * d) { d->test(); } 
}; 

template < class T > 
class Derived : public DerivedBase 
{ 
protected: 
    virtual void test() {} 
}; 

而且可以访问这种方式:

Derived<int> a; 
    Derived<int> b; 
    Derived<float> c; 

    b.call(&a); 
    c.call(&a); 
+0

由于结构性原因,我无法在我的“真实”代码上执行第一个选项,但它很适合注意。至于第二,你能解释为什么这有效吗?在我看来,这只是另一层抽象。 – cmannett85 2012-08-04 15:12:20

+0

忘掉模板。它不会工作。你正在工作,我正在改变。 – jxh 2012-08-04 15:18:43

+0

@ cbamber85:抽象层工作的原因是它成为一个通用的基类,它为'call'提供一个公共接口,用于调用受保护的'test'方法。然后,所有的“派生”代替继承自“DerivedBase”。 – jxh 2012-08-04 15:23:34

1

这是不相关的模板,但受保护的成员访问一般。见部11.4受保护的成员接入[class.protected]最新公开可用的C++草案Standard

超出在第11 前面描述的附加的访问检查时应用非静态数据成员或非 - 如果前面描述的是 ,则授予访问受保护成员的权限,因为参考 发生在朋友或某个类C的成员中。如果访问要形成 指向成员(5.3.1)的指针,嵌套名称指定器应表示C 或类派生d。所有其他访问涉及(可能为 隐式)对象表达式(5.2.5)。在这种情况下,类 对象表达的应是C或从C

派生的类[例如:

class B { 
protected: 
    int i; 
    static int j; 
}; 

class D1 : public B { 
}; 

class D2 : public B { 
    friend void fr(B*,D1*,D2*); 
    void mem(B*,D1*); 
}; 

void fr(B* pb, D1* p1, D2* p2) { 
    pb->i = 1; // ill-formed 
    p1->i = 2; // ill-formed 
    p2->i = 3; // OK (access through a D2) 
    p2->B::i = 4; // OK (access through a D2, even though 
       // naming class is B) 
    int B::* pmi_B = &B::i; // ill-formed 
    int B::* pmi_B2 = &D2::i; // OK (type of &D2::i is int B::*) 
    B::j = 5; // OK (because refers to static member) 
    D2::j = 6; // OK (because refers to static member) 
} 
+0

+1非常简洁的答案,谢谢。 – cmannett85 2012-08-04 15:28:19