public
、private
和protected
继承有什么区别?public
、private
和protected
继承有什么区别?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,只是访问的问题。
关于使用protected和private继承,您可以在这里阅读。
为了回答这个问题,我想用自己的话来描述成员访问器。如果你已经知道这个,可以跳到“下一步”标题。
我知道有三个访问器: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
的匿名成员。和其他成员一样,它有一个访问修饰符,对外部访问施加了相同的控制。 - underscore_d限制继承的可见性将导致代码无法看到某些类继承了另一个类:从派生类到基类的隐式转换将不起作用,从基类到派生类的static_cast
也不起作用。
只有类的成员/友元可以看到私有继承,只有类的成员/友元和派生类可以看到保护继承。
公共继承
IS-A继承。按钮是窗口,任何需要窗口的地方都可以传递按钮。
class button : public window { };
protected继承
以保护方式实现继承。极少使用。在 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!
}
};
私有继承
实现为术语。基类的使用仅用于实现派生类。对trait非常有用,如果大小很重要(仅包含函数的空trait将利用空基类优化)。但通常情况下,包含更好的解决方案。对于字符串,大小很关键,因此常常在这里看到它的使用。
template<typename StorageModel>
struct string : private StorageModel {
public:
void realloc() {
// uses inherited function
StorageModel::realloc();
}
};
公共成员
Aggregate 聚合
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风格的强制类型转换允许以定义和安全的方式将派生类转换为受保护或私有基类,并且也可以将其反向转换。应该尽量避免这样做,因为它可能会使代码依赖于实现细节,但如果必要,您可以利用这种技术。这三个关键字在完全不同的上下文中也用于指定可见性继承模型。
该表收集了组件声明和继承模型的所有可能组合,呈现了子类在完全定义时对组件的访问权限。
上面的表格可以按如下方式解释(看一下第一行):
如果一个组件被声明为公共的,并且它的类被继承为公共的,则结果访问将是公共的。
例如:
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
方法不可访问。为什么?
当省略可见性修饰符时,编译器假定我们要应用所谓的私有继承。这意味着所有公共超类组件都变成了私有访问,私有超类组件将根本无法访问。因此,您不能在子类中使用后者。
我们必须告诉编译器要保留先前使用的访问策略。
class Sub : public Super { };
不要被误导:这并不意味着超类的私有成员(如存储变量)会以某种神奇的方式变成公共成员。私有组件仍将保持为私有的,公共组件也将保持为公共的。
Sub
类的对象可以做与其由Super
类创建的旧兄弟几乎相同的事情。“几乎”是因为作为子类的事实也意味着该类失去了对超类私有成员的访问权。我们不能编写一个Sub
类成员函数,它能直接操作存储变量。
这是一个非常严重的限制。是否有任何解决方法?
是的。
第三个访问级别称为受保护的。关键字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;
编译器将告知您出现了错误:'int Super::storage' 是受保护的
。
最后,该程序将产生以下输出:
storage = 101
这与派生类如何公开基类的公共成员有关。
正如litb所指出的,公共继承是大多数编程语言中常见的传统继承方式。它模拟了一个“IS-A”关系。私有继承,作为C ++中独特的一部分,“IMPLEMENTED IN TERMS OF”的关系。也就是说,您想在派生类中使用公共接口,但不希望派生类的用户可以访问该接口。许多人认为,在这种情况下,您应该聚合基类,即将基类作为派生类的成员,以便重用基类的功能。
Member in base class : Private Protected Public
继承类型: 对象继承为:
Private : Inaccessible Private Private
Protected : Inaccessible Protected Protected
Public : Inaccessible Protected Public
1) 公有继承:
a. 基类的私有成员在派生类中不可访问。
b. 基类的保护成员在派生类中仍然是保护的。
c. 基类的公有成员在派生类中仍然是公有的。
因此,其他类可以通过派生类对象使用基类的公有成员。
2) 保护继承:
a. 基类的私有成员在派生类中不可访问。
b. 基类的保护成员在派生类中仍然是保护的。
c. 基类的公有成员也成为派生类的保护成员。
因此,其他类不能通过派生类对象使用基类的公有成员;但它们对于派生类的子类是可用的。
3) 私有继承:
a. 基类的私有成员在派生类中不可访问。
b. 基类的保护和公有成员成为派生类的私有成员。
因此,其他类无法通过派生类对象访问基类的任何成员,因为它们在派生类中是私有的。因此,即使是派生类的子类也无法访问它们。
公有继承模拟了一种IS-A(是一个)关系。使用
class B {};
class D : public B {};
每一个 D
都是一个 B
。class B {};
class D : private B {};
一个 D
不是一个 B
,但是每一个 D
在它的实现中都使用了它的 B
。私有继承可以通过使用包含来代替而总是被消除:
class B {};
class D {
private:
B b_;
};
对于这个D
,也可以使用B
进行实现,这种情况下需要使用它的b_
。与继承相比,包含关系在类型之间的耦合性较小,因此通常应该被优先考虑。有时候使用包含而不是私有继承并不像私有继承那么方便。经常这只是因为懒得找借口。
我认为没有人知道protected
继承模型的作用。至少我还没有看到什么令人信服的解释。
D
私有地派生自B
,它可以覆盖B
的虚函数。(例如,如果B
是观察者接口,则D
可以实现它并将this
传递给需要这样一个接口的函数,而不是每个人都能使用D
作为观察者。)此外,D
可以通过执行using B::member
来选择性地在其接口中使B
的成员可用。当B
是成员时,这两种情况在语法上都很不方便实现。 - sbiprotected
继承和protected
构造函数很有用,其中包含一个virtual
基类:struct CommonStuff { CommonStuff(Stuff*) {/* assert !=0 */ } }; struct HandlerMixin1 : protected virtual CommonStuff { protected: HandlerMixin1() : CommonStuff(nullptr) {} /*...*/ }; struct Handler : HandlerMixin1, ... { Handler(Stuff& stuff) : CommonStuff(&stuff) {} };
- lorroAccessors | 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
基于 这个 Java 示例,我认为一个小表格胜过千言万语 :)
如果你从另一个类公开继承,那么每个人都知道你正在继承,并且可以通过基类指针被任何人使用多态。
如果你只保护地继承,那么只有你的子类才能使用你的多态性。
如果你私有地继承,那么只有你自己才能执行父类方法。
这基本上象征着其他类对于你和父类的关系所了解的知识。