我对 C++ 继承中的访问级别以及更普遍地应该如何设计 C++ 类有一些困惑。
C++ 具有三个可在继承时使用的访问级别说明符:
派生类:公共基础
称为公共继承,代表IsA关系。公开继承时,基的公共成员在 Derived 中保持公开,受保护的成员在 Derived 中保持保护,私有成员是私有的(不能从 Derived 访问)。
class Derived : protected Base
称为受保护的继承。这种继承很少见,如果您不想在作为派生(或通过派生的接口)访问时暴露 Base 的公共部分,则可以使用这种继承。在这种继承中,基类中的公共成员在派生中受到保护。
另请参阅 C++ 常见问题解答
派生类:私有基础
称为私有继承。这种继承可以用来模拟遏制(Herb Sutter - 继承的使用和滥用)。这里 Base 中的公共成员在通过派生接口访问时变为私有。
还可以注意到,受保护和私有继承并不代表经典的 IsA 关系。以下多态行为仅在公开继承时才有可能:
Derived d;
Base* b = &d;
但是,对于私有继承,多态行为在第一个派生类中是可能的(但在后续类中则不行)。
struct Base{};
struct Derived : private Base
{
Derived()
{
//IsA holds within member functions of Derived, but not
// outside
Base* b = this;
}
};
struct Derived2 : public Derived
{
Derived2()
{
Base* b = this; //Fails to compile...inaccessible base
}
};
int main()
{
Derived d;
Base* b = &d; //Fails to compile
}
对于受保护的继承,多态行为在所有后续派生类中都是可能的(因此上面的 Derived2 的构造函数中的代码将编译),但在类成员函数之外。
Herb Sutter 评论了在Uses and abuses中使用非公共继承的原因。
最后,关于您上面的示例的评论:通常 Car 将是抽象的(一个接口,仅由纯虚函数组成),因此它不包含任何数据成员,而是将其开放给实现。这是我在某处Sutter - Exceptional C++听说过的关于继承的指南:
公开继承,以便被多态使用基类的代码重用。
您的示例将/可能变为:
struct Car
{
//Note: const used as brandName should not modify logical state.
virtual const std::string& brandName() const = 0;
//...virtual ~Car(), amongst others, depending
// on whether you intend to be deleted via this interface...
};
// Note: public inheritance implied by struct
struct CarFromFile: /*public*/ Car
{
CarFromFile( std::ifstream& file )
: brandName_( strFrom( file ) ),
manufacturer_( strFrom( file )
{
}
virtual const std::string& brandName() const{ return brandName_; }
private:
std::string strFrom( std::ifstream& file )
{
std::string value;
if( file >> value ) { return value; }
throw std::runtime_error( "Invalid file format!" );
}
std::string brandName_;
std::string manufacturer_;
//etc...
};
您使访问器抽象的事实允许从派生的实施者的角度自由,并定义基客户端所需的服务,而与实际数据的外观无关。