类的继承
继承是代码重用的一种方法。
通过继承创建的新类称为“派生类”或“子类”,被继承的类称为“基类”或“父类”。
继承即子类无需重新编写父类成员代码的情况下继承父类所有的成员,子类只需书写新增成员的代码
语法:
class 子类名 : 继承方式 父类名1 , 继承方式 父类名2 , ..... , 继承方式 父类名n
{
派生类的成员
};
private:1.该类中的函数、2.友元函数访问。不能被任何其他访问,该类的对象也不能访问。
protected:1.该类中的函数、2.子类的函数、3.友元函数访问。但该类的对象不能访问。
public:1.该类中的函数、2.子类的函数、3.其友元函数,4.该类的对象访问
类的继承 举例
举例:
#include<iostream>
using namespace std;
class Student
{
int id;
string name;
int age;
public:
void SetIn(int _id,string _name,int _age)
{
id = _id;
name = _name;
age = _age;
}
void Print()
{
cout<<"id:"<<id<<endl<<"name:"<<name<<endl<<"age:"<<age<<endl;
}
};
class Graduate:public Student //继承
{
string degree; //新增数据成员
public:
void SetIn_G(int _id,string _name,int _age,string _degree)
{
SetIn(_id,_name,_age); //调用父类成员函数
degree = _degree;
}
void Print() //与父类成员函数同名
{
Student::Print(); //调用父类成员函数(因为同名,所以需要加作用域)
cout<<"degree:"<<degree<<endl;
}
void Print_G()
{
Student::Print();//Print(); 调用父类成员函数(不同名,所以直接调用)
//但是跟上面 void Print() 撞名了,所以必须要加作用域,实际上可以用注释 的 Print()
cout<<"degree:"<<degree<<endl;
}
};
int main()
{
Graduate G1;
G1.SetIn_G(1,"张三",20,"计算机"); //调用子类的成员函数
G1.Print(); //调用子类的打印成员函数(默认父类打印函数被隐藏)
cout<<endl;
G1.Student::Print(); //调用父类的打印函数
cout<<endl;
G1.Print_G(); //调用不同名的子类打印函数(直接调用)
return 0;
}
总结:
1、继承中,先调用父类构造函数,再调用子类构造函数,析构函数顺序与构造函数相反
2、若父类与子类成员函数同名,子类直接调用则是子类中的同名函数,子类加作用域才能访问父类同名函数,格式:子类名.父类名::同名函数;
菱形继承
菱形继承概念:
两个子类继承同一个父类
又有某个类同时继承着两个派生类
这称为菱形继承或钻石继承
菱形继承的问题
1、狮子继承了动物的数据,老虎继承了动物的数据,当狮虎兽使用数据时,就会产生二义性
二义性:指无法直接通过变量名进行读取,需要通过域(::)成员运算符进行区分
2、狮虎兽继承了来自动物的数据且继承了两份,其实只需要一份
举例:
#include<iostream>
using namespace std;
class Animal
{
public:
int age;
};
class Sheep : public Animal
{
};
class Camel : public Animal
{
};
class Alpace : public Sheep , public Camel
{
d
};
void test()
{
Alpace a;
//当菱形继承,两个父类拥有相同数据,需要用作用域区分
a.Sheep::age = 20;
a.Camel::age = 1;
cout<<"a.Sheep:"<<a.Sheep::age<<endl;
cout<<"a.Camel:"<<a.Camel::age<<endl;
}
int main()
{
test();
return 0;
}
明明只需要一个age成员函数,但是却出现两个,造成浪费
解决菱形继承问题的办法是 虚继承
虚继承
虚继承是指在定义派生类时,通过关键字virtual来强制指定所继承的基类为虚基类
语法:
class 子类 : virtual 继承方式 父类
{
子类新增的数据成员和成员函数
}
举例
#include<iostream>
using namespace std;
class Animal
{
public:
int age;
};
class Sheep :virtual public Animal
{
};
class Camel :virtual public Animal
{
};
class Alpace : public Sheep , public Camel
{
};
void test()
{
Alpace a;
a.Sheep::age = 20;
cout<<"a.Sheep:"<<a.Sheep::age<<endl;
cout<<"a.Camel:"<<a.Camel::age<<endl;
a.Camel::age = 1;
cout<<"a.Sheep:"<<a.Sheep::age<<endl;
cout<<"a.Camel:"<<a.Camel::age<<endl;
a.Camel::age ++;
cout<<"a.Sheep:"<<a.Sheep::age<<endl;
cout<<"a.Camel:"<<a.Camel::age<<endl;
}
int main()
{
test();
return 0;
}
虽然继承了两个相同的数据,但是这两个相同的数据的地址是相同的
#include<iostream>
using namespace std;
class Person
{
int no;
string name;
public:
void Set_P(int _no,string _name) //构造函数不能是虚函数
{
no = _no;
name = _name;
}
void Print()
{
cout<<"编号:"<<no<<endl;
cout<<"姓名:"<<name<<endl;
}
};
class Student:virtual public Person
{
protected:
int grade;
int score;
public:
void Set_S(int _grade,int _score,int _no,string _name)
{
Set_P(_no,_name);
grade = _grade;
score = _score;
}
void Print()
{
Person::Print();
cout<<"年级:"<<grade<<endl;
cout<<"成绩:"<<score<<endl;
}
};
class Workers:virtual public Person
{
protected:
string title;
string department;
public:
void Set_W(int _no,string _name,string _title,string _department)
{
Set_P(_no,_name);
title = _title;
department = _department;
}
void Print()
{
Person::Print();
cout<<"职称:"<<title<<endl;
cout<<"部门:"<<department<<endl;
}
};
class Opstudent:public Student,public Workers
{
public:
void Set_O(int _grade,int _score,int _no,string _name,string _title,string _department)
{
Set_W(_no,_name,_title,_department);
Set_S(_grade,_score,_no,_name);
}
void Print()
{
Person::Print();
cout<<"年级:"<<grade<<endl;
cout<<"成绩:"<<score<<endl;
cout<<"职称:"<<title<<endl;
cout<<"部门:"<<department<<endl;
}
};
int main()
{
Student s1;
s1.Set_S(1,100,1,"张三");
s1.Print();
cout<<endl;
Workers w1;
w1.Set_W(1,"张三","组长","营销");
w1.Print();
cout<<endl;
Opstudent o1;
o1.Set_O(1,100,1,"张三","组长","营销");
o1.Print();
return 0;
}
详细规则请看 虚函数与纯虚函数