C++学习记录——십칠 继承

时间:2021-04-18 01:16:18


1、概念和定义

继承是一种类层次的复用,继承中分为基类(父类)和子类(派生类),父类的一些属性,子类同样可以拥有。

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

class Person
{
public:
	void Print()
	{
		cout << "name:" << _name << endl;
		cout << "age:" << _age << endl;
	}
protected:
	string _name = "zyd";
	int _age = 21;
};

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

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

int main()
{
	Student s;
	s.Print();
	return 0;
}

可以正常打印。此时s里面就有name, age,和未初始化是随机值的stuid变量。

继承有继承的方式,比如上面代码中的public继承

C++学习记录——십칠 继承
虽然基类的private成员派生类无法访问,但可以用基类中不是私有的函数来调用私有变量。

如果不写访问限定符,那就是私有继承。

2、基类和派生类的赋值转换

如果是公有继承,可以这样写

	Student s;
	Person p = s;
	Person& rp = s;

不会发生类型转换.p = s,就是把子类当中父类的部分给赋值过去;而引用,则是子类当中父类那一部分的引用,所以rp可以修改,访问。如果是指针,就指向子类中父类那一部分。

但是现阶段父类对象不能赋值给子类对象。

赋值兼容、切割、切片

B b = d;
B* ptrb = &d;

把子类对象赋值给父类对象,把子类中父类那一部分给过去。

3、继承中的作用域

C++学习记录——십칠 继承

父子类可以有同名成员,不过真实数据由子类决定。想访问父类的内容就得显式一下

	cout << Person::_age << endl;
	cout << _age << endl;
	s.Person::Print();

父子类用重名内容时,就出现了隐藏的现象。

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

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

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

	Person& operator=(const Person& p)
	{
		cout << "Person operator=(const Person& p)" << endl;
		if (this != &p)
			_name = p._name;

		return *this;
	}

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

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

int main()
{
	Student s;
	return 0;
}

C++学习记录——십칠 继承

只是实例化,但是子类会自动调用父类的成员函数。

那子类自己写一个构造函数,调用自己的行不行

C++学习记录——십칠 继承

不行。即使显式调用_name也不行。父子类的规定就是父类的成员变量用父类的函数,子,可以这样改。

	Student(const char* name, int num)
		:Person(name)
		,_num(num)
	{}

如果在子类构造函数不写某一个变量的初始化也可以,只要父类能构造就行。

拷贝构造如果没写,那就调用父类的,写的话

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

_num是子类变量。如果这样

Person p = s1;

会调用父类的拷贝构造。赋值重载

	Student& operator=(const Student& s)
	{
		if (this != &s)
		{
			Person::operator=(s);
			_num = s._num;
		}
		return &this;
	}

所以可以看到,各分各的,有父类的那就调用父类函数,否则就调用的自己的。

现在的代码结果是这样

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

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

	Person& operator=(const Person& p)
	{
		cout << "Person operator=(const Person& p)" << endl;
		if (this != &p)
			_name = p._name;

		return *this;
	}

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

class Student : public Person
{
public:
	Student(const char* name, int num)
		:Person(name)
		,_num(num)
	{
		cout << "Student()" << endl;
	}

	Student(const Student& s)
		:Person(s)
		, _num(s._num)
	{
		cout << "Student(const Student& s)" << endl;
	}

	Student& operator=(const Student& s)
	{
		if (this != &s)
		{
			Person::operator=(s);
			_num = s._num;
		}
		cout << "Student(const Student= s)" << endl;
		return *this;
	}
protected:
	int _num;
};

int main()
{
	Student s1("asdasd", 21);
	Student s2(s1);

	Person p = s1;

	s1 = s2;
	return 0;
}

C++学习记录——십칠 继承

写上析构函数

	~Student()
	{
		Person::~Person();
		cout << "~Student()" << endl;
	}

Student s1("asdasd", 21);

C++学习记录——십칠 继承

会发现多了一个Person。如果去掉Person::Peroson就正常了,析构函数不要显式调用,编译器会自动调用。

构造时会先构造父,析构时会先析构子。

5、继承与友元

友元不能继承

class Student;
class Person
{
public:
	friend void Display(const Person& p, const Student& s);
protected:
	string _name; // 姓名
};

class Student : public Person
{
protected:
	int _stuNum; // 学号
};

void Display(const Person& p, const Student& s)
{
	cout << p._name << endl;
	cout << s._stuNum << endl;
}

int main()
{
	Person p;
	Student s;
	Display(p, s);
	return 0;
}

如果要访问,就得再定义一个友元。

6、继承与静态成员

静态变量也不能继承,但由于是静态区的,哪里都可以访问。

class Person
{
public:
	Person() { ++_count; }
	string _name;
public:
	static int _count; 
};
int Person::_count = 0;

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

class Graduate : public Student
{
protected:
 string _seminarCourse; 
};

int main()
{
	Person p;
	Student s;
	cout << &(p._name) << endl;
	cout << &(s._name) << endl;

	cout << &(p._count) << endl;
	cout << &(s._count) << endl;
}

C++学习记录——십칠 继承

7、复杂的菱形继承及菱形虚拟继承

单继承是一个子类只有一个直接父类。

多继承是一个子类有两个或以上父类。

菱形继承是多继承的一种特殊情况。

C++学习记录——십칠 继承

实际上,多继承变得更复杂了,会出现数据冗余和二义性(无法明确访问哪一个)的问题。

8、组合

虚继承写起来麻烦,需要看内存各个变量的地址,所以就不写了。去搜搜别的博客吧

C++学习记录——십칠 继承
但如果C有保护或者私有成员,D就不能有了。D可以直接用C的公有成员,间接用保护或私有成员。耦合度来讲组合不如继承,但组合更*,修改C的保护或者私有成员不会影响到D,但修改A的成员有可能全部都影响到B。但继承不可少,面向对象语言的三大特性中就有继承,其中的多态也是基于继承而存在的。

C++学习记录——십칠 继承

结束。