c++中的继承

1.概念:

    是面向对象程序设计使代码可以复用的总要手段,它允许程序员在保持原有类特性的基础上进行扩展,增加性能。这样产生的类,称为派生类。

继承的定义格式:

class Student :public Person

这里的student就是派生类(子类)名称,public是继承权限-->public/protected/private,基类(父类)名称

2.继承权限:

  1.public继承:父类public子类public,父类protected子类protected,父类private子类不可访问

2.protected继承:父类public子类protected,父类protected子类protected,父类private子类不可访问

3.private继承:父类public子类private,父类protected子类private,父类private子类不可访问。

我在写代码验证的时候,在继承权限是public,发现父类是protected时,子类可以继承,但是类外不可访问。那么究竟是protected还是private呢? 就在写一个类,继承权限为public,此时看看在类中可以对此进行访问吗,结果证明可以访问,那么可以说明父类是protected的子类也是protected。同理,堆protected和private也进行了验证。

通过验证的结果,我们发现:

(1)public继承是一个is-a的原则。

两方面对此说明:

            1)调用形式:每个父类可用的成员对子类也可用,因为每个子类也都是一个父类对象。也就是说 可用把子类也看成一个父类,在所有使用父类对象的位置,都可使用子类进行代替。

基类中的成员变量:

public:
	int _pub;
	int _pro;
	int _pri;

子类中成员变量:

public:
	int _pubD;
	int _proD;
	int _priD;

 

 2)对象中各个变量在内存中的布局。

  在内存中:

因此public继承是一个基类

(2)protected/private  是一个has-a的关系。

        1)对象模型是一样的   2)全权限变了    3)类外不可访问

        这个可以称为组合(内嵌),基类的对象在派生类包含了一份。

3.赋值的兼容规则-------必须为public继承

(1)基类对象=派生类对象。因为基类部分+派生类特有是派生类的,因此派生类可以给基类赋值

(2)反过来不可以。因为基类小于派生类。没办法全部赋值。

(3)基类*p=&派生类对象  / 基类&r=派生类对象 。 可以的。基类指针或者引用可以访问派生类继承下来的那一块空间。

(4)反过来不可以。因为派生类会访问基类一段非法空间。

      但可以:派生类* pd=(派生类*)&基类   进行强转。虽然编译可以通过,但是存在风险。

4.作用域

(1)基类和派生类是不同的作用域。

(2)基类与派生类相同名称不能形成重载。而是同名隐藏。

            同名隐藏:基类和派生类具有相同名称的成员(成员变量||函数)与成员的类型,参数无关。通过派生类对对象相同名称的成员的调用后,优先调用派生类的。  也就是说基类中与派生类中相同名称的成员被称为成员隐藏。

            若想调用基类的成员:基类对象.派生类::成员

d.B::TestFunc();

5.继承体系中派生类对象的构造和析构

(1)构造派生类对象:调用派生类的构造函数:1:在派生类构造函数的初始化列表中完成对基类的构造-->2.在构造派生类自己的-->3.执行构造函数的函数体。    因为结果显示先打印基类,在打印派生类。

  注意:1)如果基类中有非缺省的构造函数,派生类必须显示给出自己的构造函数,并在其构造函数的初始化列表调用其基类的构造函数,完成对基类的构造。

             2)如果基类中构造函数是缺省的,派生类构造函数是否显示给出根据需要。

(2)销毁派生类对象:调用派生类的析构函数。1.派生类先完成派生类自己资源释放-->2.在派生类析构函数的后面插了一句:call 基类的析构函数

面试题:实现一个不能被继承的类

1.将基类的构造函数给为私有的,派生类就不可创建对象。通过静态方法实现基类对象的实例化与释放

class B
{
public:
	static B* get(){
		return new B;
	}
	static void del(B* obj){
		if (obj)
			delete obj;
	}
private:
	B()
		:_b(10)
	{}
	int _b;
};
class D :public B
{};
int main()
{
	//D d; //此时对象无法创建,可是父类的对象也没有办法去访问,所以我们我提供方法来进行访问
	B *obj = B::get();
	B::del(obj);
	return 0;
}

2.在c++11 里面可以通过final关键字

class B final
{};

6.友元不可继承,static可以继承

因为友元不是类的成员函数。举个例子:你爸有个好朋友你叫王叔,你把称为王哥。但你不能叫王哥。因为你把的朋友不是你的朋友。

static是在整个继承体系里面只有一个这样的成员。无论派生多少子类,都只有一个static成员实例。

7.继承体系下派生的对象模型

(1)单继承:一个子类只有一个父类

(2)多继承:一个子类有两个或两个以上的父类

class A
{
public:
	int a;
};
class B
{
public:
	int b;
};
class C :public A, public B
{
public:
	int _c;
};
int main()
{
	C c;
	c.a = 1;
	c.b = 2;
	c._c = 3;
    cout << sizeof(c) << endl;   //12
    return 0;
}

对象模型:

(3)菱形继承(钻石继承)

单继承+多继承=菱形继承

class B
{
public:
	int _b;
};
class C1: public B
{
public:
	int _c1;
};
class C2: public B
{
public:
	int _c2;
};
class D : public C1, public C2
{
public:
	int _d;
};
int main()
{

	D d;
	d._b = 1; 
 cout << sizeof(d) << endl; //20
}

此时发现出现错误,因为此时_b有两份, 这里不知道应该调用哪一份。

d.C1::_b = 1;

可以加上作用域来进行访问。但是此时_b还是存了两份,浪费内存空间。

它的对象模型

因为引入:菱形虚拟继承:来解决存在数据冗余及二义性问题。

 

在继承权限前面加上virtual关键字即可构成虚拟继承。

class B
{
public:
	int _b;
};
class C1:virtual public B
{
public:
	int _c1;
};
class C2:virtual public B
{
public:
	int _c2;
};
class D :public C1, public C2
{
public:
	int _d;
};
int main()
{

	D d;
    d._b = 1;
	d._c1 = 2;
	d._c2 = 3;
	d._d = 4
 cout << sizeof(d) << endl;
}

sizeof(d):发现大小成了24.多了4个字节。

刚调试到对象创建。此时就我们发现有两个地址,我们在进去看看这两个地址

                                                   

发现一个是14,一个是0c,我们再看看对象模型

 

此时看到基类在最底下,正好看到C1偏移基类是20个字节,C2偏移基类14个字节。说明明个虚拟继承都会多出来4个字节来记录自己相对于基类的偏移量

我们可以得出结论:

virtual.1-->这个对象就会多出4个字节    2--->取4个字节中的内容,当做地址。3--->取出基类成员的偏移量。对其进行赋值。

虚拟继承的特点

(1)对象模型:基类在下,派生类在上

(2)对象模型多了4个字节,指向偏移表格的地址

(3)是通过偏移量表格来访问基类成员的

(4)编译器一定会给其合成构造函数,多压入一个1,作用就是在看是否为虚拟继承。(0和1做了比较,不相等就将其地址压入)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值