C++继承

C++继承

继承是C++中一种重要的对象复用手段,并且也是以后实现多态的基础

在类中有三种访问限定符:public、protected、private。所以在继承方式上也就有三种方式的继承:分别是

1、公有继承(关键词:public) 
2、保护继承(关键词:protected) 
3、私有继承(关键词:private)

一般的继承格式为 class 子类:访问限定符 父类 
其中子类也叫派生类,父类也叫基类。 
先来看一个简单的继承

1.class A
2.{
3.public:
4. int _a1;
5.protected:
6. int _a2;
7.private:
8. int _a3;
9.};
10.
11.class B:public A
12.{
13.public:
14. int _b1;
15.protected:
16. int _b2;
17.private:
18. int _b3;
19.};

这种方式就是B继承A。 
在现实生活中继承就相当于孩子会继承父母亲的一些东西,并且能够使用父亲的东西,在C++的继承里还可以这样使用吗? 
现在再B中分别给A的三个成员赋值,看看能不能得到正确赋的值


可以看到,在编译时给出了一个报错,_a3不可访问,那么_a3有没有被继承下来?所以对派生类求一下大小就知道有没有把基类的所有成员继承下来


所以就可以得出 
在公有继承时派生类继承了基类的权限,并且所有的积累成员全都被继承下来

如果现在把继承方式变为保护继承还会不会和上面的公有继承一样 
要想测试,保护继承是基类的所有成员权限是否被继承下来就必须在类外访问以下基类的所有成员,如果能被访问就说明是权限继承下来了

1.void test()
2.{
3. B b;
4.
b._a1 = 1;
5.}

先来看看编译有没有错误

 
咦,怎么公有的成员现在都访问不到了,这是因为在继承方式为保护时,基类里面权限比保护大的权限全都降级变为了保护权限,私有的还是私有的,所以不能通过类外访问基类里面的公有成员了。

既然保护继承会将比他权限大的公有成员变为保护的,那么私有继承就肯定能将基类里面比他权限大的公有成员和保护成员变为私有的权限,也就是不能通过类外访问基类里面的所有成员。 
在类中,默认的继承方式是私有继承,和类里面的默认成员权限一样,在结构体中默认的继承权限为公有的。

继承关系

在一个类中,默认成员函数的调用都是先调用构造函数,最后在退出的时候在调用析构函数。如果在集成这种方式里,他的默认函数又是怎么来调用的。

1.class A
2.{
3.public:
4. A()
5. {
6. cout<<"A()"<<endl;
7. }
8.
9. ~A()
10. {
11. cout<<"~A()"<<endl;
12. }
13.
14.public:
15. int _a1;
16.};
17.
18.class B: public A
19.{
20.public:
21. B()
22. {
23. cout<<"B()"<<endl;
24. }
25.
26. ~B()
27. {
28. cout<<"~B()"<<endl;
29. }
30.
31.public:
32. int _b1;
33.
34.};
35.
36.void test()
37.
{
38. B b;
39.}

 
在结果中可以看到先调的是基类的构造函数,然后是派生类的,但实际上真是这样吗? 
在调试时,让程序往下走,会发现在系统先调用的是派生类的构造函数,只不过在调派生类构造函数的初始化列表处,会调用一次基类的构造函数,所以实际上是先调用派生类的构造函数,只不过在执行派生类构造函数体内之前调一次基类的构造函数。在析构的时候,是先析构派生类,然后在析构基类。 
往往在写构造函数的时候,会给构造函数传一个参数,用作初始化,如果给基类的构造函数里加一个参数进行初始化,再通过派生类里的构造函数初始化列表来调用,会不会还是和上面一样大的结果呢?

 
在编译时系统提示报错,说没有可用的默认构造函数,因为刚刚是在派生类的构造函数里调用了一次基类的构造函数,而这里默认的是如果基类的构造函数不是缺省的,就不会在编译的时候自动合成基类的构造函数,所以在构造派生类的时候在初始化列表里加上给基类的参数

1.class B:public A
2.{
3.public:
4. B(int a)
5. :A(a)
6. {}
7.};

另一个继承关系: 
基类如果构造函数是缺省的,则派生类会自动合成一个,如果基类和派生类里有相同的变量名,则会优先调用派生类的变量

优先调用派生类的变量是,在基类和派生类里两个变量名相同的情况下,会先调用派生类里的变量

 
在A和B中都有一个_a1,给_a1赋值之后,B::_a1的值会变为10,基类里的不变。 
相同的变量是这样,那相同变量名的函数呢?

1.class A
2.{
3.public:
4. void test1()
5.
{
6. cout<<"A::test1()"<<endl;
7. }
8.
9.
10.public:
11. int _a1;
12.};
13.
14.class B: public A
15.{
16.public:
17. void test1()
18.
{
19. cout<<"B::test1()"<<endl;
20. }
21.
22.
23.public:
24. int _b1;
25.};
26.
27.void test()
28.
{
29. B b;
30. b.test1();
31.}

在用派生类调用基类里的test1()函数的时候,会优调用派生类里的test1()函数


在这里把基类里的同名函数叫做同名隐藏函数,如果要想调用基类里的同名隐藏函数就在函数前面加上类的作用域限定符来加以说明。如果要是基类和派生类的同名函数有返回值,且返回值类型不同,在调用的时候也是优先调用派生类的函数, 参数不同也是优先调用派生类。所以最好不要写出具有相同变量名的函数来。
继承–赋值兼容规则(public继承)

我们知道在继承时,派生类会继承基类的成员变量,而每一个派生类里面可能还有他自己的成员变量,这时在派生里面就会有多个变量 
而要是将基类给派生类赋值的话,会出现什么情况

1.void test()
2.{
3. A a;
4. B b;
5.
b = a;
6.}

在刚刚创建的派生类里,如果将基类赋给派生类编译器会报错

 
那么就来探究一下为什么这种方式不可以

 
在继承中,派生类继承基类的成员会出现在派生类成员变量的前面,因为构造的时候在初始化列表中先调用的基类的构造函数。若将基类的变量赋给派生类,基类会在他的成员变量里找和派生类一样大的空间和变量,而派生类里面除了基类的变量(_a)还有他自己的成员变量(_b),所以在基类中找不到(_b),这时就谈不上赋值了,所以会发生错误。而反过来,如果是派生类给基类赋值的时候就可以,因为他里面有所有的基类的成员变量

1.void test()
2.{
3. A a;
4. B b;
5.

6. A* pa1 = &b;
7.
A& pa2 = b;
8.

9. B* pb1 = &a;
10. B& pb2 = a;
11.}
12.

让基类的指针(或引用)指向派生类,让派生类的指针(或引用)指向基类哪个可行?

 
在派生类中,派生类指针指向的是他自己的哪一块内存(里面有基类的成员变量和自己的成员变量),当他指向基类时,就说明基类里面有和它一样的内存空间,存放着成员变量,但事实上,基类里只存的是自己的成员变量,所以在通过派生类的指针指向基类的时候就会出错(考虑一下可不可以在赋值的时候强转成派生类的,可以但是这样做存在内存泄漏问题,一般不推荐)。所以,基类的指针可以指向派生类。

我们知道友元函数可以访问一个类里面的私有成员变量,如果继承了父类的友元函数,那么还能通过友元函数来访问私有变量成员

1.class A
2.{
3.public:
4. friend void Display(A& a, B& b);
5.public:
6. int _a1;
7.};
8.
9.class B: public A
10.{
11.public:
12. int _b1;
13.};
14.void Display(A& a, B& b)
15.
{
16. cout<<a._a1<<endl;
17. cout<<b._a1<<endl;
18. cout<<b._b1<<endl;
19.
20.}
21.
22.void test()
23.
{
24. A a;
25. B b;
26. Display(a, b);
27.}
28.


定义了一个基类的友元函数,如果要是友元可以继承下来,那么编译会直接通过,而现在报错,提示语法错误。所以这里的基类友元函数不会被派生类继承下来(你爸爸的朋友不一定是你的朋友)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值