第7节课:9.7:继承

第7节课:9.7:继承

一、继承和派生

1.是什么?

1.继承是创建类的一个新的方式,新的类可以继承一个或者多个类

2.继承和派生,从不同的角度来区别:父类派生出子类,子类继承父类

3.新建的类为派生类****或者是子类***,原来的被继承的那个类称为基类或者父类

2.怎么定义一个派生类?

在新建的类的定义后面加上 :继承方式(分为三种) 要继承的那个类的类 :public CA

class CA
{
    public:
int a;
}

class CB : public class CA
{
    public:
    int b;
}

3.使用:

1.继承的方式有三种:

私有,公有,保护

1.基类中的私成员,无论哪种继承方式都不可以继承类中的新成员和对象都不可以访问

2.基类中的成员属性,继承的方式,哪个更严格,使用哪种属性

#include <stdio.h>

class CA
{
public:
	int a;
private:
	int b;
protected:
	int c;
public:
	int GetCA();
};

int CA::GetCA()
{
	return b;
}

class CB : public  CA
{
public:
	int GetCATest();
};

int CB::GetCATest()
{
	return c;//保护属性的成员可以被访问
	//return a;//公有的成员也可以
	//return b;//私有的不行这时只能够通过调用CA中的方法来访问b
}


int main()
{
	CB obj;
	//obj.b;//私有
	//obj.c;//保护
	obj.GetCA();//通过接口拿到基类私有数据
	return 0;
}
2.继承类的类中成员:

1.继承类中的成员:有一个父类拷贝过来的无名类(也就是说包含了父类的数据成员和函数成员)+自己新的数据成员和函数成员

继承过来的函数成员除了基类的构造函数和析构函数,其他都会继承过来???

3.派生类的内存分配:
(1)基类的无名拷贝类内存在继承类的内存之前(可以打断点调试,查看内存)
#include <stdio.h>

class CA
{
public:
	int a;
};

class CB : public  CA
{
public:
	int b;
};

int main()
{
	CA obj1;
	CB obj2;
	obj1.a = 30;
	obj2.a = 10;
	obj2.b = 20;
	CA * a1 = &obj1;
	CB * a2 = &obj2;
	int c = 0;
	return 0;
}
(2)回顾结构体那部分的内存,做一个比较:

#萝卜老师这里的内存分配有点问题,

(我的理解应该是,把类CA整个作为整体拷贝过来也就是24字节,然后字节对齐(最大成员内存的最小公倍数)int b 就要占8个字节,所以一共是32个字节

结构体那里的实例也是一样的计算方法)这种理解是对的

#include <stdio.h>

class CA
{
public:
	int a;
	double b;
	char c;
};

class CB : public  CA//如果按照展开的方式:CB应该是24,但是结果是32,
{
public:
	int b;
};

struct a//24
{
	int a1;
	double b;
	char c;
};

struct b//24?是错的,原来可达老师说是把A展开得到注释的部分8+8+8 = 24,但是结果却是32??
{
	int d;
	struct a A;
	/*int a1;
	double b;
	char c;*/
};


int main()
{
	printf("类:%d %d ",sizeof(CA),sizeof(CB));//24 32
	printf("结构体:%d %d ", sizeof(a), sizeof(b));//24 32
	return 0;
}
4.派生类和基类有同名成员
#include <stdio.h>

class CA
{
public:
	int a;
private:
	int b;
protected:
	int c;
public:
	//int GetCA();
};

//int CA::GetCA()
//{
//	return b;
//}

class CB : public  CA
{
public:
	int a;
};



int main()
{
	CB obj;
	obj.a = 10;
	obj.CA::a = 20;
	CB *p = &obj;//内存之中,赋值情况是:CB中的无名拷贝类中的a没有赋值,而是CB类区域有a被赋值了
	int c = 0;
	return 0;
}

前面12个字节分别是拷贝过来的a,b,c,之后的四个字节才是CB的a值为10;

5.基类与派生类之间的关系:
(1)基类对象不是派生类对象
(2)派生类对象是基类对象

二、构造函数的调用顺序以及多继承以及派生类构造函数怎么定义

1.不带参构造

2.带参构造,用成员初始化列表,要调用父类构造函数传参

#include <stdio.h>

class CA
{
public:
	int a;
	CA(int i);
	~CA();
};

CA::CA(int i)
{
	printf("调用构造CA\n");
}

CA::~CA()
{
	printf("调用析构CA\n");
}


class CB
{
public:
	int a;
	CB();
	~CB();
};

CB::CB()
{
	printf("调用构造CB\n");
}

CB::~CB()
{
	printf("调用析构CB\n");
}

class CC : public CB,public CA//12字节
{
public:
	int a;
	CC(int i);
	~CC();
};

CC::CC(int i) :CA(i), CB()
{
	printf("调用构造CC\n");
}

CC::~CC()
{
	printf("调用析构CC\n");
}

int main()
{
	CC obj(1);//如果父类都不是带参构造时,则CC的构造函数会自动调用父类构造函数,不用自己定义
	//调用顺序,如果多个基类,则按照定义类是的继承的声明顺序:先调用CB,在CA最后调用自己
	//析构函数则是相反,栈先进后出
	return 0;
}

三、菱形继承(虚基类的引出)虚基类virtual关键字

1.B和D中都有一份名字相同的A的拷贝过来的成员,

所以E中有两份数据a,访问会出问题,只能够通过如下方式:

#include <stdio.h>

class CA//虚基类
{
public:
	int a;
	CA(int i);
	~CA();
};

CA::CA(int i)
{
	a = i;
	printf("构造CA\n");
}

CA::~CA()
{
	printf("析构CA\n");
}


class CB : public CA
{
public:
	int b;
	CB();
	~CB();
};

CB::CB() :CA(10)
{
	b = 2;
	printf("构造CB\n");
}

CB::~CB()
{
	printf("析构CB\n");
}

class CD : public CA
{
public:
	int d;
	CD();
	~CD();
};

CD::~CD()
{
	printf("析构CD\n");
}

CD::CD() :CA(11)
{
	d = 3;
	printf("构造CD\n");
}

class CE : public CB, public CD
{
public:
	int e;
	CE();
	~CE();
};

CE::CE() :CB(), CD()
{
	e = 4;
	printf("构造CE\n");
}

CE::~CE()
{
	printf("析构CE\n");
}

int main()
{
	CE obj;//CA,CB,CA,CD,CE
	CE* p = &obj;//20字节
	int c = 0;
	//printf("%d",obj.CA::a);
	printf("%d\n", obj.CD::CA::a);//虽然有波浪线但是可以编译成功
	return 0;
}

2.只让最上层的基类独占一份:

四、虚基类virtual关键字

1.特点:

(1).调用构造函数的顺序发生了一点点小变化:虚基类,基类,子对象,最后自己、

(2).可以优化内存

虽然此时多了四个字节:CB四个字节的指针,四个字节的数据;CD四个字节的指针,四个字节的数据;CE四个字节的数据,和四个字节的唯一一个CA数据a

一共24

但是如果a是8个字节,那么内存就会有优化了

(3).注意内存的内容的变化:

出现了不认识的四个字节该四个字节是指针,用来询问CA中的a是否被构造了

(4)当派生类加了virtual关键字之后,派生类的构造函数首先看有没有其他地方调用了基类的析构,如果调用了,就不用再调用了。

但是定义派生类析构函数的时候任然要加上声明调用基类构造,仅仅是在程序执行的时候不会调用而已,声明还是需要的

#include <stdio.h>

class CA
{
public:
	int a;
	CA(int i);
	~CA();
};

CA::CA(int i)
{
	a = i;
	printf("构造CA\n");
}

CA::~CA()
{
	printf("析构CA\n");
}


class CB : virtual public CA//有了这个关键字以后,调用该本类的构造函数的时候,先看看基类有没有被构造,有就不调用基类的构造函数
{
public:
	int b;
	CB();
	~CB();
};

CB::CB(): CA(10) //虽然有virtual关键字,不用调用CA构造函数,但是如果定义了CB对象,则还是要调用CA的构造函数,所以在这里也要定义
{
	b = 2;
	printf("构造CB\n");
}

CB::~CB()
{
	printf("析构CB\n");
}

class CD : virtual public CA//有了这个关键字以后,调用该本类的构造函数的时候,先看看基类有没有被构造,有就不调用基类的构造函数
{
public:
	int d;
	CD();
	~CD();
};

CD::~CD()
{
	printf("析构CD\n");
}

CD::CD() :CA(11)
{
	d = 3;
	printf("构造CD\n");
}

class CE: public CB,public CD
{
public:
	int e;
	CE();
	~CE();
};

CE::CE() :CA(100)
{
	e = 4;
	printf("构造CE\n");
}

CE::~CE()
{
	printf("析构CE\n");
}

int main()
{
	//CE obj;//CA,CB,CA,CD,CE
	//obj.CD::CA::a;
	//CE* p = &obj;//20字节
	printf("%d", obj.CA::a);
	//int c = 0;
	CE obj;
	CE * p = &obj;
	int c = 0;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值