公有继承(public)
- 基类的public和protected成员的访问属性在派生类中保持不变,但基类的private成员不可直接访问;
- 派生类中的成员函数可以直接访问基类中的public和protected成员,但是不能直接访问基类的private成员;
- 通过派生类的对象只能访问基类的public成员。
私有继承(private)
- 基类的public和protected成员都以private身份出现在派生类中,但基类的private成员不可以直接访问;
- 派生类中的成员函数可以直接访问基类中的public和protected成员,但是不能直接访问基类的private成员;
- 通过派生类的对象不能访问基类的任何成员。
保护继承(protected)
- 基类的public和protected成员都以protected身份出现在派生类中,但基类的private成员不可以直接访问;
- 派生类中的成员函数可以直接访问基类中的public和protected成员,但是不能直接访问基类的private成员;
- 通过派生类的对象不能访问基类的任何成员。
总结:
- 由上可见,三种继承方式的区别在于对于基类的成员在继承类中的访问属性的不同改变方式。public继承方式,基类成员在派生类中保持不变,private和protected方式将public和protected基类成员分别改变为private和protected访问属性;
- 三种方式下继承类中都不能直接访问基类的private成员;
- public方式下,派生类对象只能访问基类的public成员(因为public成员访问属性没有改变),private和protected方式派生类对象不能访问基类任何成员(因为基类中的public和protected成员都被改成private或者protected访问属性,都不能被实例化的对象直接访问,原因下述)。
protected成员的特点和作用
- 对建立其所在类对象的模块来说,他与private成员的性质相同(都不能被对象访问);
- 对于其派生类来说,他的性质又与public成员性质相同(在派生类中使用时和public一样可用);
- 即实现了数据隐藏,又方便继承,实现代码重用;
类型兼容原则:
一个公有派生类对象在使用上可以被当作基类的对象,反之则禁止,具体表现在:
- 派生类的对象可以隐含转换为基类对象;
- 派生类的对象可以初始化基类的引用;
- 派生类的指针可以隐含转换为基类的指针;
通过基类对象名、指针只能使用从基类继承的成员。
例:
#include <iostream>
using namespace std;
class Base1 { //基类Base1定义
public:
void display() const {
cout << "Base1::display()" << endl;
}
};
class Base2: public Base1 { //公有派生类Base2定义
public:
void display() const {
cout << "Base2::display()" << endl;
}
};
class Derived: public Base2 { //公有派生类Derived定义
public:
void display() const {
cout << "Derived::display()" << endl;
}
};
void fun(Base1 *ptr) { //参数为指向基类对象的指针
ptr->display(); //"对象指针->成员名"
}
int main() { //主函数
Base1 base1; //声明Base1类对象
Base2 base2; //声明Base2类对象
Derived derived; //声明Derived类对象
//用Base1对象的指针调用fun函数
fun(&base1);
//用Base2对象的指针调用fun函数
fun(&base2);
//用Derived对象的指针调用fun函数
fun(&derived);
return 0;
}
运行结果:
Base1::display()
Base1::display()
Base1::display()
拷贝构造函数
若建立派生类对象时没有编写拷贝构造函数,编译器会自动生成一个隐含的拷贝构造函数,该函数先调用基类的拷贝构造函数,再为派生类新增的成员对象执行拷贝;
若编写派生类的拷贝构造函数,则需要为基类相应的拷贝构造函数传递参数,例如:
C::C(C &c1):B(c1){...}