继承
1.继承的概念
在C++中,所谓“继承”就是在一个已存在的类的基础上建立一个新的类。已存在的类称为“基类”或“父类”,新建立的类称为“派生类”或“子类”。
单继承:一个子类只有一个直接父类时称这个继承关系为单继承;
多继承:一个子类有两个或多个以上的父类时称这个继承关系为多继承
菱形继承(钻石继承):
class Person
{
private:
int _p;
};
class Student:public Person
{
private:
int _s;
};
class Teacher:public Person
{
private:
int _t;
};
class Assistant:public Student,public Teacher
{
private:
int _a;
};
int main()
{
printf("%d\n",sizeof(Assistant));
system("pause");
return 0;
}
输出的结果为20(后面详述菱形继承)
2.派生类的声明方式
class 派生类名 : [继承方式] 基类名
{
派生类新增加的成员;
};
继承方式:public(公有继承) protected(保护继承) private(私有继承)
当没有给出继承方式时,使用关键字class声明派生类默认继承方式是private,使用关键字struct时默认继承方式是public.
3.派生类的构成
构造一个派生类包含三部分工作:
- 从基类接受成员;
- 调整从基类接受的成员;(调整:指定继承方式;声明与基类相同名字的成员或函数,函数参数列表也要相同,构成隐藏,若参数列表不同,并不能认为它构成了重载,因为两个函数并不在同一作用域中,不满足重载的条件,具体是什么还不太了解,但类外不能调用该基类中的函数;)
- 在声明派生类是增加成员;
4.派生类的成员访问属性
不同的继承方式决定了基类成员在派生类中的访问属性
- 公用继承
公有成员:在基类、派生类和类外均可访问
- 私有继承
私有成员:基类的私有成员只能在基类内部访问,派生类和类外均不可访问
- 保护继承
保护成员:基类的私有成员可以在基类内部和派生类中访问,类外不允许访问
公用继承中,派生类直接访问基类的私有成员是不允许的,只有通过基类的公有成员函数来引用基类的私有成员;
私有继承中,不能通过派生类的对象来访问从私有基类继承过来的任何成员或函数,但可以通过派生类的成员函数调用私有基类的公用成员函数来实现对基类私有成员的访问。
5.派生类的构造函数和析构函数
基类的构造函数和析构函数时不能被继承的
构造函数一般形式:
派生类的构造函数名(总参数列表)
:基类的构造函数名(参数列表)
[,子类对象(参数列表)]
{
派生类新增数据成员初始化语句
}
继承关系中构造函数的调用顺序
派生类的构造函数(按照继承列表的顺序)------>初始化列表中调用基类构造函数----->基类构造函数体----> 派生类的构造函数体
继承关系中析构函数的调用顺序
派生类的构造函数---->派生类的构造函数体---->基类的析构函数--->基类的析构函数体(与构造函数相反)
说明:
- 基类没有缺省的构造函数(即没有缺省值和半缺省),派生类必须要在初始化列表中显示的给出基类名和参数列表。(必须显示的定义构造函数情景二)
- 基类没有定义构造函数,则派生类也可以不用定义,全部使用缺省的构造函数(全缺省和系统默认合成的构造函数)
class Test1
{
public:
Test1(int data)
{
cout<<"Test1()"<<endl;
}
~Test1()
{
cout<<"~Test1()"<<endl;
}
};
class Test2
{
public:
Test2(int data)
{
cout<<"Test2()"<<endl;
}
~Test2()
{
cout<<"~Test2()"<<endl;
}
};
class Base1
{
public:
Base1(int data)
:_data1(data)
{
cout<<"Base1()"<<endl;
}
~Base1()
{
cout<<"~Base1()"<<endl;
}
protected:
int _data1;
};
class Base2
{
public:
Base2(int data)
:_data2(data)
{
cout<<"Base2()"<<endl;
}
~Base2()
{
cout<<"~Base2()"<<endl;
}
protected:
int _data2;
};
class Derive:public Base1,public Base2
{
public:
/*Derive()
:Base1(0)
,Base2(1)
,t1(3)
,t2
(4)*/ //两种情况结果相同
Derive()
:t2(3)
,t1(4) //与对象的定义顺序有关
,Base1(0)
,Base2(1) //与继承时的声明顺序有关
{
cout<<"Deriver()"<<endl;
}
~Derive()
{
cout<<"~Deriver()"<<endl;
}
protected:
Test1 t1;
Test2 t2;
};
void funtest()
{
Derive d1;
}
int main()
{
funtest();
system("pause");
return 0;
}
结果图:
静态成员可以继承
6.继承与转换-----赋值兼容规则----public规则
- 子类对象可以赋值给父类对象-----赋值的那部分是子类继承父类的部分
- 父类的对象不能赋值给子类的对象---->程序会崩溃(因为赋值给派生类对象的区域为未知区域,系统不允许)
- 父类的指针、引用可以指向子类对象,(但不能访问派生类新增加的部分,因为父类不能强制转化为子类)
- 子类的指针、引用不能指向父类的对象(可以通过强制类型转换完成)
是不是不太明白,我们可以用程序示例和画图来解释
class A
{
public:
A(int data = 1)
:_a(data)
{}
private:
int _a;
};
class B:public A
{
public:
B(int data1)
:_b(data1)
{}
private:
int _b;
};
int main()
{
A a(1); //父类对象
B b(2); //子类对象
A* _a; //父类指针
B* _b; //子类指针
/*b = a; */ //父类给子类赋值,运行出错
a = b; //子类给父类赋值,运行成功
_a = &b; //父类对象指向子类指针,运行成功
//_a->_b = 1; //访问派生类对象出错
//_b = &a; //子类指针指向父类对象,运行出错,原因是无法将A*转化为B*
_b = (B*)&a; //将地址强制类型转化为B类的地址,运行成功
return 0;
}