一. 引言
继承自动地为一个类提供来自另一个类的操作和数据结构,这使得程序员只需在新类中定义已有的类中没有的成分来建立一个新类,比设计新类要容易的多。
二. 什么是继承
当一个类被其他的类继承时,被继承的类称为基类,又称为父类。
继承其他类属性的类称为派生类,又称为子类。
三. 三种继承方式
- 公有继承
公有继承时,对基类的公有成员和保护成员的访问属性不变,派生类的新增成员可以访问基类的公有成员和保护成员,但是访问不了基类的私有成员。派生类的对象只能访问派生类的公有成员(包括继承的公有成员),访问不了保护成员和私有成员。
base.h
#include<iostream>
#include<string>
using namespace std;
class Student
{
private:
int age;
protected:
string name;
int grade;
public :
Student(string s, int g,int a)
{
cout << "Constuct Student" << endl;
name = s;
grade = g;
age = a;
}
void print()
{
cout << "Student:" << endl;
cout << "name=" << name << endl;
cout << "grade=" << grade<<endl;
cout << "age=" << age << endl;
}
};
class graStudent :public Student
{
public:
graStudent(string s, int g, int a) :Student(s, g, a) //调用基类的构造函数,构造基类
{
cout << "Constuct GraduateStudent" << endl;
}
/*
公有继承方式,会把基类的公有成员(变量和函数)继承到子类公有成员,保护成员
变成基类的保护成员,但是私有成员子类也一样不可以访问
*/
void print1()
{
cout << "graStudent:\n";
cout << "name= " << name << endl;
cout << "grade= " << grade << endl;
}
};
int main()
{
graStudent g("ytt", 80, 19);
g.print(); //子类可以直接访问基类公共成员成员
g.print1();
system("pause");
}
注:
- 公有继承中,基类的私有成员,子类不可以访问。
- 公有继承中,基类的保护成员,子类可以继承为自己的保护成员。
- 公有继承中,基类的公有成员,子类可以继承为自己的公有成员。
- 私有继承
私有继承时,基类的公有成员和保护成员都被派生类继承下来之后变成私有成员,派生类的新增成员可以访问基类的公有成员和保护成员,但是访问不了基类的私有成员。派生类的对象不能访问派生类继承基类的公有成员,保护成员和私有成员
初始化基类组件
隐式的继承组件而不是成员对象将影响代码的编写,因为再也不能使用name来描述对象了。而必须使用用于公有继承的技术。例如,对于构造函数:
Student::Student(const char*str):name(str) {}
对于使用私有继承的构造函数则成了这个样子:
Student::Student(const char*str) : std::string(str) {
};
访问基类方法
使用私有继承时,只能在派生类的方法中使用基类的方法。私有继承使得能够使用类名和作用域解析符来调用基类的方法:
double Student::average() const
{
return Array::sum()/Array::size();
}
访问基类对象
使用作用域解析符可以访问基类的方法,但是如果要使用基类对象本省,就需要强制类型转换。由于Student类是从stirng派生而来,因此可以通过强制类型转换,将Student对象转换为string对象
const string & Student::Name() const
{
return (const string &)*this;
}
访问基类友元函数
使用类名显式的限定函数名不适合于友元函数,这是因为友元不是类的成员。然而我们可以显式的转换为基类来正确的调用。
ostream & operator<<(ostream &os ,const Student &stu)
{
os<<(const string &)stu<<endl;
}
应该使用包含还是继承
对于has-a的关系,我们既可以使用包含去实现,也可以使用私有继承实现,那么我们应该使用继承还是包含?
大多数人倾向于使用包含,因为这样代码层次清晰,耦合度比继承低,是一种良好的设计模式。并且,继承会引起许多的问题,尤其是从多个基类继承时,需要多次的转型操作。然而,私有继承提供的特性比包含多。另一种情况是私有继承需要重新定义虚拟函数。派生类可以重定义虚函数,但包含不可以。
eg:
#include <iostream>
#include <string>
using namespace std;
class base{
private:
int i;
public:
base(int x) :i(x){
cout << "base is called i = " << i << endl; }
void print(void)
{
cout << "i = " << i << endl;
}
};
//私有继承
class child1:private base
{
private:
int j;
public:
child1(int x):base(x),j(x){
cout << "j = " << j << endl;
}
void print(void){
base::print();
}
};
- 保护继承
保护继承中,基类的公有成员和保护成员被派生类继承后变成保护成员,派生类的新增成员可以访问基类的公有成员和保护成员,但是访问不了基类的私有成员。派生类的对象不能访问派生类继承基类的公有成员,保护成员和私有成员。
base.h
#include <iostream>
#include <string>
using namespace std;
class Base
{
public:
Base(int nId) {
mId = nId;}
int Id() {
mId++;cout<< mId<<endl;return mId;}
protected:
int Num() {
cout<< 0 <<endl;return 0;}
private:
int my_Id;
};
class Child : public Base
{
public:
Child() : Base(7) {
;}
int BId() {
return Id();} //新增成员可以访问公有成员
int BNum() {
return Num();} //新增成员可以访问保护成员
//无法访问基类的私有成员
protected:
int y;
private:
int x;
};
实现过程在这就不写了
base.cpp
int main()
{
Child child;
child.Id(); //派生类对象访问不了继承的公有成员,因为此时保护继承时GetId()已经为 protected类型
child.Num(); //这个同理
child.BId();
child.BNum();
return 0;
}
注:
- 在保护继承中,基类公有成员,子类中继承为自己的保护成员,在派生类可以访问,在外部不可访问
- 在保护继承中,基类保护成员,子类中继承为自己的保护成员,在派生类可以访问,在外部不可访问
- 在保护继承中,基类私有成员,子类不可访问基类的私有成员。
四. 总结
从上面的三点来看,私有继承和保护继承作用一样。
但还是有区别:如果派生类再一次去派生其它类时,对于刚才的私有继承来说,再派生的类将得不到任何成员。而对于刚才的保护继承,仍能够得到基类的公有和保护成员。
各种继承方式
特征 | 公有继承 | 保护继承 | 私有继承 |
---|---|---|---|
公有成员变成 | 派生类的公有成员 | 派生类的保护成员 | 派生类的私有成员 |
保护成员变成 | 派生类的保护成员 | 派生类的保护成员 | 派生类的私有成员 |
私有成员变成 | 只能通过基类接口访问 | 只能通过基类接口访问 | 只能通过基类接口访问 |
能否隐式向上转换 | 是 | 是(但只能在派生类中) | 否 |