C++继承

继承

继承允许我们依据另外一个类来定义一个新的类,这使得创建和维护一个应用程序变得更容易。这样做,也达到了重用代码功能和提高执行效率的效果。

继承方式

当继承一个类时,可以选择公有继承(public)、私有继承(private)和保护继承(protected)

公有继承私有继承保护继承
公有成员公有的私有的保护的
保护成员保护的私有的保护的
私有成员不可见不可见不可见
  • 基类的私有成员无论采用什么继承方式在子类都是不可见的。
  • 基类中的公有成员和保护成员在子类中的方式为min(基类中的类型,继承方式)。取权限小的。
  • class的默认是成是private,struct的默认继承方式是public;
  • 私有和保护的区别,在继承中,私有的成员无论采用什么继承方式在基类中是不可见的,保护成员在基类中是可以访问的,在类外面都不可以访问。

派生类对象可以直接赋值给基类的指针、对象和引用,这种方式叫做切片。

基类的对象是不能直接赋值给子类的。

基类的指针可以通过强制类型转换将基类指针转换为子类指针,但是转换的前提必须是安全的。

作用域

当基类和子类中存在同名的函数或者变量时,子类将屏蔽对基类成员的访问,称为隐藏或重定义

当基类和子类中存在同名的函数,即使参数不同,也构成隐藏。

#include <iostream>
using namespace std;

class A
{
    public:
        int a = 10;
};

class B :public A
{
    public:
        int a = 20;//和基类构成隐藏

        void Print()
        {
            cout << a <<endl;
        }
};
int main()
{
    B b;
    b.Print();
    return 0;
}

image-20220403202934252

当想要访问基类的a时候需要就需要指定作用域

        void Print()
        {
            cout<<"访问基类的a "<<A::a<<endl;
            cout << a <<endl;
        }

成员函数构成隐藏

#include <iostream>
using namespace std;

class A
{
    public:
        void Print()
        {
            cout<<"基类的Print"<<endl;
        }
};

class B :public A
{
    public:
        void Print(int i)
        {
            cout <<"派生类的Print"<<endl;
        }
};
//虽然基类和子类的参数不同但是仍然构成隐藏。
int main()
{
    B b;
    b.A::Print();//指定调用基类中的Print
    b.Print(1);//默认调用的是子类的。
    return 0;
}

默认成员函数

默认生成的派生类的构造函数和析构函数会做什么?

  • 对于继承下来的成员调用基类的默认构造和析构函数,对于自己的成员自定义类型调用自己的默认构造,内置类型不做处理。
  • 拷贝构造和赋值同样也是。基类继承下来的调用基类的operator=和拷贝构造,自己的对于自定义类型调用自定义类型的,内置类型完成值拷贝。

什么情况下需要自己写?

  • 父类没有默认的构造函数。
  • 子类中有资源需要释放,就需要我们自己写析构
  • 使用浅拷贝存在问题就需要我们自己实现拷贝构造和赋值。

怎么实现呢?

  • 对于父类成员调用父类的构造、拷贝构造、赋值和析构(不需要显示调用)。
  • 自己的成员按照普通类处理。
//子类析构函数不需要显示去调用父类的析构
#include <iostream>
#include <string>
using namespace std;

class A
{
public:
	int* _ptr = new int[10];
	~A()
	{
		cout << "~A()" << endl;
	}
};

class B :public A
{
public:
	~B()
	{
		A::~A();
	}
};

int main()
{
	B b;
	return 0;
}

image-20220404093243216在子类的析构函数中,不需要我们显示去调用基类的析构函数,会自动调用。

在构造时,会先调用基类的构造函数,再调用子类的构造函数,同样在析构时,会先调用子类的析构函数,再调用基类的析构函数。

image-20220404094640983

继承与友元

友元关系不能被继承。那就好比我和你爹是朋友,但我和你不一定是朋友。

静态成员

静态成员在整个继承中只会存在一份。

#include <iostream>
using namespace std;

class A
{
public:
	static int count;
	int b = 0;
	A()
	{
		count++;
		cout << "A的构造函数" << endl;
	}
	void Print()
	{
		printf("基类的 count %p\n", &count);
	}
};
int A::count = 0;

class B :public A
{
public:
	void Print()
	{
		printf("派生类 count %p\n", &count);
	}
};

int main()
{
	A a;
	a.Print();
	B b;
	b.Print();
	B c;
	c.Print();
	return 0;
}

image-20220404100127499

可以看到他们的地址是相同的。

菱形继承

image-20220404101256988

菱形继承就是一个基类被多个类继承,而这些派生类又同时被一个类继承。

#include <iostream>
using namespace std;

class A
{
public:
	int _a;
};

class B :public A
{
public:
	int _b = 10;
};

class C :public A
{
public:
	int _c = 10;
};

class D :public B, public C
{
public:
	int _d = 20;
	void Print()
	{
		cout << _a << _b << _c << _d;
	}
};

int main()
{
	D d;
	d.Print();
	return 0;
}

这里面就会出现一个问题,B和C里面都有一个_a,当继承下来以后就有2份 —a,如果不指定类域就会出现调用不明确。并且造成了数据冗余和二义性。

虚继承

对于多继承可以会产生一些问题,C++给了一个解决方案叫做虚继承。

  • 虚拟继承的目的是令某个类做出声明,承诺愿意共享它的基类,其中贡献的基类对象称为虚基类。在这种机制下,无论基类在继承体系中出现了多少次,在派生类中都只包含唯一一个共享的虚基类子对象。

虚继承就是在继承是时候加上virtural关键字。

class B :virtual public A   //虚继承
{
public:
	int _b = 10;
};
class A
{
public:
	 int _a;
};

class B : virtual public  A
{
public:
	int _b = 10;
};

class C : virtual public A
{
public:
	int _c = 10;
};

class D :public B,  public C
{
public:
	int _d = 20;
};

int main()
{
	D d;
	d._a = 1;
	d._b = 2;
	d._c = 3;
	d._d = 4;
	return 0;
}

image-20220404135036188

通过查看d的内存可以看到,内存的布局是按照继承的先后顺序是一样的,在使用虚继承后可以看到,虚基类是共享的,只存在一份,在B和C里面各有一个地址,这个地址指向的是一个虚基表,虚基表里面存储了当前类到虚基类的偏移量。通过就可以找到A。

image-20220404135632873 ; return 0; } ```

[外链图片转存中…(img-jqDNThby-1649052102493)]

通过查看d的内存可以看到,内存的布局是按照继承的先后顺序是一样的,在使用虚继承后可以看到,虚基类是共享的,只存在一份,在B和C里面各有一个地址,这个地址指向的是一个虚基表,虚基表里面存储了当前类到虚基类的偏移量。通过就可以找到A。

image-20220404135632873
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值