社区
C++ 语言
帖子详情
类的公有继承和私有继承有什么不同
zydcat
2003-04-29 10:11:09
问问
...全文
560
13
打赏
收藏
类的公有继承和私有继承有什么不同
问问
复制链接
扫一扫
分享
转发到动态
举报
写回复
配置赞助广告
用AI写文章
13 条
回复
切换为时间正序
请发表友善的回复…
发表回复
打赏红包
短歌如风
2003-04-30
打赏
举报
回复
楼上说的好象有点问题:
公有继承是接口继承,同时也(可以)是实现继承。
私有继承只是实现继承。
AMin2001
2003-04-30
打赏
举报
回复
共有继承是接口继承,成员函数的实现代码并没有继承
私有继承是实现继承,成员函数的实现代码进入继承类的私有部分
zydcat
2003-04-30
打赏
举报
回复
多谢大家的讨论,收益非浅,先放分,讨论继续啊,:)
MessiahLS
2003-04-30
打赏
举报
回复
可以参考一下设计模式那本书的11页,“类继承与接口继承的比较”
短歌如风
2003-04-30
打赏
举报
回复
是吗?那看一下Template Method模式,它的实现必然是用公有继承的,你不能不承认子类实际上是继承了父类的一部分实现吧。
AMin2001
2003-04-30
打赏
举报
回复
to plainsong(伤心的风★短歌) :
公有继承如果也有实现继承的话
那就是类的设计者的失误
QiHY
2003-04-30
打赏
举报
回复
class parent{void f();};
class child1 :: public parent{};
class child2 :: private parent{};
parent *p;
child1 c1;child2 c2;
p = &c1;//ok
p = &c2;//wrong
c1.f();//ok
c2.f();//wrong
公有继承可以隐式转换到父类类型,私有继承不可以。
公有继承可以将父类的接口函数作为自己的接口函数,私有继承不可以。
chon81
2003-04-30
打赏
举报
回复
公有继承,子类可以访问基类的公有成员和保护成员,子类的实例也可以访问基类的公有成员。
私有继承,子类只能访问基类的公有成员,面子类的实例对基类什么也访问不了.
zydcat
2003-04-29
打赏
举报
回复
to: plainsong(伤心的风★短歌)
多谢,我仔细看看先
zydcat
2003-04-29
打赏
举报
回复
to: hddhddhdd(还刀的)
sorry,我还是不明白,能说清楚些吗
hddhddhdd
2003-04-29
打赏
举报
回复
你能分得清“公有”“私有”的区别吗?
如果可以的话,那么
“公有继承”就是把它继承为“公有”的
私有就是把它继承为“私有”的
短歌如风
2003-04-29
打赏
举报
回复
但如果有些用户没认识到这一点怎么办?如果他们错误地认为使用GenericStack更高效,或者,如果他们鲁莽而轻率地认为类型安全不重要,那该怎么办?怎么才能阻止他们绕过IntStack和CatStack而直接使用GenericStack(这会让他们很容易地犯类型错误,而这正是设计C++所要特别避免的)呢?
没办法!没办法防止。但,也许应该有什么办法。
在本条款的开始我就提到,要表示类之间 "用...来实现" 的关系,有一个选择是通过私有继承。现在这种情况下,这一技术就比分层更有优势,因为通过它可以让你告诉别人:GenericStack使用起来不安全,它只能用来实现其它的类。具体做法是将GenericStack的成员函数声明为保护类型:
class GenericStack {
protected:
GenericStack();
~GenericStack();
void push(void *object);
void * pop();
bool empty() const;
private:
... // 同上
};
GenericStack s; // 错误! 构造函数被保护
class IntStack: private GenericStack {
public:
void push(int *intPtr) { GenericStack::push(intPtr); }
int * pop() { return static_cast<int*>(GenericStack::pop()); }
bool empty() const { return GenericStack::empty(); }
};
class CatStack: private GenericStack {
public:
void push(Cat *catPtr) { GenericStack::push(catPtr); }
Cat * pop() { return static_cast<Cat*>(GenericStack::pop()); }
bool empty() const { return GenericStack::empty(); }
};
IntStack is; // 正确
CatStack cs; // 也正确
和分层的方法一样,基于私有继承的实现避免了代码重复,因为这个类型安全的接口类只包含有对GenericStack函数的内联调用。
在GenericStack类之上构筑类型安全的接口是个很花俏的技巧,但需要手工去写所有那些接口类是件很烦的事。幸运的是,你不必这样。你可以让模板来自动生成它们。下面是一个模板,它通过私有继承来生成类型安全的堆栈接口:
template<class T>
class Stack: private GenericStack {
public:
void push(T *objectPtr) { GenericStack::push(objectPtr); }
T * pop() { return static_cast<T*>(GenericStack::pop()); }
bool empty() const { return GenericStack::empty(); }
};
这是一段令人惊叹的代码,虽然你可能一时还没意识到。因为这是一个模板,编译器将根据你的需要自动生成所有的接口类。因为这些类是类型安全的,用户类型错误在编译期间就能发现。因为GenericStack的成员函数是保护类型,并且接口类把GenericStack作为私有基类来使用,用户将不可能绕过接口类。因为每个接口类成员函数被(隐式)声明为inline,使用这些类型安全的类时不会带来运行开销;生成的代码就象用户直接使用GenericStack来编写的一样(假设编译器满足了inline请求 ---- 参见条款33)。因为GenericStack使用了void*指针,操作堆栈的代码就只需要一份,而不管程序中使用了多少不同类型的堆栈。简而言之,这个设计使代码达到了最高的效率和最高的类型安全。很难做得比这更好。
本书的基本认识之一是,C++的各种特性是以非凡的方式相互作用的。这个例子,我希望你能同意,确实是非凡的。
从这个例子中可以发现,如果使用分层,就达不到这样的效果。只有继承才能访问保护成员,只有继承才使得虚函数可以重新被定义。(虚函数的存在会引发私有继承的使用,例子参见条款43)因为存在虚函数和保护成员,有时私有继承是表达类之间 "用...来实现" 关系的唯一有效途径。所以,当私有继承是你可以使用的最合适的实现方法时,就要大胆地使用它。同时,广泛意义上来说,分层是应该优先采用的技术,所以只要有可能,就要尽量使用它。
短歌如风
2003-04-29
打赏
举报
回复
《Effective C++》:
条款42: 明智地使用私有继承
条款35说明,C++将公有继承视为 "是一个" 的关系。它是通过这个例子来证实的:假如某个类层次结构中,Student类从Person类公有继承,为了使某个函数成功调用,编译器可以在必要时隐式地将Student转换为Person。这个例子很值得再看一遍,只是现在,公有继承换成了私有继承:
class Person { ... };
class Student: // 这一次我们
private Person { ... }; // 使用私有继承
void dance(const Person& p); // 每个人会跳舞
void study(const Student& s); // 只有学生才学习
Person p; // p是一个人
Student s; // s是一个学生
dance(p); // 正确, p是一个人
dance(s); // 错误!一个学生不是一个人
很显然,私有继承的含义不是 "是一个",那它的含义是什么呢?
"别忙!" 你说。"在弄清含义之前,让我们先看看行为。私有继承有那些行为特征呢?" 那好吧。关于私有继承的第一个规则正如你现在所看到的:和公有继承相反,如果两个类之间的继承关系为私有,编译器一般不会将派生类对象(如Student)转换成基类对象(如Person)。这就是上面的代码中为对象s调用dance会失败的原因。第二个规则是,从私有基类继承而来的成员都成为了派生类的私有成员,即使它们在基类中是保护或公有成员。行为特征就这些。
这为我们引出了私有继承的含义:私有继承意味着 "用...来实现"。如果使类D私有继承于类B,这样做是因为你想利用类B中已经存在的某些代码,而不是因为类型B的对象和类型D的对象之间有什么概念上的关系。因而,私有继承纯粹是一种实现技术。用条款36引入的术语来说,私有继承意味着只是继承实现,接口会被忽略。如果D私有继承于B,就是说D对象在实现中用到了B对象,仅此而已。私有继承在软件 "设计" 过程中毫无意义,只是在软件 "实现" 时才有用。
私有继承意味着 "用...来实现" 这一事实会给程序员带来一点混淆,因为条款40指出,"分层" 也具有相同的含义。怎么在二者之间进行选择呢?答案很简单:尽可能地使用分层,必须时才使用私有继承。什么时候必须呢?这往往是指有保护成员和/或虚函数介入的时候 ---- 但这个问题过一会儿再深入讨论。
条款41提供了一种方法来写一个Stack 模板,此模板生成的类保存不同类型的对象。你应该熟悉一下那个条款。模板是C++最有用的组成部分之一,但一旦开始经常性地使用它,你会发现,如果实例化一个模板一百次,你就可能实例化了那个模板的代码一百次。例如Stack模板,构成Stack<int>成员函数的代码和构成Stack<double>成员函数的代码是完全分开的。有时这是不可避免的,但即使模板函数实际上可以共享代码,这种代码重复还是可能存在。这种目标代码体积的增加有一个名字:模板导致的 "代码膨胀"。这不是件好事。
对于某些类,可以采用通用指针来避免它。采用这种方法的类存储的是指针,而不是对象,实现起来就是:
· 创建一个类,它存储的是对象的void*指针。
· 创建另外一组类,其唯一目的是用来保证类型安全。这些类都借助第一步中的通用类来完成实际工作。
下面的例子使用了条款41中的非模板Stack类,不同的是这里存储的是通用指针,而不是对象:
class GenericStack {
public:
GenericStack();
~GenericStack();
void push(void *object);
void * pop();
bool empty() const;
private:
struct StackNode {
void *data; // 节点数据
StackNode *next; // 下一节点
StackNode(void *newData, StackNode *nextNode)
: data(newData), next(nextNode) {}
};
StackNode *top; // 栈顶
GenericStack(const GenericStack& rhs); // 防止拷贝和
GenericStack& // 赋值(参见
operator=(const GenericStack& rhs); // 条款27)
};
因为这个类存储的是指针而不是对象,就有可能出现一个对象被多个堆栈指向的情况(即,被压入到多个堆栈)。所以极其重要的一点是,pop和类的析构函数销毁任何StackNode对象时,都不能删除data指针 ---- 虽然还是得要删除StackNode对象本身。毕竟,StackNode 对象是在GenericStack类内部分配的,所以还是得在类的内部释放。所以,条款41中Stack类的实现几乎完全满足the GenericStack的要求。仅有的改变只是用void*来替换T。
仅仅有GenericStack这一个类是没有什么用处的,但很多人会很容易误用它。例如,对于一个用来保存int的堆栈,一个用户会错误地将一个指向Cat对象的指针压入到这个堆栈中,但编译却会通过,因为对void*参数来说,指针就是指针。
为了重新获得你所习惯的类型安全,就要为GenericStack创建接口类(interface class),象这样:
class IntStack { // int接口类
public:
void push(int *intPtr) { s.push(intPtr); }
int * pop() { return static_cast<int*>(s.pop()); }
bool empty() const { return s.empty(); }
private:
GenericStack s; // 实现
};
class CatStack { // cat接口类
public:
void push(Cat *catPtr) { s.push(catPtr); }
Cat * pop() { return static_cast<Cat*>(s.pop()); }
bool empty() const { return s.empty(); }
private:
GenericStack s; // 实现
};
正如所看到的,IntStack和CatStack只是适用于特定类型。只有int指针可以被压入或弹出IntStack,只有Cat指针可以被压入或弹出CatStack。IntStack和CatStack都通过GenericStack类来实现,这种关系是通过分层(参见条款40)来体现的,IntStack和CatStack将共享GenericStack中真正实现它们行为的函数代码。另外,IntStack和CatStack所有成员函数是(隐式)内联函数,这意味着使用这些接口类所带来的开销几乎是零。
OpenStack 云计算管理平台(凭借强大的可扩展性,为组建企业级
私有
云和
公有
云提供助力)
OpenStack是一个开源的云平台管理项目,可以用于构建
公有
云或
私有
云平台,并且通过DASHBOARD可控制计算、存储、网络等资源池,同时它覆盖了网络、虚拟化、操作系统、服务器等各个方面,本课程主要讲解OpenStack的...
公有
继承
/
私有
继承
/保护
继承
的区别
1.
公有
继承
–public
公有
继承
时,对基
类
的
公有
成员和保护成员的访问属性不变,派生
类
的新增成员可以访问基
类
的
公有
成员和保护成员,但是访问不了基
类
的
私有
成员。派生
类
的对象只能访问派生
类
的
公有
成员(包括
继承
的
公有
成员),访问不了保护成员和
私有
成员。 2.保护
继承
–protected 保护
继承
中,基
类
的
公有
成员和保护成员被派生
类
继承
后变成保护成员,派生
类
的新增成员可以访问基
类
的
公有
成员和保护成员,但是访问不了基
类
的
私有
成员。派生
类
的对象不能访问派生
类
继承
基
类
的
公有
成员,保护成员和
私有
成员,派生
类
的对象只能访问
公有
继承
,
私有
继承
,保护
继承
一.
公有
继承
1.在声明一个派生
类
时将基
类
的
继承
方式指定为public,称为
公有
继承
。 2.用
公有
继承
方式建立的派生
类
称为
公有
派生
类
,其基
类
称为
公有
基
类
例,
公有
继承
的测试 #include<iostream> using namespace std ; class A { public : void get_XY( ) { cout << "Enter two numbers of x, y : " ; cin >> x
c++中的
公有
继承
、
私有
继承
和保护
继承
对于
公有
继承
,在派生
类
中,基
类
的
公有
成员、
私有
成员和保护成员的访问控制属性不变。且派生
类
的对象只能访问派生
类
和基
类
继承
过来的
公有
成员。 对于
私有
继承
,在派生
类
中,基
类
的
公有
成员、
私有
成员和保护成员的访问控制属性均变为
私有
。在派生
类
的函数中,可以访问基
类
的
公有
成员和保护成员。但无法通过派生
类
对象访问从基
类
继承
过来的成员。(即基
类
中的所有成员,派生
类
对象都不能访问) 对于保护
继承
,在派生
类
中,基
类
的
公有
成员和保护成员的访问控制属性将变为保护的,
私有
成员的访问控制属性仍为
私有
的。派生
类
内部仍可访问基
类
的
公有
成员和
C++ 访问控制——
公有
继承
、
私有
继承
、保护
继承
派生
类
继承
了基
类
的全部数据成员和除了构造函数和析构函数之外的全部函数成员,但是这些成员的访问属性在派生的过程中是可以调整的。从基
类
继承
的成员,其访问属性由
继承
方式控制。基
类
的成员有public(
公有
)、protected(保护)和private(
私有
)三种访问属性。基
类
的自身成员可以对基
类
中任何一个其他成员进行访问,但是通过基
类
的对象,只能访问该
类
的
公有
成员。
类
的
继承
方式有public(
公有
继承
)、protected(保护
继承
)和private(
私有
继承
)三种。
C++ 语言
64,646
社区成员
250,476
社区内容
发帖
与我相关
我的任务
C++ 语言
C++ 语言相关问题讨论,技术干货分享,前沿动态等
复制链接
扫一扫
分享
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++
技术论坛(原bbs)
社区管理员
加入社区
获取链接或二维码
近7日
近30日
至今
加载中
查看更多榜单
社区公告
请不要发布与C++技术无关的贴子
请不要发布与技术无关的招聘、广告的帖子
请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下
试试用AI创作助手写篇文章吧
+ 用AI写文章