【发布时间】:2010-10-26 00:15:37
【问题描述】:
C++中
public、private和protected继承有什么区别?
我在 SO 上找到的所有问题都涉及特定案例。
【问题讨论】:
标签: c++ inheritance encapsulation access-specifier c++-faq
C++中
public、private和protected继承有什么区别?
我在 SO 上找到的所有问题都涉及特定案例。
【问题讨论】:
标签: c++ inheritance encapsulation access-specifier c++-faq
class A
{
public:
int x;
protected:
int y;
private:
int z;
};
class B : public A
{
// x is public
// y is protected
// z is not accessible from B
};
class C : protected A
{
// x is protected
// y is protected
// z is not accessible from C
};
class D : private A // 'private' is default for classes
{
// x is private
// y is private
// z is not accessible from D
};
重要提示:B、C 和 D 类都包含变量 x、y 和 z。这只是访问权限的问题。
关于受保护和私有继承的使用,您可以阅读here。
【讨论】:
为了回答这个问题,我想先用我自己的话来描述一下成员的访问器。如果您已经知道这一点,请跳到标题“下一个:”。
我知道有三个访问器:public、protected 和 private。
让:
class Base {
public:
int publicMember;
protected:
int protectedMember;
private:
int privateMember;
};
Base 的东西也都知道Base 包含publicMember。Base 包含protectedMember。Base,没有人知道privateMember。“知道”是指“承认存在,因此能够访问”。
公共、私有和受保护的继承也是如此。让我们考虑一个类Base 和一个继承自Base 的类Child。
public,所有知道Base和Child的东西也都知道Child继承自Base。protected,则只有Child 及其子级知道它们是从Base 继承的。private,则除了Child 之外没有其他人知道继承。【讨论】:
SomeBase 继承就像是一种硬编码的方式来组成SomeBase 类型的匿名成员。与任何其他成员一样,它有一个访问说明符,它对外部访问施加相同的控制。
限制继承的可见性将使代码无法看到某个类继承了另一个类:从派生到基的隐式转换不起作用,从基到派生的static_cast 也不起作用.
只有类的成员/朋友才能看到私有继承,只有成员/朋友和派生类才能看到受保护的继承。
公共继承
IS-A 继承。按钮是一个窗口,在需要窗口的任何地方,也可以传递一个按钮。
class button : public window { };
受保护继承
受保护的实施条款。很少有用。在boost::compressed_pair 中使用以从空类派生并使用空基类优化节省内存(下面的示例不使用模板来保持这一点):
struct empty_pair_impl : protected empty_class_1
{ non_empty_class_2 second; };
struct pair : private empty_pair_impl {
non_empty_class_2 &second() {
return this->second;
}
empty_class_1 &first() {
return *this; // notice we return *this!
}
};
私有继承
按条款实施。基类的使用仅用于实现派生类。对特征有用,如果大小很重要(只包含函数的空特征将利用空基类优化)。不过,containment 通常是更好的解决方案。字符串的大小很关键,所以这里经常使用它
template<typename StorageModel>
struct string : private StorageModel {
public:
void realloc() {
// uses inherited function
StorageModel::realloc();
}
};
公众成员
聚合
class pair {
public:
First first;
Second second;
};
访问器
class window {
public:
int getWidth() const;
};
受保护的成员
为派生类提供增强的访问权限
class stack {
protected:
vector<element> c;
};
class window {
protected:
void registerClass(window_descriptor w);
};
私人会员
保留实现细节
class window {
private:
int width;
};
请注意,C 风格的强制转换有意允许以一种已定义且安全的方式将派生类强制转换为受保护或私有的基类,并且也可以强制强制转换为另一个方向。应该不惜一切代价避免这种情况,因为它会使代码依赖于实现细节——但如果有必要,您可以利用这种技术。
【讨论】:
这三个关键字也用在完全不同的上下文中来指定可见性继承模型。
该表收集了组件声明和继承模型的所有可能组合,展示了子类完全定义后对组件的访问。
上表解释如下(看第一行):
如果一个组件声明为public并且其类继承为public,则结果访问是公开的。
一个例子:
class Super {
public: int p;
private: int q;
protected: int r;
};
class Sub : private Super {};
class Subsub : public Sub {};
Subsub 类中变量 p、q、r 的最终访问权限为 none。
另一个例子:
class Super {
private: int x;
protected: int y;
public: int z;
};
class Sub : protected Super {};
对 Sub 类中的变量 y、z 的最终访问是 protected 的,而对于变量 x 的访问是 none。
更详细的例子:
class Super {
private:
int storage;
public:
void put(int val) { storage = val; }
int get(void) { return storage; }
};
int main(void) {
Super object;
object.put(100);
object.put(object.get());
cout << object.get() << endl;
return 0;
}
现在让我们定义一个子类:
class Sub : Super { };
int main(void) {
Sub object;
object.put(100);
object.put(object.get());
cout << object.get() << endl;
return 0;
}
定义的名为 Sub 的类是名为 Super 的类的子类,或者 Sub 类派生自 Super 类。
Sub 类既没有引入新变量,也没有引入新函数。这是否意味着Sub 类的任何对象都继承了Super 类实际上是Super 类对象的副本之后的所有特征?
否。没有。
如果我们编译以下代码,我们只会得到编译错误,指出 put 和 get 方法无法访问。为什么?
当我们省略可见性说明符时,编译器假定我们将应用所谓的私有继承。这意味着所有public超类组件都变成private访问,私有超类组件根本无法访问。因此,这意味着您不能在子类中使用后者。
我们必须通知编译器我们要保留以前使用的访问策略。
class Sub : public Super { };
不要被误导:这并不意味着 Super 的私有组件 类(如存储变量)将在 有点神奇的方式。 私有组件将保持私有、公开 将保持公开。
Sub 类的对象可以做“几乎”与从Super 类创建的兄弟姐妹一样的事情。 “几乎”,因为作为子类的事实也意味着 类无法访问超类的私有组件。我们不能编写能够直接操作存储变量的Sub 类的成员函数。
这是一个非常严重的限制。有什么解决办法吗?
是的。
第三个访问级别称为protected。关键字protected 意味着用它标记的组件在被任何子类使用时表现得像一个公共组件,而在世界其他地方看起来像一个私有组件。 -- 这仅适用于公共继承的类(如我们示例中的 Super 类) --
class Super {
protected:
int storage;
public:
void put(int val) { storage = val; }
int get(void) { return storage; }
};
class Sub : public Super {
public:
void print(void) {cout << "storage = " << storage;}
};
int main(void) {
Sub object;
object.put(100);
object.put(object.get() + 1);
object.print();
return 0;
}
正如您在示例代码中看到的,我们为Sub 类提供了一项新功能,它做了一件重要的事情:它从 Super 类访问存储变量。
如果变量被声明为私有是不可能的。 在主函数范围内,变量仍然是隐藏的,所以如果你写这样的东西:
object.storage = 0;
编译器会通知你这是一个error: 'int Super::storage' is protected。
最后,最后一个程序会产生如下输出:
storage = 101
【讨论】:
这与基类的公共成员如何从派生类公开有关。
正如 litb 所指出的,公共继承是您将在大多数编程语言中看到的传统继承。也就是说,它模拟了“IS-A”关系。私有继承是 C++ 特有的 AFAIK,是一种“在条款中实现”的关系。也就是说,您希望使用派生类中的公共接口,但不希望派生类的用户有权访问该接口。许多人认为,在这种情况下,您应该聚合基类,而不是将基类作为私有基类,而是在派生的成员中创建,以便重用基类的功能。
【讨论】:
Member in base class : Private Protected Public
继承类型: 对象继承为:
Private : Inaccessible Private Private
Protected : Inaccessible Protected Protected
Public : Inaccessible Protected Public
【讨论】:
1) 公共继承:
一个。 Base 类的私有成员在 Derived 类中不可访问。
b. Base 类的受保护成员在 Derived 类中仍然受到保护。
c。 Base 类的公共成员在 Derived 类中仍然是公共的。
因此,其他类可以通过派生类对象使用基类的公共成员。
2) 受保护的继承:
一个。 Base 类的私有成员在 Derived 类中不可访问。
b. Base 类的受保护成员在 Derived 类中仍然受到保护。
c。 Base 类的公共成员也成为 Derived 类的受保护成员。
所以,其他类不能通过派生类对象使用基类的公共成员;但它们可用于 Derived 的子类。
3) 私有继承:
一个。 Base 类的私有成员在 Derived 类中不可访问。
b. Base 类的受保护和公共成员成为 Derived 类的私有成员。
因此,其他类不能通过派生类对象访问基类的任何成员,因为它们在派生类中是私有的。所以,即使是 Derived 的子类 类无法访问它们。
【讨论】:
公共继承对 IS-A 关系建模。与
class B {};
class D : public B {};
每个D 都是一个 B。
私有继承对 IS-IMPLEMENTED-USING 关系(或任何所谓的)建模。与
class B {};
class D : private B {};
D不是B,但每个D 在其实现中都使用它的B。私有继承总是可以通过使用容器来消除:
class B {};
class D {
private:
B b_;
};
这个D 也可以使用B 来实现,在本例中使用它的b_。与继承相比,包含在类型之间的耦合不那么紧密,因此通常应该首选它。有时使用包含而不是私有继承不如私有继承方便。这通常是懒惰的一个蹩脚的借口。
我认为没有人知道protected 继承模型。至少我还没有看到任何令人信服的解释。
【讨论】:
D 私有派生自D,它可以覆盖B 的虚函数。 (例如,如果B 是一个观察者接口,那么D 可以实现它并将this 传递给需要一个接口的函数,而每个人都不能使用D 作为观察者。)另外,@ 987654341@ 可以通过执行using B::member 选择性地使B 的成员在其界面中可用。当B 是成员时,两者在语法上都不方便实现。
protected继承我发现virtual基类和protectedctor:struct CommonStuff { CommonStuff(Stuff*) {/* assert !=0 */ } }; struct HandlerMixin1 : protected virtual CommonStuff { protected: HandlerMixin1() : CommonStuff(nullptr) {} /*...*/ }; struct Handler : HandlerMixin1, ... { Handler(Stuff& stuff) : CommonStuff(&stuff) {} };
如果您从另一个类公开继承,每个人都知道您正在继承,并且任何人都可以通过基类指针多态地使用您。
如果您受保护地继承,则只有您的子类才能多态地使用您。
如果你私有继承,只有你自己能够执行父类方法。
这基本上象征着其他班级对你与父班级关系的了解
【讨论】:
从您的类继承的任何类都可以访问受保护的数据成员。但是,私有数据成员不能。假设我们有以下内容:
class MyClass {
private:
int myPrivateMember; // lol
protected:
int myProtectedMember;
};
在您对此类的扩展中,引用 this.myPrivateMember 将不起作用。但是,this.myProtectedMember 会。该值仍然是封装的,所以如果我们有一个名为myObj 的类的实例化,那么myObj.myProtectedMember 将不起作用,因此它在功能上类似于私有数据成员。
【讨论】:
Accessors | Base Class | Derived Class | World
—————————————+————————————+———————————————+———————
public | y | y | y
—————————————+————————————+———————————————+———————
protected | y | y | n
—————————————+————————————+———————————————+———————
private | | |
or | y | n | n
no accessor | | |
y: accessible
n: not accessible
基于this java 的示例...我认为一张小表值得一千字:)
【讨论】:
总结:
继承时,您可以(在某些语言中)在某个方向更改数据成员的保护类型,例如从受保护到公开。
【讨论】:
基类的私有成员只能由该基类的成员访问。
基类的公共成员可以被该基类的成员、其派生类的成员以及基类和派生类之外的成员访问。
基类的成员及其派生类的成员都可以访问基类的受保护成员。
私有:基础
受保护:基础 + 派生
public:基础 + 派生 + 任何其他成员
【讨论】:
我找到了一个简单的答案,因此也想将其发布以供我将来参考。
来自http://www.learncpp.com/cpp-tutorial/115-inheritance-and-access-specifiers/的链接
class Base
{
public:
int m_nPublic; // can be accessed by anybody
private:
int m_nPrivate; // can only be accessed by Base member functions (but not derived classes)
protected:
int m_nProtected; // can be accessed by Base member functions, or derived classes.
};
class Derived: public Base
{
public:
Derived()
{
// Derived's access to Base members is not influenced by the type of inheritance used,
// so the following is always true:
m_nPublic = 1; // allowed: can access public base members from derived class
m_nPrivate = 2; // not allowed: can not access private base members from derived class
m_nProtected = 3; // allowed: can access protected base members from derived class
}
};
int main()
{
Base cBase;
cBase.m_nPublic = 1; // allowed: can access public members from outside class
cBase.m_nPrivate = 2; // not allowed: can not access private members from outside class
cBase.m_nProtected = 3; // not allowed: can not access protected members from outside class
}
【讨论】:
它本质上是派生类中基类的公共和受保护成员的访问保护。通过公共继承,派生类可以看到基类的公共和受保护成员。使用私有继承,它不能。使用protected,派生类和任何派生类都可以看到它们。
【讨论】: