继承:一个非常自然的概念,现实中的很多事情都是具有继承性的。类似于自己继承父母的特性,这也是继承的特性:
而继承的上层称为基类,下一层就叫做派生类。
格式: class 派生类 : 继承方式 基类
继承
例如:
#include<iostream>
using namespace std;
class Person
{
public:
int length;
int weight;
int age;
};
class Student :public Person //其中,学生类继承了人类的属性
{
public:
int son;
char *data;
char *school;
};
int main()
{
Student S;
Person P;
S.age = 18;
S.son = 1;
cout << S.age << endl;
cout << S.son << endl;
cout << sizeof(P) << endl;
cout << sizeof(S) << endl;
system("pause");
return 0;
}
我们可以发现,学生也是人,可以具有人类的任何属性
输出结果为
18
1
12
24
我们也能轻易发现在这段代码中,public的继承方式下,学生类的size 变成了24,说明学生类全部继承人类的属性,那么是不是就可以说继承就是派生类继承基类的所有属性呢?还是只是在公有继承方式下才能够继承所有属性,还是在公有继承下继承公有成员呢??等等,一系列问题,那我们在看一段代码来了解
#include<iostream>
using namespace std;
class Base
{
public:
Base()
{
cout << "Base()" << endl;
}
~Base()
{
cout << "~Base()" << 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 << "Derived()" << endl;
}
~Derived()
{
cout << "~Derived()" << endl;
}
void ShowDerived()
{
//cout << "pri :" << pri << endl;
cout << "pro :" << pro << endl;
cout << "pub :" << pub << endl;
cout << "D_pri :" << D_pri << endl;
cout << "D_pro :" << D_pro << endl;
cout << "D_pub :" << D_pub << endl;
}
public:
int D_pub;
private:
int D_pri;
protected:
int D_pro;
};
int main()
{
Derived D;
Base B;
cout << sizeof(D) << endl;
cout << sizeof(B) << endl;
system("pause");
return 0;
}
输出结果为
Base()
Derived()
Base()
24
12
还是public继承的方式,在程序中我们可以看到在Derived类中访问Base类的private成员是非法的,也就是无法访问,但是看字节大小,我们可以知道Base类的private成员是被继承了的,但是只是无法访问。当把继承方式换成我们之前从未接触的protected时,结果是不变的,这也是protected出现的原因了。
再说说其中对象的调用顺序,很明显,在创建对象的时候,由Base直接创建的对象就只调用Base(),而由Derived创建的对象则是先调用Base(),在调用Derived()的,这是构造函数的调用,那么自然而然,析构函数则是相反的顺序,就不多说了。
protected
在说一说protected,基类的private成员在派生类中是不能被访问的,如果基类的成员不想被类外直接访问,但需要在派生类中被访问,就定义protected。
来举个栗子吃吃看:
在上面的代码的Derived类中添加成员函数,并将继承方式改为protected
void show()
{
Base B;
B.pro = 1;
}
会发现B.pro是可以被访问的,这就是protected的用法。
如果不给出显式的继承方式,那么系统将默认为private继承,但是一般情况下还是给出继承类型比较好。特别的,struct的默认继承方式是public。
再说说同名隐藏
直接上栗子,开吃:
#include<iostream>
using namespace std;
class Base
{
public:
int _b;
};
class Derived :public Base
{
public:
int _b;
};
int main()
{
Derived d;
d.Base::_b = 1;
d.Derived::_b = 2;
cout << d._b << endl;
cout << d.Base::_b << endl;
cout << d.Derived::_b << endl;
system("pause");
return 0;
}
输出结果
2
1
2
同名的成员还是继承了,只是在调用的时候访问Derived类,也就是同名隐藏直接将Base类中的成员屏蔽掉了,但是还是继承了,并且也可以通过::来进行访问。这里有必要提一下重载,重载是同一个作用域内进行功能的重写,而同名隐藏则是不同作用域的使用,有着本质的区别。
友元函数是不能被继承的!!!!友元函数是不能被继承的!!!!友元函数是不能被继承的!!!!
多继承
多继承,顾名思义,也就是一个Derived类有多个(不止两个)Base类,第四盘栗子了:
class Base1
{
public:
int b1;
};
class Base2
{
public:
int b2;
};
class Derived :public Base1, public Base2
{
public:
int d;
};
int main()
{
Derived D;
D.b1 = 1;
D.b2 = 16;
D.d = 1;
system("pause");
return 0;
}
这就是多继承,那么我们来看看多继承的内存顺序
很明显多继承先存储的是Base类中的成员,Base的成员则是按照继承的顺序来存储的
菱形继承
菱形继承也就是钻石继承
程序如图
#include<iostream>
using namespace std;
class Base
{
public:
int b;
};
class C1 :public Base
{
public:
int c1;
};
class C2 :public Base
{
public:
int c2;
};
class Derived :public C2, public C1
{
public:
int d;
};
int main()
{
Derived d;
//d.b=0;
d.c1 = 1;
d.c2 = 2;
d.d = 3;
system("pause");
return 0;
}
我们从上面的程序可以看到,菱形继承存在一些问题。也就是二义性,当我们让对象访问_b的时候会出错,原因就是,二义性,Base1从Base中继承了一个_b,Base2也从Base中继承了一个_b,显然这两个_b都被Derived继承了,那么,我们直接访问_b的时候,系统就会报错“对_b的访问不明确”。
所以引入下面这个概念
虚继承
虚继承解决了在菱形继承体系里面子类对象包含多份父类对象的数据冗余&浪费空间的问题。
虚继承体系看起来好复杂,在实际应用我们通常不会定义如此复杂的继承体系。一般不到万不得已都不要定义菱形结构的虚继承体系结构,因为使用虚继承解决数据冗余问题也带来了性能上的损耗。
#include<iostream>
using namespace std;
class Base
{
public:
int b;
};
class C1 :virtual public Base
{
public:
int c1;
};
class C2 :virtual public Base
{
public:
int c2;
};
class Derived :public C2, public C1
{
public:
int d;
};
int main()
{
Derived d;
d.b = 0;
d.c1 = 1;
d.c2 = 2;
d.d = 3;
cout << sizeof(d) << endl;
system("pause");
return 0;
}
这样就解决的二义性所造成的问题