什么是继承?
继承是C++语言的一种重要机制,该机制自动地为一个类提供来自另一个类的操作和数据结构,这使得程序员只需在新类中定义已有类中没有的成分来建立新类。
* 继承使得我们得以用一种简单的方式来描述事物
* 面向对象程序设计可以让你声明一个新类作为另一个类的派生。
* 派生类/子类继承它父类的属性和操作。
* 子类同时也声明了新的属性和新的操作,剔除了那些不适合于其用途的继承下来的操作。
继承的工作方式
* 实现一个简单的继承
class Person
{
public:
void Display()
{
cout << "LuLaLaLuLaLa" << endl;
}
protected:
string _name;
};
class Student:public Person
{
protected:
int _num;
};
代码实现如图:
事实上,继承的内存布局如下所示:
继承与转换
- 子类对象可以赋值给父类对象(切割/切片)。
- 父类对象不能赋值给子类对象。
- 父类的指针/引用可以指向子类对象。
- 子类的指针/引用不能指向父类对象(可以通过强制类型转换完成)。
派生类的构造
在上述简单实现继承的代码中,可以看出我们并没有声明派生类Student的构造函数,根据类的实现机制,派生类对象创建时,将执行其默认的构造函数。
该默认构造函数首先会调用基类的默认构造函数,而如果基类没有默认构造函数的话,但正好匹配默认参数的构造函数,则会调用该构造函数。
- 派生类可以直接访问基类的保护数据成员,甚至在构造时初始化他们,但一般并不这样做。
- 派生类会通过基类的接口(成员函数)去访问他们,初始化也是通过基类的构造函数。
- 这样的好处有:一旦基类的实现有错误,只要不涉及接口,那么基类的修改不会影响派生类的操作。
- 类与类之间,你做你的,我做我的,以接口作沟通。
单继承 & 多继承
单继承:一个子类只有一个直接父类时称这个继承关系为单继承
多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承
菱形继承
- 菱形继承内存布局示意图如下所示:
- 由上图可知,在Assistant中有两份Person成员,故产生了二义性的问题。
- 同时,二义性也称数据冗余。
为了解决二义性和数据冗余的问题,C++引入了虚继承。
虚继承
在被继承的类前加一个virtual关键字即可。
代码示例:
class Person
{
public:
Person()
{
cout << "Person()" << endl;
}
~Person()
{
cout << "~Person()" << endl;
}
public:
string _name;
};
class Student :virtual public Person
{
public:
Student()
{
cout << "Student()" << endl;
}
~Student()
{
cout << "~Student()" << endl;
}
public:
int _id;
};
class Teacher : virtual public Person
{
public:
Teacher()
{
cout << "Teacher()" << endl;
}
~Teacher()
{
cout << "~Teacher()" << endl;
}
public:
int _num;
};
class Assistant : public Student, public Teacher
{
public:
Assistant()
{
cout << "Assistant()" << endl;
}
~Assistant()
{
cout << "~Assistant()" << endl;
}
protected:
string _ClassName;
};
void Test()
{
Assistant a;
a._name = "Evey";
}
运行结果如下所示:
值得注意的是,若没有使用虚继承,还可以制定类进行调用,示例如下:
void Test()
{
Assistant a;
a.Student::_name = "Evey";
}
运行结果如下所示:
观察以上两种方法各自的运行结果图可知,虚继承的效率更高,能更好的解决数据冗余的问题。
综上所述,在解决菱形继承的二义性和数据冗余性问题时,虚继承是最优的解决方法。