继承是以一个类来定义另一个类,使创建和维护一个应用程序变得更容易。达到了代码重用和提高执行时间的效果。
派生类的构成:
1.吸收基类成员
2.改造基类成员
3.添加新的成员
分类
多重继承
定义三个类:人、士兵、步兵 :步兵->士兵->人,这样的关系称为多重继承
多继承
定义三个类:农民、工人、农民工 :农民工->农民,农民工->工人,这样的关系称为多继承
虚继承
类A是基类,类B和类C继承了类A,而类D既继承类B又继承类C(菱形继承关系)
当实例化D的对象时,每个D的实例化对象中都有了两份完全相同的A的数据。
可以通过虚基类(virtual base class)的方法,使得在继承间接共同基类时只保留一份成员。
public protected private
修饰类成员:
1.public公有成员。可以被外部访问。
2.protected保护成员。只能被内部或继承类访问。
3.private私有成员。只能被内部访问。
继承方式:
1.public继承。基类成员在子类中保持原有访问属性。
2.protected继承。基类中public成员在派生类中变为protected,基类中protected成员在派生类中变为protected,基类中private成员在派生类中还是private。
3.private继承。基类成员在子类中全部成为私有成员。
/*
1.当基类的指针P指向派生类对象时,只能操作派生类中从基类中继承过来的数据。
2.不允许指向派生类的指针指向基类,因为内存空间比基类长,会导致严重了后果。
*/
class a
{
public:
int aa;
};
class b:public a
{
public:
int bb;
}
从内存来看a:
|----int类型大小(aa变量)----|
|--------------------------|
而b则是
|--int数据大小(继承类a的aa变量)--|--int数据大小(bb变量)--|
|-----------------------------------------------------|
当定义一个基类类型指针时,这个指针指向的是a类型的数据
指向派生类时,因为p是a类型的指针,所以*p只解释为a类型数据的长度
|--int数据大小(继承类a的aa变量)--|--int数据大小(bb变量)--|
|---------p只指向此区域---------|
虚基类
虚基类的声明:class 派生类名 : virtual 继承方式 基类名
class A{}; //声明基类A
class B: virtual public A{}; //声明类B是类A的公用派生类,A是B的虚基类
class C: virtual public A{}; //声明类C是类A的公用派生类,A是C的虚基类
class D: public B, public C{}; //类D中只有一份A的数据
构造函数、析构函数
每个类有自己独有的构造函数和析构函数,子类无法继承父类的构造函数和析构函数。
构造函数和析构函数调用顺序:
构造函数:基类->派生类。
析构函数:派生类->基类。
当创建一个派生类的对象时,系统首先自动创建一个基类对象。也就是在调用派生类构造函数
创建派生类对象之前,系统首先调用基类的构造函数
创建基类对象。
当派生类对象生命期结束时,首先调用派生类的析构函数,然后调用基类的析构函数。
#include <iostream>
using namespace std;
int init(const std::string & info)
{
std::cout << info << std::endl;
return 0;
}
class A
{
int m_x;
public:
A():m_x(init("Init A::m_x"))
{
init("Call A::A()");
}
};
class A1 : public A
{
int m_x;
int m_y;
public:
A1(): m_y(init("Init A1::m_y")), m_x(init("Init A1::m_x"))
{
init("Call A1::A1()");
}
};
/*
打印出来的结果为:
Init A::m_x
Call A::A() //是因为隐式调用了基类的构造函数
//先调用基类构造函数
Init A1::m_x //按照成员变量的声明顺序,而非初始化列表指定的顺序来进行构造
Init A1::m_y
Call A1::A1()
//后调用子类构造函数
*/
/*
派生类隐式调用与显式调用基类的构造函数。
所谓隐式调用就是在派生类的构造函数中不指定对应的基类的构造函数,这时调用的是基类的默认构造函数(即含有默认参数值或不带参数的构造函数)。
所谓显式调用就是在派生类的构造函数中指定要调用的基类的构造函数,并将派生类构造函数的部分参数值传递给基类构造函数。
注:除非基类有默认的构造函数,否则必须采用显式调用方式。
*/
//隐式调用
#include <iostream>
using namespace std;
class A
{
public:
A(int x = 0, int y = 0)
{
a = x;
b = y;
}
private:
int a;
int b;
};
//基类A有默认的构造函数,可以隐式调用
class B:public A
{
public:
B(int z = 0)
{
c = z;
}
private:
int c;
};
int main()
{
B b1;
return 0;
}
//显示调用
#include <iostream>
using namespace std;
class A
{
public:
A(int x,int y)
{
a = x;
b = y;
}
private:
int a;
int b;
};
class B:public A
{
public:
//基类A没有默认的构造函数,其现有的构造函数需要传递参数,通过派生类构造函数调用A构造函数时必须如下显式调用
B(int x,int y,int z):A(x,y)
{
c = z;
}
private:
int c;
};
int main()
{
B b1(1,2,3); //显式调用基类构造函数
return 0;
}