在C++编程中软件可重用性(software reusability)是通过继承(inheritance)机制来实现的。类的继承,是新的类从已有类那里得到已有的特性。或从已有类产生新类的过程就是类的派生。原有的类称为基类或父类,产生的新类称为派生类或子类。
派生类的声明:
class 派生类名:[继承方式] 基类名
{
派生类成员声明;
};
一个派生类可以同时有多个基类,这种情况称为多重继承,派生类只有一个基类,称为单继承。
继承方式规定了如何访问基类继承的成员。继承方式有public, private, protected。继承方式指定了派生类成员以及类外对象对于从基类继承来的成员的访问权限。继承方式如下:
A、公有继承
基类的公有成员和保护成员在派生类中保持原有访问属性,其私有成员仍为基类的私有成员。
B、私有继承
基类的公有成员和保护成员在派生类中成了私有成员,其私有成员仍为基类的私有成员。
C、保护继承
基类的公有成员和保护成员在派生类中成了保护成员,其私有成员仍为基类的私有成员。
#include using namespace std;class Base{public: int pub;protected: int pro;private: int pri;};class Drive:public Base{public: void func() { pub = 10; pro = 100;//pro是保护成员,在派生类中可见,相当于私有 // pri = 1000;//pri是基类的私有成员,在派生类中不可见 }};//int main(){ Base b; b.pub = 10; // b.pro = 100;//pro是保护成员,在外部不可见 // b.pri = 1000;//pri是私有成员在外部不可见 return 0;}
派生类中的成员,包含两大部分,一类是从基类继承过来的,一类是自己增加的成员。从基类继承过过来的表现其共性,而新增的成员体现了其个性。
650) this.width=650;" src="http://s1.51cto.com/wyfs02/M00/86/36/wKioL1e4Qk-QWDHmAAFquDtNWFM626.png" title="图片1.png" alt="wKioL1e4Qk-QWDHmAAFquDtNWFM626.png" />
派生类中由基类继承而来的成员的初始化工作还是由基类的构造函数完成,然后派生类中新增的成员在派生类的构造函数中初始化。所以派生类没有继承基类的构造函数和析构函数。
派生类的构造函数语法
派生类名::派生类名(参数总表)
:基类名(参数表),内嵌子对象(参数表)
{
派生类新增成员的初始化语句; //也可出现地参数列表中
}
Graduate::Graduate(string sn, int in, char cs, float fs)
:Student(sn,in,cs),salary(fs)
{ }
构造函数的初始化顺序并不以上面的顺序进行,而是根据声明的顺序初始化。如果基类中没有默认构造函数(无参),那么在派生类的构造函数中必须显示调用基类构造函数,以初始化基类成员。
u 派生类构造函数执行的次序:
基类-->成员-->子类
A、调用基类构造函数,调用顺序按照它们被继承时声明的顺序(从左到右);
B、调用内嵌成员对象的构造函数,调用顺序按照它们在类中声明的顺序;
C、派生类的构造函数体中的内容。
祖父类student.hclass Student{public: Student(string sn,int n,char s); ~Student(); void dis();private: string name; int num; char sex;};student.cppStudent::Student(string sn, int n, char s):name(sn),num(n),sex(s){ }Student::~Student(){ }void Student:: dis(){ cout<
子类构造函数中,要么显示的调用父类的构造函数(传参),要么隐式的调用。发生隐式调用时,父类要有无参构造函数或是可以包含无参构造函数的默认参数函数。子类对象亦然。
定义
派生类::派生类(const 派生类& another)
:基类(another),派生类新成员(another.新成员)
{ }
父类student.hclass Student{public: Student(string sn,int n,char s); Student(const Student & another); ~Student(); void dis();private: string name; int num; char sex;};student.cppStudent::Student(string sn, int n, char s):name(sn),num(n),sex(s){ }Student::~Student(){ }void Student:: dis(){ cout<
派生类中的默认拷贝构造器会调用父类中默认或自实现拷贝构造器, 若派生类中自实现拷贝构造器,则必须显示的调用父类的拷贝构造器。
定义
子类& 子类::operator=(const 子类& another)
{
if(this == &another)
return *this; //防止自赋值
父类::operator =(another); // 调用父类的赋值运算符重载
this->salary = another.salary;//子类成员初始化
return * this;
}
基类student.hStudent & operator=(const Student & another);student.cppStudent & Student::operator=(const Student & another){ this->name = another.name; this->num = another.num; this->sex = another.sex; return * this;} 派生类graduate.hGraduate & operator=(const Graduate & another);graduate.cppGraduate & Graduate::operator=(const Graduate & another){ if(this == &another) return *this; Student::operator =(another); this->salary = another.salary; return * this;} 测试代码int main(){ Graduate g("liuneng",2001,'x',2000); g.dump(); Graduate gg = g; gg.dump(); cout<<"-----------"<
派生类的默认赋值运算符重载函数, 会调用父类的默认或自实现函数。 派生类若自实现,则不会发生调用行为,也不报错,需要自实现。
派生类的析构函数的功能是在对象消亡之前进行一些必要的清理工作,析构函数没有类型,也没有参数。析构函数的执行顺序与构造函数相反。
析构顺序
子类->成员->基类
析构函数只有一种,无重载,无默参。
A、作用域
基类名::成员名;基类名::成员名(参数表);
如果某派生类的多个基类拥有同名的成员,派生类又新增这样的同名成员,派生类成员将shadow(隐藏)所有基类的同名成员,需要作用域的调用方式才能调用基类的同名成员。
#include using namespace std;class Base{public: void func(int) { cout<<"haha"<
重载:同一作用域 ,函数同名不同参(个数,类型,顺序);
隐藏:父子类中,标识符(函数,变量)相同,无关乎返值和参数(函数),或声明类
型(变量)。
B、继承方式
public公有继承:
当类的继承方式为公有继承时,基类的公有和保护成员的访问属性在派生类中不变,而基类的私有成员不可访问。即基类的公有成员和保护成员被继承到派生类中仍作为派生类的公有成员和保护成员。派生类的其他成员可以直接访问它们。无论派生类的成员还是派生类的对象都无法访问基类的私有成员。
private私有继承:
当类的继承方式为私有继承时,基类中的公有成员和保护成员都以私有成员身份出现在派生类中,而基类的私有成员在派生类中不可访问。基类的公有成员和保护成员被继承后作为派生类的私有成员,派生类的其他成员可以直接访问它们,但是在类外部通过派生类的对象无法访问。无论是派生类的成员还是通过派生类的对象,都无法访问从基类继承的私有成员。通过多次私有继承后,对于基类的成员都会成为不可访问。因此私有继承比较少用。
protected保护继承:
保护继承中,基类的公有成员和私有成员都以保护成员的身份出现在派生类中,而基类的私有成员不可访问。派生类的其他成员可以直接访问从基类继承来的公有和保护成员,但是类外部通过派生类的对象无法访问它们,无论派生类的成员还是派生类的对象,都无法访问基类的私有成员。
650) this.width=650;" src="http://s1.51cto.com/wyfs02/M00/86/36/wKiom1e4QpaSse58AAFFa0WifJA418.png" title="图片2.png" alt="wKiom1e4QpaSse58AAFFa0WifJA418.png" />
C、派生类成员的属性
派生类的成员分为公有成员;保护成员;私有成员;不可访问的成员。
#include using namespace std;class Base{public: int pub;protected: int pro;private: int pri;};class Drive:public Base{public: void func() { pub = 10; pro = 100;//pro是保护成员,在派生类中可以访问 // pri = 1000;//pri是私有成员,在派生类中不可见 }};//int main(){ Base b; b.pub = 10; //b.pro = 100;//pro是保护成员,在外部不可访问 // b.pri = 1000;//pri是私有成员,在外部不可见 return 0;}
650) this.width=650;" src="http://s4.51cto.com/wyfs02/M01/86/36/wKioL1e4QruA_fT3AABhBxdBuRQ794.png" title="图片3.png" alt="wKioL1e4QruA_fT3AABhBxdBuRQ794.png" />650) this.width=650;" src="http://s1.51cto.com/wyfs02/M02/86/36/wKioL1e4QwSDY1dXAABhBxdBuRQ895.png" title="图片3.png" alt="wKioL1e4QwSDY1dXAABhBxdBuRQ895.png" />
#include using namespace std;class Base{public: int pub;protected: int pro;private: int pri;};class Drive:public Base{public:};
private在子类中不可见,但仍可通过父类接口访问。
public作用:传承接口,间接的传承了数据(protected)。
protected作用:传承数据,间接封杀了对外接口
private作用:封杀了数据和接口
A、只要是私有成员到派生类中,均不可访问。正是体现的数据隐蔽性.其私有成员仅可被本类的成员函数访问。
B、如果多级派生当中,均采用public,直到最后一级,派生类中均可访问基类的
public,protected成员。兼顾了数据的隐蔽性和接口传承和数据传递
C、如果多级派生当中,均采用private,直到最后一级,派生类中基类的所有成员均变为不可见。只兼顾了数据的隐蔽性
D、如果多级派生当中,均采用protected,直到最后一级,派生类的基类的所有成员即使可见,也均不可被类外调用。
多继承的语法定义
派生类名::派生类名(参数总表)
:基类名 1(参数表 1),基类名(参数名 2)....基类名 n(参数名 n),
内嵌子对象 1(参数表 1),内嵌子对象 2(参数表 2)....内嵌子对象 n(参数表 n)
{
派生类新增成员的初始化语句;
}
在多继承中,保存共同基类的多份同名成员,可以在不同的数据成员中分别存放不同的数据,但保留多份数据成员的拷贝,不仅占有较多的存储空间,还增加了访问的困难。C++提供了虚基类和虚继承机制,实现了在多继承中只保留一份共同成员。
虚继承的语法
class 派生类名:virtual 继承方式 基类名
650) this.width=650;" src="http://s1.51cto.com/wyfs02/M01/86/36/wKiom1e4Q1aTv30uAABkNpx8CPo888.png" title="图片4.png" alt="wKiom1e4Q1aTv30uAABkNpx8CPo888.png" />
SleepSofa类继承自Bed和Sofa两个类,因此,SleepSofa类拥有这两个类的特性。创建一个SleepSofa对象时,会构造Bed类和Sofa类,但Bed类和Sofa类都有一个父类,因此Furniture类被构造了两次,这是不合理的,使用虚继承后Furniture类就只会构造一次,sleepSofa对象只会包含一个Furniture对象。
#include #include using namespace std; class Furniture{public: Furniture(void) : m_weight(0){} Furniture(double weight) : m_weight(weight){} ~Furniture(void){} double GetWeight() const { return m_weight; } void SetWeight(double val) { m_weight = val; }private: double m_weight;}; class Bed : virtual public Furniture{public: Bed() : Furniture(), m_second(0) {} Bed(double weight, int second) : Furniture(weight), m_second(second){} void Sleep(int second) { m_second = second; cout << "休息" << m_second << "秒..."<< endl; }private: int m_second; };class Sofa : virtual public Furniture{public: Sofa() : Furniture() {} Sofa(double weight) : Furniture(weight){} void WatchTV(string programme) { cout << "正在看" << programme << "节目..." << endl; }}; class SleepSofa : public Bed, public Sofa{public: SleepSofa() : Bed(), Sofa() {} SleepSofa(double weight, int second) : Bed(weight, second), Sofa(weight) {} void FoldOut() { cout << "展开沙发当床用." << endl; Sleep(360); }};int main(void){ SleepSofa sleepSofa; sleepSofa.SetWeight(55); double weight = sleepSofa.GetWeight();}
在程序设计中最好不要出现多继承,要有也是继承多个作为接口使用抽象类(只声明需要的功能,没有具体的实现)。因为出现一般的多继承本身就是一种不好的面向对象程序设计。
本文出自 “生命不息,奋斗不止” 博客,转载请与作者联系!