继承的概念
继承是一种使代码能被复用的一种机制。通过继承机制,可以利用已有的数据类型来定义新的数据类型。所定义的新的数据类型不仅拥有新定义的成员,而且还同时拥有旧的成员。我们称已存在的用来派生新类的类为基类,又称为父类。由已存在的类派生出的新类称为派生类,又称为子类。
继承格式
// 派生类 继承方式 基类
// || || ||
class Son:public Dad
{
public:
char * identity;//地位
};
继承方式和访问限定符
/--public继承
/
继承方式-----protected继承
\
\--protected继承
/---public访问
/
访问限定符 ---protected访问
\
\--private访问
继承基类成员访问方式的变化
类成员/继承方式 | public继承 | protected | private继承 |
---|---|---|---|
基类的public成员 | 继承到派生类的public | 继承到派生类的protected | 继承到派生类的private |
基类的protected成员 | 继承到派生类的protected | 继承到派生的protected | 继承到派生类的private |
基类的private成员 | 在派生类中的不可见 | 在派生类中不可见 | 在派生类中不可见 |
小结:
1.父类的private成员在任何继承方式下,子类都无法继承
2.除上述外子类能继承到访问限定区域(指public之类)的哪一块,取决于继承方式
与父类中成员所处区域
哪一个开放程度更小(public>protected>private)
3.子类继承到protected和private的成员在第一次继承中使用没有本质区别,都只能在类内访问,无法在类外访问。但是子类再继承给别的类就能体现区别所在
基类和派生类对象赋值转换
在子类和父类之间,子类能够赋值给父类,这种赋值类似与int
与int
之间的赋值,不会产生类型转换(double与int之间会产生、传参给单参数构造函数也会)
但是父类不能赋值给子类
子类赋值给父类的两种方式:
1.直接赋值
class Dad
{
public:
int property;
char* job;
};
class Son:public Dad
{
public:
char* gf;
};
int main()
{
Son son;
Dad d=son;//son类在继承状态下创建对象同时继承Dad类的成员
//此时赋值是将除自身成员外来自Dad的成员赋值给d
Dad *pd=&son;//pd指向son中继承自d的那部分成员
Dad& rd=son;//rd引用的同样是son继承自d的那部分成员
}
继承中的作用域
这部分内容有个与之相关的词汇叫做隐藏
1.子类虽能继承父类成员并在子类中使用,但他两其实都有独立的作用域
2.当父类和子类有重名成员时,默认调用子类成员
3.在调用时标注上作用域也可以调用父类成员
class Daddy
{
protected:
string name = "小头爸爸";
};
class Baby:public Daddy
{
public:
void Print()
{
cout << name << endl;
}
protected:
string name = "小头儿子";
};
int main()
{
Baby b;
b.Print();
}
派生类的默认成员函数
我们都知道默认成员函数不写会自动生成,那么在继承关系中又是怎么样呢
class Person
{
public:
Person()
{cout<<"起床"<<endl;}
Person(const Person& p)
{cout<<"造娃"<<endl;}
operator=(const Person& p)
{cout<<"模仿"<<endl;}
~Person()
{cout<<"睡觉"<<endl;}
};
class Student():public Person
{
//如果在子类不写默认成员函数,系统会调用相对应不写的默认成员函数
//如果写了,则不会调用父类相对应的默认成员函数
//如果自己写了也想调用父类的默认成员函数则需要如下列所写
public:
Student()
:Person()
{}
Student(const Student& s)
:Person(const Student& s)
{}
operator(const Student& s)
{
//因为同名构成隐藏
Person::operator(s);
}
//这里可以不写因为编译器自动完成,为的就是满足 父比子先构造
子比父先析构
~Student()
{}
};
继承和友元
就提一嘴:
父类的友元子类不能继承
继承和静态成员
静态成员与成员函数一般,多个对象共用一个静态成员
菱形继承
聊到菱形继承首先来看看多继承
class A
{
public:int _a
};
class B:
^ {
| public:int _b
| };
| ^
| /
| /
class C:public A,public B
{
public: int _c
}
//在内存中A(_a),B(_a,_b),C(_a,_b,_c)
再来看看菱形继承
class A{public:_a};
^ ^
/ \
/ \
class B:public A
{int _b};
^ class C:public A
| {int _c};
| ^
| /
class D:public B,public C
{int _d};
//A(_a)
//B(_a,_b)
//C(_a,_c)
//D(_a,_b,_c,_d)
在菱形继承中有个最大的问题就是B、C分别继承了_a,那么在D继承B、C后同时也继承到了_a。如果想要使用这个_a那么问题就来了,由于类域的存在,使用_a就会产生二意性。同时,如果A的成员增加的话,D就会数据冗余
菱形虚拟继承
为了解决这个问题,虚拟继承就诞生了
class A{public:_a};
^ ^
/ \
/ \
class B:virtual public A
{int _b};
^ class C:virtual A
| {int _c};
| ^
| /
class D:public B,public C
{int _d};
通过虚拟继承的_a ,它就类似静态成员,多个类共用一个变量,这样不仅解决了二意性还解决了数据冗余。
但同样虚拟继承继承了类似于name这样的变量就不太合适了,所以酌情使用