C++回顾 Day7

调用子类构造器之前就会调用父类构造器

cpp 复制代码
#include <iostream>
using namespace std;

class father
{
	public:
		father()
		{
			cout << "father()" << endl;
		}
	private:
		int a;
};

class son : public father
{
	public:
		son()
		{
			cout << "son()" << endl;
		}
};

class grandson : public son
{
	public:
		grandson()
		{
			cout << "grandson()" << endl;
		} 
};

int main()
{
	father f;
	cout << "---------------" << endl;
	son s;
	cout << "---------------" << endl;
	grandson g;
	return 0;
}

//结果
//father()
//---------------
//father()
//son()
//---------------
//father()
//son()
//grandson()

同时还要注意,子类只需要管好父类的构造器就可以了

cpp 复制代码
class father
{
	public:
		father(int a):a(a)
		{
			cout << "father()" << endl;
		}
	private:
		int a;
};

class son : public father
{
	public:
		son():father(4)	//father类没有无参构造所以必须显式调用
		{
			cout << "son()" << endl;
		}
};

class grandson : public son
{
	public:
		grandson()	//son类有无参构造所以不必显式调用
		{
			cout << "grandson()" << endl;
		} 
};

当然了,如果son类通过son(int a):father(a)这种传入参数的方式对father类进行初始化,并且son类不包含其他的默认构造参数,那么grandson类的构造器也必须显式调用son类的构造器

初始化顺序:父类的初始化,类对象的初始化,本类的初始化

遵循一个原则:想要初始化一个类就要先初始化其父类的全部,再初始化其类对象

继承后的拷贝构造器

子类没有自实现拷贝构造器则使用子类的默认拷贝构造器,而子类的默认拷贝构造器会调用父类的拷贝构造器

若实现了则使用自实现的

cpp 复制代码
#include <iostream>
using namespace std;

class student
{
	public:
		student(string sn,int ia,string sg):name(sn),age(ia),gender(sg){}
		student(const student &another):name(another.name)
		//写成参数列表运行快就是因为还未生成this指针就进行了赋值 
		{
			this->age = another.age;
			this->gender = another.gender;
		}
		void show()
		{
			cout << "name  :" << name << endl;
			cout << "age   :" << age << endl;
			cout << "gender:" << gender << endl;
		}
	private:
		string name;
		int age;
		string gender;
};

class graduate : public student 
{
	public:
		graduate(string sn,int ia,string sg,int ss)
			:student(sn,ia,sg),salary(ss){}
		graduate(const graduate &another)
			:student(another),salary(another.salary)
		//这里调用了 父类构造器,传入子类用来初始化父类,这是赋值兼容 
		//赋值兼容:子类的引用或指针能够赋给父类对象的引用或指针 
		{
			//this->name = another.name;
			//这是不合法的,因为name是父类的private成员,对子类是不可见的 
		}
		void print()
		{
			show();
			cout << "salary:" << salary << endl;
		}
	private:
		int salary;
};

int main()
{
	student s1("bob",18,"男");
	s1.show();
	cout << "----------------" << endl; 
	student s2 = s1;
	s2.show();
	cout << "----------------" << endl; 
	graduate g1("dick",22,"女",23000);
	g1.print();
	cout << "----------------" << endl; 
	graduate g2 = g1;
	g2.print();
	return 0;
}

继承后的赋值重载

子类没有自实现赋值重载则使用子类的默认赋值重载,而子类的默认赋值重载会调用父类的赋值重载

若实现了则使用自实现的

cpp 复制代码
#include <iostream>
using namespace std;

class student
{
	public:
		student(string sn,int ia,string sg):name(sn),age(ia),gender(sg){}
		student(const student &another):name(another.name)
		{
			this->age = another.age;
			this->gender = another.gender;
		}
		student& operator=(const student& another)
		{
			cout << "student& operator=(const student& another)" << endl;
			if(this == &another)
				return *this;
			this->name = another.name;
			this->age = another.age;
			this->gender = another.gender;
			return *this;
		}
		void show()
		{
			cout << "name  :" << name << endl;
			cout << "age   :" << age << endl;
			cout << "gender:" << gender << endl;
		}
	private:
		string name;
		int age;
		string gender;
};

class graduate : public student 
{
	public:
		graduate(string sn,int ia,string sg,int ss)
			:student(sn,ia,sg),salary(ss){}
		graduate(const graduate &another)
			:student(another),salary(another.salary){}
		graduate& operator=(const graduate& another)
		{
			if(this == &another)return *this;
			//不写也可以,因为父类赋值重载有自赋值检查,但是加上会使得代码更加健壮 
			student::operator=(another);
			//调用了父类的赋值重载,必须加作用域,
			//不然对于重名的成员,子类会把父类中重名的成员shadow掉
            //shadow与重载不同,只和参数名有关
            //同时,这里也发生了一次赋值兼容
			//这里就会导致递归,程序崩溃 
			this->salary = another.salary;
		} 
		void print()
		{
			show();
			cout << "salary:" << salary << endl;
		}
	private:
		int salary;
};

int main()
{
	student s1("bob",18,"男");
	s1.show();
	cout << "----------------" << endl; 
	student s2("111",1,"1");
	s2.show();
	cout << "----------------" << endl; 
	s2 = s1;
	s2.show();
	cout << "----------------" << endl; 
	graduate g1("dick",22,"女",23000);
	g1.print();
	cout << "----------------" << endl; 
	graduate g2("222",2,"2",2);
	g2.print();
	cout << "----------------" << endl; 
	g2 = g1;
	g2.print();
	return 0;
}

注意:

绝不要使得子类中的成员和父类中的成员同名

如果同名发现冲突,要加上作用域(命名空间)

继承后的友元函数

cpp 复制代码
#include <iostream>
using namespace std;

class father
{
	public:
		father(int _a,int _b):a(_a),b(_b){}
		friend ostream& operator<<(ostream &os,father &f);
	private:
		int a;
		int b;
};

ostream& operator<<(ostream &os,father &f)
{
	os << f.a << endl << f.b << endl;
}

class son : public father
{
	public:
		son(int _a,int _b,int _c):father(_a,_b),c(_c){}
		friend ostream& operator<<(ostream& os,son &s);
	private:
		int c; 
};

ostream& operator<<(ostream& os,son &s)//只写函数不写具体实现cout << s就不输出了,
										//因为严格匹配到了这个函数,不再赋值兼容 
{
	cout << static_cast<father&>(s);//static_cast<father>(s)的话就要
									//将之前的函数的参数变成const father &f 
	//father::operator<<(cout,s);不对 
	//友元函数并不是类的一个成员函数,而是与类相关联的一个函数
	//因此,它不能通过类的作用域解析运算符 :: 直接作为成员函数来调用
	os << s.c << endl;
}

int main()
{
	father f(1,2);
	cout << f;
	son s(3,4,5);
	//cout << s;
	//这里会输出3和4,是因为发生了一次赋值兼容 
	cout << s;
	
	return 0;
}

注意:

继承不会继承友元关系;

不能通过域解析运算符 :: 调用父类中的同名的友元函数

析构函数调用的顺序和构造函数相反

先析构子类,再析构对象成员,最后析构父类

对于不同的继承方式

99%使用的都是public继承

不同访问权限的作用

public用来提供接口

protected用来隐藏数据,传承数据

private用来隐藏数据

继承方式总结

public继承了接口,也传承了数据

protected只传承了数据

private既没有继承接口,也没有传承数据

多继承

当一个子类继承自多个父类,而父类之间又有重名的成员,便会出现冗余数据(数据保存了两份),还会给使用者带来不便利(使用前必须加上作用域(类名::))

为了解决这种问题,出现了虚基类和虚继承

就是将这两个父类中共有的东西提取出来放到一个类M里面,然后让这两个父类都虚继承自这个虚基类M。这样数据就只有一份,访问也不会有冲突

cpp 复制代码
#include <iostream>
using namespace std;

class M
{
	public:
		int m;
		M(int _m):m(_m){}
		void show()
		{
			cout << m << endl;
		}
}; 

class A : virtual public M
{
	public:
		A(int a):M(a){}
		void M_set(int x)
		{
			m = x;
		}
};

class B : virtual public M
{
	public:
		B(int b):M(b){}
		void M_get()
		{
			cout << m << endl;
		}
};

class myclass : public A,public B
{
	public:
		myclass(int x):A(2),B(3),M(x){}//这里必须把M的初始化也完成,并且A、B的初始化本质上对m的值没有影响,只是陪跑的
		void print()
		{
			cout << m << endl;
		}
};

int main()
{
//	A a(10);
//	cout << "A a(10);" << endl; 
//	B b(20);
//	cout << "B b(20);" << endl;
//	myclass c(30);
//	cout << "myclass c(30);" << endl;
//	a.M_set(100);
//	cout << "a.M_set(100);" << endl;
//	cout << "b.M_get() = ";
//	b.M_get();
//	cout << "c.print() = ";
//	c.print();
//	c.m = 999;
//	cout << "c.m = 999;" << endl;
//	cout << "c.print() = ";
//	c.print();
//	cout << "b.M_get() = ";
//	b.M_get();
	myclass t(20);
	t.show();
	t.print();
	t.M_set(10);
	t.show();
	t.print();
	t.m = 999;
	t.show();
	t.print();//由于只有M里面存着一份,所以使用A类继承过来的赋值进行赋值还是使用直接赋值都只有一份结果
	return 0;
}

注意:当同时存在A、B和myclass类时,里面的m是互不干扰的

当只有一个myclass类时,可以理解为将M、A、B的内容都聚合到了一起,只有一份存储