C++中有四大特性:继承,封装,多态,抽象。继承是面向对象设计中一种重要的复用的手段,所谓继承,就是在一个已存在的类的基础上建立一个新的类,一个新的类从已有的类那里获得其已有特性,这种现象称为类的继承。通过继承,一个新建子类从已有的父类那里获得父类的特性。从另一个的角度说,从已有的类产生一个新的子类,称为类的派生。
★继承与派生
下面是一个简单的继承与派生的关系:
<pre name="code" class="cpp">#include<iostream>
using namespace std;
class Base
{
public:
Base()
{
cout << "Base()" << endl;
}
~Base()
{
cout << "~Base()" << endl;
}
void ShowBase()
{
cout << "_pub=" << _pub << endl;
cout << "_pro=" << _pro << endl;
cout << "_pri=" << _pri << endl;
}
public:
int _pub;
protected:
int _pro;
private:
int _pri;
};
class Derived :public Base
{
public:
Derived()
{
cout << "Derived()" << endl;
}
~Derived()
{
cout << "~Derived" << endl;
}
void ShowDerived()
{
cout << "_dpub=" << _pub << endl;
cout << "_dpro" << _dpro << endl;
cout << "_dpri" << _dpri <<endl;
}
public:
int _dpub;
protected:
int _dpro;
private:
int _dpri;
};
int main()
{
Base b;
Derived d;
return 0;
}
此处类的继承方式是公有继承。
在c++中,一共有三种继承方式:public,protect,private
我们可以通过改变上面程序的继承方式来探索三种继承方式的区别。
基类在派生类访问属性的变化如下:
表1 基类成员在派生类中的访问属性
在公用派生中,基类的私有成员是不可访问的,我们可以通过基类的公有成员函数来引用基类的私有数据成员。
私有基类的公用成员和保护成员在派生类中的访问属性相当于派生类中的私有成员,即派生类的成员函数能访问它们,而在派生类外不能访问,私有基类的私有成员在派生类中成为不可访问的成员,只有基类的成员函数可以引用它们。
保护继承中,保护基类的公有成员和保护成员在派生类中都成了保护成员,其私有成员仍为基类私有,不可访问。保护继承在类外的访问属性与私有继承的一样,均不能在类外访问。
从表1可知,基类的私有成员被派生类继承后变为不可访问的成员(无论是哪种继承方式)。若想要在派生类中引用基类的某些成员,应该将这些基类的成员声明为protected。
表2 基类成员与派生类成员的访问属性
★派生类的构造函数与析构函数
基类中的构造函数与析构函数是不能被继承的,所以在设计派生类的构造函数时,不仅要考虑派生类所增加的数据成员的初始化,还应考虑基类的数据成员的初始化,所以在执行派生类的构造函数时,调用基类的构造函数。
♧在以下几种情况时,编译器会自动合成构造函数:
1.基类有缺省(全缺省或无参)构造函数,派生类没有显式的声明构造函数;
<pre name="code" class="cpp">#include<iostream>
using namespace std;
class Base
{
public:
Base(int pub=0,int pro=0,int pri=0)
:_pub(pub)
, _pro(pro)
, _pri(pri)
{
cout << "Base()" << endl;
}
~Base()
{
cout << "~Base()" << endl;
}
void ShowBase()
{
cout << "_pub=" << _pub << endl;
cout << "_pro=" << _pro << endl;
cout << "_pri=" << _pri << endl;
}
public:
int _pub;
protected:
int _pro;
private:
int _pri;
};
class Derived :public Base
{
public:
/*Derived(int dpub=0,int dpro=0,int dpri=0)
:Base()
,_dpub(dpub)
, _dpro(dpro)
, _dpri(dpri)*/
/*{
cout << "Derived()" << endl;
}*/
~Derived()
{
cout << "~Derived" << endl;
}
void ShowDerived()
{
cout << "_dpub=" << _pub << endl;
cout << "_dpro" << _dpro << endl;
cout << "_dpri" << _dpri <<endl;
}
public:
int _dpub;
protected:
int _dpro;
private:
int _dpri;
};
int main()
{
Base b;
Derived d;
return 0;
}
查看汇编如下:
此时编译器自动合成了构造函数,在调试过程中,查看运行窗口我们也会发现调用了两次Base()。第一个Base()是基类构建对象调用的,第二个基类是派生类构建对象调用的。
2.派生类中定义了一个类类型,类类型中有一个缺省的构造函数。
<pre name="code" class="cpp">#include<iostream>
using namespace std;
class Base
{
public:
~Base()
{
cout << "~Base()" << endl;
}
void ShowBase()
{
cout << "_pub=" << _pub << endl;
cout << "_pro=" << _pro << endl;
cout << "_pri=" << _pri << endl;
}
public:
int _pub;
protected:
int _pro;
private:
int _pri;
};
class Time
{
public:
Time()
{
}
private:
int _time;
};
class Derived :public Base
{
public:
~Derived()
{
cout << "~Derived" << endl;
}
void ShowDerived()
{
cout << "_dpub=" << _pub << endl;
cout << "_dpro" << _dpro << endl;
cout << "_dpri" << _dpri <<endl;
}
public:
int _dpub;
protected:
int _dpro;
private:
int _dpri;
Time t;
};
int main()
{
Base b;
Derived d;
return 0;
}
查看汇编如下:
3.虚继承
class Derived :virtual public Base
.
4.虚函数
<pre name="code" class="cpp">#include<iostream>
using namespace std;
class Base
{
public:
void virtual ShowBase()
{
cout << "_pub=" << _pub << endl;
cout << "_pro=" << _pro << endl;
cout << "_pri=" << _pri << endl;
}
public:
int _pub;
protected:
int _pro;
private:
int _pri;
};
class Derived :virtual public Base
{
public:
void virtual ShowBase()
{
cout << "_dpub=" << _pub << endl;
cout << "_dpro" << _dpro << endl;
cout << "_dpri" << _dpri <<endl;
}
public:
int _dpub;
protected:
int _dpro;
private:
int _dpri;
};
查看汇编如下:
♧调用构造函数的顺序:
基类的构造函数->派生类子对象的构造函数->派生类的构造函数
♧调用析构函数的顺序:
派生类的析构函数->派生类子对象的析构函数->基类的析构函数
<pre name="code" class="cpp">#include<iostream>
using namespace std;
class Base
{
public:
Base(int pub=0,int pro=0,int pri=0)
:_pub(pub)
, _pro(pro)
, _pri(pri)
{
cout << "基类Base()" << endl;
}
~Base()
{
cout << "基类~Base()" << endl;
}
void virtual ShowBase()
{
cout << "_pub=" << _pub << endl;
cout << "_pro=" << _pro << endl;
cout << "_pri=" << _pri << endl;
}
public:
int _pub;
protected:
int _pro;
private:
int _pri;
};
class Time
{
public:
Time()
{
cout << "子对象Time()" << endl;
}
~Time()
{
cout << "子对象~Time()" << endl;
}
private:
int _time;
};
class Derived :virtual public Base
{
public:
Derived(int dpub=0,int dpro=0,int dpri=0)
:Base()
,_dpub(dpub)
, _dpro(dpro)
, _dpri(dpri)
{
cout << "派生类Derived()" << endl;
}
~Derived()
{
cout << "派生类~Derived" << endl;
}
void ShowBase()
{
cout << "_dpub=" << _pub << endl;
cout << "_dpro" << _dpro << endl;
cout << "_dpri" << _dpri <<endl;
}
public:
int _dpub;
protected:
int _dpro;
private:
int _dpri;
Time t;
};
int main()
{
Derived d;
return 0;
}
调试结果:
总结:
1.基类没有定义构造函数,派生类也可以不用定义构造函数,全部使用缺省构造函数;
2.基类有缺省构造函数(全缺省或无参),派生类可以定义构造函数,也可以不定义构造函数;
2.基类没有缺省构造函数(带有形参列表),派生类必须定义构造函数,在初始化列表中显式给出基类名和参数列表(传参)。
★继承体系中的作用域
1.在继承体系中,基类与派生类是两个不同的作用域;
2.基类和派生类中若有同名函数成员,则默认为派生类的函数成员,若想要访问基类的同名成员,可以通过基类名::函数名的方式访问;
3.在继承体系中,最好不要定义同名成员。
★继承与转换——赋值兼容规则(public)
1.子类可以给父类赋值;
2.父类不可以给子类赋值;
3.父类的指针/引用可以指向子类对象;
4.子类的指针/引用不可以指向父类对象。
从上图中,我们可以大致的看到基类与派生类之间的关系,子类的范围是大于父类的,其包括父类的范围,所以我们可以用子类给父类赋值,而用父类给子类时,有些数据成员父类是没有的,无法完成赋值的工作。而进行指针或引用时,Basel类型的指针可访问的范围是小于Derived指针可访问的范围的,用一个Base类型的指针去访问Derived的范围是没有问题的,而用一个Derived的指针去访问Base的范围,就有可能越界了,去访问一块不属于自己的空间,发生错误。
Base b;
Derived d;
Base*p=&d;
Derived *q=&b;
★友元与继承
友元关系不能继承,即基类友元不能访问派生类的私有和保护成员
★继承与静态成员
基类定义了一个静态成员,则无论派生出多少个子类,都只有这一个静态成员实例。
★多重继承
一个子类有多个父类时称这个继承为多重继承。
多重继承可能会产生二义性与冗余性,使程序变得复杂,比如派生类的两个基类都由一个基类派生而来(也就是菱形继承),这时,基类的数据成员派生类继承了两次,当进行访问时会产生二义性,不知道是是访问哪一个派生类中的数据成员。
★虚继承
虚继承是用来解决多重继承中二义性的问题的。在声明派生类时,给基类前加上一个virtual,就形成了虚继承,此时相同的数据成员只继承一次。