基类和派生类的赋值对象转换、派生类与基类成员的函数隐藏、派生类中的默认成员函数、继承与友元、继承与静态成员函数、菱形继承、菱形虚拟继承等的介绍


前言

基类和派生类的赋值对象转换、派生类与基类成员的函数隐藏、派生类中的默认成员函数、继承与友元、继承与静态成员函数、菱形继承、菱形虚拟继承等的介绍


一、基类和派生类的赋值对象转换

派生类可以赋值给基类,因为会是使用切片、切割的方式将派生类中基类所拥有的成员赋值给基类
基类不可以直接赋值给派生类

在这里插入图片描述

#include <iostream>
#include <string>
using namespace std;

class Person
{
public:
	string _name;
	string _sex;
	int _age;
};

class Student : public Person
{
public:
	int _No;
};
int main()
{
	Student s;
	s._name = "zhangsan";
	s._sex = "nan";
	s._age = 18;
	s._No = 999;

	Person p;
	p = s;


	return 0;
}

在这里插入图片描述

二、派生类与基类成员的函数隐藏

当派生类中的成员函数与基类中的成员函数同名时,就会默认隐藏基类中的成员函数,只能访问到派生类的成员函数,若需要访问基类中的成员函数,需要加类的访问限定符。

#include <iostream>
using namespace std;

class Person
{
public:
	void fun()
	{
		cout << "name:" << _name << endl;
	}


protected:
	string _name = "peter";
	int _num = 100;
};

class Student : public Person
{
public:
	void fun(int i)
	{
		// 派生类找成员变量的顺序,默认从局部域开始找,若没有去自己的类中找,
      // 若没有再去基类中找,最后再去全局域中找,没找到报错
		//int _num = 0;
		cout << "num:" << _num << " i:" << i << endl;
		cout << "Person::num:" << Person::_num << endl;
	}

protected:
	int _num = 999;
};
int main()
{

	Student s;
	s.fun(1);
	s.Person::fun();


	return 0;
}

三、派生类中的默认成员函数

  • 派生类中的构造函数,会默认调用基类的默认构造函数,若基类中没有默认构造函数,则需要手动调用。

  • 派生类中的拷贝构造,在调用基类的拷贝构造函数时,只要传入派生类对象即可,因为派生类会自动切片赋值给基类

  • 派生类中的赋值运算符重载函数,与基类中的赋值运算符重载函数名相同,会自动隐藏,在调用基类中的赋值运算符重载时,要加类访问限定符调用基类的赋值运算符重载函数。

  • 派生类中的析构函数,在析构函数调用结束时会自动调用基类中的析构函数,因为必须先析构派生类,再析构基类(防止析构完基类,但再次用到基类的成员)

// 派生类的默认成员函数
#include <iostream>
using namespace std;

class Person
{
public:
	Person(const char* name)
		:_name(name)
	{
		cout << "Person()" << endl;
	}

	Person(const Person& p)
		:_name(p._name)
	{
		cout << "Person(const Person& p)" << endl;
	}

	Person& operator=(const Person& p)
	{
		if (this != &p)
		{
			_name = p._name;
		}
		return *this;
	}

	~Person()
	{
		cout << "~Person()" << endl;
	}
protected:
	string _name;
};


class Student : public Person
{
public:
	// 派生类的构造函数会在初始化列表中先调用基类的默认构造函数,在对自己的成员变量初始化
	// 如果基类中没有默认构造函数,需要在派生类的初始化列表调用基类的构造函数
	// 派生类继承基类,基类成员的声明一定是在派生类中的,所以初始列表一般应先初始化基类成员,与声明保持一致
	Student(const char* name = "zhangsan", int num = 99)
		:Person(name)
		,_num(num)
	{}

	Student(const Student& s)
		:Person(s)
		,_num(s._num)
	{}

	Student& operator=(const Student& s)
	{
		if (this != &s)
		{
			// 直接写operator=会调用派生类的赋值运算符重载
			// 此处应该调用基类的赋值运算符重载,应该加类域
			Person::operator=(s);
			_num = s._num;
		}

		return *this;
	}

	~Student()
	{}
protected:
	int _num;
};

int main()
{
	Student s1("knowledge", 100);

	Student s2(s1);

	Student s3;

	s3 = s2;


	


	return 0;
}

在这里插入图片描述

在这里插入图片描述

四、继承与友元

友元关系不能继承

五、 继承与静态成员函数

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例 。

所以,我们可以计算一个基类以及它的派生列共实例化了多少个对象。

因为每一个派生类构造时都会调用基类的默认构造,所以我们用静态成员变量在基类的默认构造中计数

#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
	Person() { ++_count; }
protected:
	string _name;
public:
	static int _count;
};
int Person::_count = 0;

class Student : public Person
{
protected:
	int _num;
};

class Teacher : public Person
{
protected:
	int _id;
};
int main()
{
	Person p;
	Student s1;
	Student s2;
	Teacher t;

	cout << p._count << endl; // 4
	return 0;
}

在这里插入图片描述

六、菱形继承

#include<iostream>
#include <string>
using namespace std;

class Person
{
public:
	string _name;
};

class Student : public Person
{
protected:
	int _num;
};

class Teacher : public Person
{
protected:
	int _id;
};

class Assistant : public Student, public Teacher
{
protected:
	int _age;
};


int main()
{
	Assistant a;
	// 二义性的问题,_name指向不明确
	//a._name = "zhangsan";


	// 可以解决二义性的问题,但是无法解决数据冗余的问题
	a.Student::_name = "zhang";
	a.Teacher::_name = "Mr.zhang";


	return 0;
}

在这里插入图片描述

七、菱形虚拟继承

在腰部的类的继承方式前面加 virtual

虚拟继承会改变类对象模型的结构,比如: D会改变B和C的结构,B中除了存储_b外,还存除了,当前位置到A的偏移量。有了偏移量可以让每个类都有很好的访问方式

#include<iostream>
#include <string>
using namespace std;

class A
{
public:
	int _a;
};

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

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

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


int main()
{
	D d;

	d._a = 2;
	d._b = 3;
	d._c = 4;
	d._d = 5;

	return 0;
}

在这里插入图片描述


总结

基类和派生类的赋值对象转换、派生类与基类成员的函数隐藏、派生类中的默认成员函数、继承与友元、继承与静态成员函数、菱形继承、菱形虚拟继承等的介绍

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值