继承
面向对象编程的四大基础特性分别是:抽象,封装,继承,多态
-
抽象:将多个对象共同属性和行为抽离出来,形成一个类
-
封装:申明类时,可以使用publicl,protected、private等访问权限修饰符去修饰数据成员,例如,当某个成员被声明为private时,那么实例化出的对象就不能直接访问这个数据成员,只能通过提供的共有接口去间接访问,体现封装性
-
继承:在类的基础还可以进行抽象,将多个类共同的属性和行为抽离,形成更高层次的类[基类],通过继承,我们可以用原有类型去定义一个新的类,定义的新的类型既包括了原有类型的成员,也能新增自己的成员,原有类型成为"基类",再此基础上建立的类称为:“派生类”
继承的定义:
当一个派生类继承一个基类时,需要在派生类的类派生列表中明确指明他是从哪个基类继承而来的。声明规则如下:
class 派生类: public/protected/private 基类
{
//...
};
派生类的生成包括三个步骤:
-
吸收基类的成员
-
改造基类的成员
-
新增自己的成员
-
class Point3D :public Point { public: Point3D(int x, int y, int z) :Point(x,y) ,_z(z) { cout << "Point3D(int,int,int)" << endl; } void display() const { print(); cout << _z << endl; } private: int _z; };
继承的局限性
在继承的过程中,一些成员是不能被继承的,如下:
- 构造函数
- 析构函数
- 基类中重载的 operator new/delete函数不能被继承
- 基类中重载的 operator =(const A& rhs),赋值运算符函数不能被继承
- 友元关系不能继承
派生方式对基类成员的访问权限
note:派生方式实际上限定了派生类对基类成员的最高访问权限。如:基类的data成员的访问权限是public,通过protect方式派生,data成员在派生类中的访问权限是protected,所以就算基类成员的访问权限比派生方式要高,在派生类中会自动降级。
如图:
结论:
-
不管是什么继承方式(public/protected/private),基类中的私有成员都不能再派生类的类体中访问==【封装性】==
-
不管是什么继承方式(public/protected/private),基类的非私有成员都可以在派生类的类体中访问。
-
对于派生类对象而言,只能访问公有继承中的公有成员,其他成员都不能直接访问
-
默认的继承方式是私有继承
#include <iostream>
using std::cout;
using std::endl;
class Point
{
public:
Point(int ix = 0, int iy = 0)
: _ix(ix)
, _iy(iy)
{
cout << "Point(int = 0, int = 0)" << endl;
}
void print() const
{
cout << "(" << _ix << ", "
<< _iy << ")" << endl;
}
int getY() const
{
return _iy;
}
~Point()
{
cout << "~Point()" << endl;
}
protected:
int _ix;
private:
int _iy;
};
class Point3D
: public Point
{
public:
Point3D(int ix = 0, int iy = 0, int iz = 0)
: Point(ix, iy)//借助基类的构造函数将吸收过来的数据成员初始化
, _iz(iz)
{
cout << "Point3D(int = 0, int = 0, int = 0)" << endl;
}
void show()
{
print();
cout << "(" << _ix //protected
/* << ", " << _iy//私有的,体现封装性 */
<< ", " << getY()//public
<< ", " << _iz //private
<< " )" << endl;
}
private:
int _iz;
};
//继承(吸收)与直接访问是两码事
void test()
{
cout << "sizeof(Point) = " << sizeof(Point) << endl;
cout << "sizeof(Point3D) = " << sizeof(Point3D) << endl;
Point3D pt3d(1, 2, 3);
/* pt3d._ix;//error */
BF'//error */
pt3d.getY();//ok
}
int main(int argc, char **argv)
{
test();
return 0;
}
派生类对象的构造
-
创建派生类对象时,会调用派生类的构造函数,但是因为派生类继承基类,会吸收基类的数据成员,为了完成从基类吸收的数据成员的初始化,会借助基类的构造函数,在派生类构造函数的初始化成员列表中,默认调用基类的无参构造函数,再执行自己数据成员的初始化。
-
在派生类的初始化成员列表中默认调用基类的默认构造函数,若基类没有提供默认的构造函数,则报错,若希望借助基类的有参构造函数初始化吸收的数据成员,一定要在派生类的构造函数的初始化成员列表中显示的写出来!
-
若派生类含对象成员,引用成员,const成员,则必须在初始化成员列表中初始化。
派生类的构造顺序:
- 1.调用基类的构造函数
- 2.调用派生类对象成员的构造函数[没有,则不会调用]
- 3.调用派生类自身的构造函数
派生类的销毁:
- 1.调用派生类的析构函数
- 2.调用派生类对象成员的析构函数
- 3.调用基类的构造函数
#include <iostream>
using std::cout;
using std::endl;
class Test
{
public:
Test()
{
cout << "Test()" << endl;
}
~Test()
{
cout << "~Test()" << endl;
}
};
class Base
{
public:
Base()
: _base(0)
{
cout << "Base()" << endl;
}
Base(long base)
: _base(base)
{
cout << "Base(long)" << endl;
}
~Base()
{
cout << "~Base()" << endl;
}
private:
long _base;
};
class Derived
: public Base
{
public:
Derived()
:Base()
, _derived(0)
{
}
Derived(long derived)
: _derived(derived)
,_tst()
, Base(10)
{
cout << "Derived(long )" << endl;
}
~Derived()
{
cout << "~Derived()" << endl;
}
private:
long _derived;
Test _tst;
};
void test()
{
Derived d(10);
}
int main(int argc, char **argv)
{
test();
return 0;
}
//执行结果
Base(long)
Test()
Derived(long )
~Derived()
~Test()
~Base()