C++继承和受保护的基类成员的访问:这样做Java风格是个坏主意吗?

mor*_*ort 3 c++ inheritance protected

不幸的是,我主要在Java的背景下研究了类设计和设计模式; 因此,我有时很难将熟悉的模式转换为C++.

假设我们想要一个基类,其功能由子类扩展.在Java中,我们会这样做:

public class BaseClass<T> {
     //T is used here
     protected int foo = 0;         
}

public class DerivedClass<T> extends BaseClass<T> {
    public void incr_foo() {
         ++foo;
    }
}
Run Code Online (Sandbox Code Playgroud)

我直接翻译成C++:

template<class T>
class BaseClass {
protected:
    size_t foo = 0;
     //T is used here
};

template<class T>
class DerivedClass : public BaseClass<T> {
public:
     void incr_foo() {
          ++(this->foo);
     }
};
Run Code Online (Sandbox Code Playgroud)

由于'protected'的C++语义来自Java语义,我们需要使用类似'this-> foo'的东西来访问基类的受保护成员.

编辑:如果我们只使用'++ foo',编译器会给出错误:'使用未声明的标识符foo'.

EditEnd

如果您需要访问基类的几个成员,这可能会有点乏味,并且不太好阅读.

这是一个很好的设计决定吗?如果没有,有什么更好的方法来实现这一目标?

Rei*_*ica 11

这与受保护的成员无关; 你会得到与公共成员完全相同的错误.

真正的原因是模板被调用.更具体地说,您的类模板具有依赖于模板参数的基类.这意味着在解析模板时,编译器不会(能够)查看该基类以查找使用不合格的继承成员.这是有道理的:当解析模板时,模板参数值还不知道,因此编译器不知道基类将具有哪些成员(记住部分和全部特化存在!).

为了克服这个问题,你必须以某种方式告诉编译器名称foo取决于模板参数(它是一个依赖名称).执行此操作后,编译器在解析模板时不会尝试解析它; 它会推迟解析,直到模板被实例化.此时,模板参数是已知的,因此可以检查基类.

您有三种方法可以将成员名称标记为依赖:

  1. this正如您所做的那样,请参阅名称:this->foo

  2. 通过基类资格来参考名称: BaseClass<T>::foo

  3. 将名称带入派生类的范围:

    template<class T>
    class DerivedClass : public BaseClass<T> {
    protected:
         using BaseClass<T>::foo;  // from this point on, `foo` is a dependent name
    public:
         void incr_foo() {
              ++foo;  // works fine now
         }
    };
    
    Run Code Online (Sandbox Code Playgroud)


Mik*_*our 5

BaseClass<T>是一个依赖类型 - 它取决于用于实例化的模板参数DerivedClass.在DerivedClass实例化之前,编译器不知道类型是什么:它可能是通用模板的实例化,或者它可能是具有不同成员的显式特化.

因此,在定义中DerivedClass,无法知道哪些名称引用基类的成员.您必须使用this->或指定它们BaseClass::.

在具有非依赖基类的类中,您可以使用任何可访问的基类成员,就像它们是直接成员一样,就像在Java中一样.