最近学习到C++中的的派生类,讲到了一个面向对象的继承,然后看书上的例子就感觉有一些熟悉,因为在C语言求struct的时候就有类似的东西,把一个结构体作为另一个结构体的成员,
#include <stdio.h>
struct A
{
int a;
int b;
};
struct B
{
int c;
struct A;
};
int main()
{
printf("%d\n", sizeof(struct A));
printf("%d\n", sizeof(struct B));
system("pause");
return 0;
}
继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能。这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程
#include <iostream>
using namespace std;
class base
{
public:
base();
base(const base& s);
~base();
//访问权限
public:
int _pb;
private:
int _pi;
protected:
int _po;
};
//public为继承权限
class der :public base
{
per(int pub, int pri, int pro)
{
base::_pb = 10;
//base::_pi = 20;
base::_po = 30;
}
public:
int _pub;
private:
int _pri;
protected:
int _pro;
};
void test()
{
cout << sizeof(base) << endl;
cout << sizeof(per) << endl;
}
int main()
{
test();
system("pause");
return 0;
}
//der类就是base类的派生类,base类时der类的基类
继承权限
和访问权限一样,继承权限也是三个
public :
protected:
private:
基类的private成员在派生类中是不能被访问的,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected,在访问权限中,我们是将protected和private的权限看作是一样的,这里可以看出保护成员限定符是因继承才出现的
public继承:
是一个接口继承,保持is-a原则,每个父类可用的成员对子类也可用,因为每个子类对象也都是一个父类对象
protected/private继承:
是一个实现继承,基类的部分成员并非完全成为子类接口的一部分,是 has-a的关系原则,所以非特殊情况下不会使用这两种继承关系,在绝大多数的场景下使用的都是公有继承。
私有继承通常组合更低级,但当一个派生类需要访问基类保护成员或需要重定义基类的虚函数时它就是合理的。
不管是哪种继承方式,在派生类内部都可以访问基类的公有成员和保护成员,基类的私有成员存在但是在子类中不可见(不能访问)。
使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式
继承中的作用域:
在继承体系中基类和派生类是两个不同的作用域
子类和父类中有同名成员,子类成员将屏蔽父类对成员的直接访问。(在子类成员函数中,可以使用 基类::基类成员 访问)--隐藏 --重定义
注意在实际中在继承体系里面最好不要定义同名的成员
派生类的继承形式
单继承 :
派生类只有一个直接父类
class person;
class student:public person;
多继承:
派生类有多个直接父类
class person;
class anime;
class student : public person,public anime
菱形继承:
派生类有多个直接父类
多个直接父类继承自同一个父类
存在数据冗余和二义性问题
class person;
class student :public person; class teacher:public person;
class assistant: public student,public teacher;
菱形虚拟继承:
在菱形继承的基础上解决了菱形继承的数据冗余和二义性问题,在后续的学习中会再详细了解。
继承的继承与转换(赋值兼容规则):
1.子类对象可以赋值给父类对象
2.父类对象不能赋值给子类对象
3.父类的指针/引用可以指向子类对象
4.子类的指针/引用不能指向父类对象,但是可以通过强转来完成
在继承中有几点要注意:
1.友元,友元关系是不能继承的,基类的友元成员不能访问派生类的protected成员和protected成员。
2.静态成员变量,如果在基类中定义了static成员,那么整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员
3. 类的六个默认函数是不能被继承的。
继承体系中的构造函数次序
如果在体系中先创建派生类的对象,是如何调用构造函数的呢?
#include <iostream>
using namespace std;
class Student
{
private:
int _num;
string _name;
char _gender;
public:
Student(int num, string name, char gender)
{
_num = num;
_name = name;
_gender = gender;
cout << _gender << endl;
cout << "基类的构造函数" << endl;
}
};
class Student1: public Student
{
private:
string _addr;
string _major;
Student object_1;
public:
Student1(string addr, string major, int num, string name, char gender,char object)
:Student(num, name, gender)
,object_1(num, name, object)
{
_addr = addr;
_major = major;
cout << "派生类新成员构造函数" << endl;
}
};
int main()
{
Student1 object("西安", "计算机", 123456789, "罗宇", 'M', 'W');
system("pause");
return 0;
}
但从结果图中可以看出,顺序是
基类的构造函数->
派生类中对象的构造函数->
派生类的构造函数
但是我们一般都知道,我们是创建哪个对象调用哪个对象的构造函数,显然在这里是不适用的,那么我们再来一段代码
class Base
{
public:
Base()
{
cout<<"B()" <<endl;
}
~Base ()
{
cout<<"~B()" <<endl;
}
void ShowBase()
{
cout<<"_pri = " <<_pri<< endl;
cout<<"_pro = " <<_pro<< endl;
cout<<"_pub = " <<_pub<< endl;
}
private:
int _pri;
protected:
int _pro;
public:
int _pub;
};
class Derived:public Base
{
public:
Derived()
{
cout<<"D()"<<endl;
}
~Derived ()
{
cout<<"~D()"<<endl;
}
void ShowDerived()
{
cout<<"_d_pri = "<<_d_pri<< endl;
cout<<"_d_pro = "<<_d_pro<< endl;
cout<<"_d_pub = "<<_d_pub<< endl;
}
private:
int _d_pri;
protected:
int _d_pro;
public:
int _d_pub;
};
在创建派生类对象这里打一个断点,然后F5运行
,
可以看到,在构造派生类对象时,最先使用的时派生类的构造函数,然后我们再F11,
然后马上又跳到基类的构造函数,因为在基类没有构造对象之前,派生类无法构造对象,所以,我们在这里可以说明关于继承体系中构造函数的调用过程应该如下:
1.调用派生类的构造函数,在派生类的构造函数中call基类的构造函数。
2.调用基类构造函数,
3.调用派生类对象的构造函数,
4.调用派生类的构造函数
基类的构造函数调用顺序按照继承列表的声明顺序,对象的构造函数调用顺序按照对象的声明顺序
说明:
1.基类没有缺省构造函数,派生类必须要在初始化列表中显式给出基类名和参数列表。
2.基类没有定义构造函数,则派生类也可以不用定义,全部使用缺省构造函数。
3.基类定义了带有形参表构造函数,派生类就一定定义构造函数
析构函数与构造函数的次序相反。