我对C ++继承中的访问级别有一些混淆,更一般地说我应该如何设计C ++类。
class Car
{
public:
private:
string brandName;
}
class Sedan: public Car
{
public:
// this function needs to know the Sedan's brandName
void someFun()
{
brandName = "sss"; // error!!!
}
private:
}
在上面的示例中,Car作为基类具有私有成员“brandName”,而作为Car的派生类的Sedan继承了该成员“brandName”。正如在Effective C ++中所解释的那样,公共继承产生了“is-a”关系,这意味着Sedan是一辆Car,现在我可以说Sedan有一个“brandName”吗?如果答案是肯定的,为什么Sedan不能在someFun中访问自己的属性?
我知道解决方案是将brandName的访问级别更改为protected,我的困惑是,哪些功能或条件使变量成员访问级别,换句话说我如何决定给定成员应该附加哪个访问级别?
如果你推荐任何关于这个主题的书或文章,我也会喜欢它。
答案 0 :(得分:3)
现在可以说轿车有“品牌名称”吗?
不,没有Car
。 brandName
是一个私有的,隐藏于其他任何人的详细信息,并没有为类的公共接口做出贡献,这就是Car
的本质。因此,虽然从技术上讲,在汽车中的某个地方有一个品牌名称,因此在轿车中,从纯粹的OO视图中无关紧要。类似于仅由公共继承而非私有继承表达的“Is-A”关系,OO观点中的“Has-A”关系仅在组合或关联时出现可公开访问/可见(主要通过getter / setter)。
juanchopanza的回答和对它的评论让我在网上偷偷摸摸,试图找到有关Is-A和Has-An关系的资源。 this转录似乎表明这两个术语并非源于面向对象的思想。事实上,OO文献似乎引用Liskov替代原则而不是使用“Is-A”关系。它强调封装和不透明对象,因此它主要涉及类的公共接口以及它们的行为如何影响关联的对象。虽然“Has-A”关系可以是关联,但它也可以是聚合。在这种情况下,由于brandName
不是外部关联对象(在C ++中我们用指针或引用成员表示这个事实),它是一个聚合 - “Has-A”,隐藏在opaque中{ {1}}因此对OO不感兴趣(上述答案在语义上有效)。
我知道解决方案是将brandName的访问级别更改为受保护的
NO! Car
的访问级别必须保持私有状态,以便brandName
可以完全控制发生的情况。如果要在派生类中访问它,请提供受保护的getter和/或setter。
如果你推荐任何关于这个主题的书或文章,我也会喜欢它。
任何关于面向对象设计的书都应该这样做。如果您阅读其中的两个或更多内容,您将会知道该做什么和不该做什么。
答案 1 :(得分:2)
我可以说轿车有“品牌名称”吗?
是的,肯定有string
类型的对象叫Car::brandName
(这是否构成一个OOP“有一个”关系对我来说并不完全清楚,但可能不是:见评论)
如果答案是肯定的,为什么Sedan不能在someFun中访问自己的属性?
简单,因为brandName
是private
。这些是语言规则。如果要为非公共数据成员提供派生类访问权限,可以将其设为protected
。
如果你推荐任何关于这个主题的书或文章,我也会喜欢它。
有一个list of books here。也许“C ++编程语言”可以很好地解释这个特定的方面。
答案 2 :(得分:1)
公众成员可以自由使用。
私人成员只能使用当前的类,而不能使用其他任何人。它主要用于类的内部成员使用和保存它自己的目的或提供访问它们的方法。
受保护的成员可以被继承的类访问,正如您的示例所示 - brandName是为了使用继承类,因此它应该受到保护。
答案 3 :(得分:1)
您将来自外部世界的隐藏者 brandName 设为私有。使其受保护,以便孩子可以继承它。要了解有关说明符的继承规则,请转到HERE
答案 4 :(得分:1)
我对C ++继承中的访问级别有一些混淆,更一般地说我应该如何设计C ++类。
C ++有三个访问级别说明符,可以在继承时使用:
类派生:public Base
称为公共继承并表示IsA关系。当公开继承时,基础的公共成员在Derived中保持公开,受保护的成员在Derived中保持受保护,私有成员是私有的(并且无法从Derived访问)。
类派生:受保护的基础 被称为受保护的继承。这种继承很少,并且如果您不希望在派生时(或通过Derived接口)访问Base的公共部分,则会使用此类继承。在这种继承中,基础中的公共成员在派生中受到保护。
类派生:私人基地
被称为私有继承。这种继承可用于模拟包含(Herb Sutter - Uses and abuses of inheritance)。当通过派生界面访问时,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...
};
你使访问者抽象的事实允许自由 派生的实现者的角度,并定义服务 base的客户端要求,与实际数据的外观无关。
答案 5 :(得分:0)
public
,protected
和private
的分类旨在允许用户将公开可用的界面与内部实施细节分开。因此,非public
的内容无法访问,迫使class
的用户使用提供的public
接口。
如果某些内容是private
,那么即使继承会使成员成为派生class
的一部分,也不允许派生访问它。如果希望允许对不属于class
接口的接口和成员进行派生public
访问,则确切时间使用protected
。