C++中的继承
一、继承的概念及定义
1.概念
继承是提高代码复用性的一种重要手段。它可以在原有类的基础上进行扩展,通过这种方式产生的类称为派生类或子类。
2.定义
1. 格式
class Student:public Person
{
public:
int _stuid;
int _major;
};
其中Person类称之为父类或者基类,Student类称为 子类或者 派生类。
2.继承关系和访问限定符
继承方式有三种:公有public,私有private,保护protected。
其中访问关系如图所示:
从图中可以看出,基类的私有成员,在子类都是不可访问的。
同时,我们可以看出protected就是因为继承出现的。
二、基类和派生类中的对象模型
1. 继承对象
派生类会继承基类中的 所有对象,就算是private类型的,也只是被隐藏了,无法访问而已!
2.赋值转换
- 派生类对象 可以赋值给 基类的对象/指针/引用 。(后面的多态就用到这一点)
- 基类对象不能赋值给派生类对象。
下面举个简单例子:
class Person
{
};
class Student:public Person
{
};
Student s;
Person* p=s;//子类对象赋值给父类指针
三、继承中的作用域
1.基类与父类作用域
继承中基类和派生类都有 独立 的作用域。
当子类和父类有同名成员时,子类将屏蔽父类对同名成员的直接访问,称为 隐藏 也叫重定义。
注意 :如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
2.访问方式
访问子类同名成员,直接访问即可。
访问父类同名成员,需要加作用域。
class Base
{
public:
Base()
{
_A = 100;
}
public:
int _A;
};
class Son:public Base
{
public:
Son()
{
_A = 200;
}
public:
int _A;
};
void test()
{
Son s1;
cout << s1._A << endl;//200
cout << s1.Base::_A << endl;//使用作用域符号来访问 100
}
3. 静态成员
对于静态成员的访问,与普通成员相同,但不要忘记: static成员类内声明,类外初始化。
四 、继承中构造和析构的顺序
调用顺序:父类构造->子类构造->子类析构->父类析构。
这里我们不做讲解,大家可自行验证。
class Base
{
public:
Base()
{
cout << "Base构造函数" << endl;
}
~Base()
{
cout << "Base析构函数" << endl;
}
};
class Son:public Base
{
public:
Son()
{
cout << "Son构造函数" << endl;
}
~Son()
{
cout << "Son析构函数" << endl;
}
};
void test()
{
Son s;
}
int main()
{
test();
return 0;
}
五、复杂的菱形继承
1. 分类
单继承 :一个子类只有一个直接父类继承
class Student:public Person
多继承 :一个子类有两个或以上直接父类的继承
class Professor:public Person,public Teacher
2.菱形继承
菱形继承是多继承的一种方式
可以看出,菱形继承存在 二义性和冗余性 的问题。
因此,实际使用中,应避免出现。
六、继承的总结
1.继承和组合
- 继承:一种is-a的关系,即 每个派生类对象都相当于是一个基类对象。
特点: 代码复用,层次结构,可扩展性
- 组合:一种has-a的关系,即 将对象作为成员包含在另一个类中的关系。
特点: 灵活性,代码复用,独立性。
2.白箱复用
白箱复用是指通过继承来实现代码复用。在白箱复用中,子类继承父类的实现细节,可以直接访问和修改父类的内部实现。这种方式被称为白箱复用,因为子类对父类的内部结构是可见的。继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响。基类与派生类之间耦合度很高。
3. 黑箱复用
黑箱复用是指通过组合来实现代码复用。在黑箱复用中,一个类包含另一个类的实例,并通过该实例来实现功能。这种方式被称为黑箱复用,因为类对包含的对象的内部实现是不可见的,只能通过公开的接口进行交互。
实际使用中,尽量多使用组合,组合的耦合度低,代码维护性好。不过继承也有它独特的优势,比如:要实现多态,那么就必须用继承!