wap网站前台模板,北京商场打折,大理住房和城乡建设局网站,备案期间网站能打开吗目录 1.继承的概念
2.举个继承的例子
3.继承基类成员访问方式的变化
1.父类成员的访问限定符对在子类中访问父类成员的影响
2.父类成员的访问限定符子类的继承方式对在两个类外访问子类中父类成员的影响
4.继承类模版#xff08;注意事项#xff09;
5.父类与子类间的转…目录 1.继承的概念
2.举个继承的例子
3.继承基类成员访问方式的变化
1.父类成员的访问限定符对在子类中访问父类成员的影响
2.父类成员的访问限定符子类的继承方式对在两个类外访问子类中父类成员的影响
4.继承类模版注意事项
5.父类与子类间的转换
6.继承中的作用域主讲隐藏
7.派生类的默认成员函数
1.子类中的构造函数
2.子类中的拷贝构造函数
3.子类中的赋值运算符重载
4.子类中的析构函数
8.不能被继承的类
9.继承与友元
10.继承与静态成员
11.多继承及其菱形继承问题
0.简单介绍
1.单继承模型
2.多继承模型
3.菱形继承模型
4.二义性例子
5.虚继承
12.继承和组合 1.继承的概念
继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段它允许我们在保持原有类特性的基础上进⾏扩展增加⽅法(成员函数)和属性(成员变量)这样产⽣新的类称派⽣类。继承 呈现了面向对象程序设计的层次结构体现了由简单到复杂的认知过程。以前我们接触的函数层次的 复用继承是类设计层次的复用。 2.举个继承的例子
下面我们公共的成员都放到Person类中Student和teacher都继承Person就可以复用这些成员就不需要重复定义了省去了很多麻烦。
class Person
{
public:// 进⼊校园/图书馆/实验室刷⼆维码等⾝份认证 void identity(){cout void identity() _name endl;}
protected:string _name 张三; // 姓名 string _address; // 地址 string _tel; // 电话 int _age 18; // 年龄
};class Student : public Person
{
public:// 学习 void study(){// ...}
protected:int _stuid; // 学号
};class Teacher : public Person
{
public:// 授课 void teaching(){//...}
protected:string title; // 职称
}; 3.继承基类成员访问方式的变化 1.父类成员的访问限定符对在子类中访问父类成员的影响
1.父类成员public、protected修饰子类中可以访问父类的成员变量。
2.父类成员private修饰子类中不可以访问父类的成员变量。 2.父类成员的访问限定符子类的继承方式对在两个类外访问子类中父类成员的影响
1.private继承在两个类外访问不了。
2.除了private继承以外的其它继承方式
父类成员在子类中的访问方式min(成员在父类的访问限定符继承方式)其中public
protectedprivate。 4.继承类模版注意事项
namespace yx
{//用vector容器实现stack容器适配器templateclass Tclass stack : public std::vectorT{public:void push(const T x){// 基类是类模板时需要指定⼀下类域 // 否则编译报错:error C3861: “push_back”: 找不到标识符 // 因为stackint实例化时也实例化vectorint了 // 但是模版是按需实例化虽实例化vectorint了// 但vectorint中的push_back等成员函数未实例化所以找不到 vectorT::push_back(x);//push_back(x);需要指定类域实例化模版参数}void pop(){vectorT::pop_back();}const T top(){return vectorT::back();}bool empty(){return vectorT::empty();}};} 5.父类与子类间的转换 class Person
{
protected:string _name; // 姓名 string _sex; // 性别 int _age; // 年龄
};class Student : public Person
{
public:int _No; // 学号
};1.public继承的派生类对象、指针、引用可以赋值给基类的对象、指针、引用切片基类指针或引用指向的是派生类中切出来的基类那部分
2.基类对象不能赋值给派生类对象
3.基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。 Student sobj;
// 1.派⽣类对象、指针、引用可以赋值给基类的对象、指针、引⽤
Person pobj sobj;
Person* pp sobj;
Person rp sobj;//2.基类对象不能赋值给派⽣类对象这⾥会编译报错
sobj pobj;
//3.可以这么写
Student sobj;
Person* pp sobj;
Person rp sobj;
Student* sp (Student*)pp;
Student rs (Student)rp; 6.继承中的作用域主讲隐藏 // Student的_num和Person的_num构成隐藏关系可以看出这样代码虽然能跑但是非常容易混淆
class Person
{
protected:string _name ⼩轩; // 姓名 int _num 111; // ⾝份证号
};class Student : public Person
{
public:void Print(){cout 姓名: _name endl;cout ⾝份证号: Person::_num endl;cout 学号: _num endl;}
protected:int _num 999; // 学号
};int main()
{Student s1;s1.Print();return 0;
} 7.派生类的默认成员函数
在讨论子类的默认成员函数时我们只需要按照之前看默认成员函数的方式将父类中的那些成员看作是子类中的一个对象来看就可以。 1.子类中的构造函数
class Person
{
public:Person(const char* name): _name(name){cout Person() endl;}protected:string _name; // 姓名
};class Student : public Person
{
public:// 子类中默认生成的构造函数的行为// 1、内置类型-不确定// 2、自定义类型-调用默认构造// 3、继承的父类成员看做一个整体对象要求调用父类的默认构造//自己写的子类构造Student(const char* name, int num, const char* addrss):Person(name)//必须显式调用父类的构造函数, _num(num), _addrss(addrss){}protected:int _num 1; //学号string _addrss 武汉市洪山区;
}; 2.子类中的拷贝构造函数
class Person
{
public:Person(const Person p): _name(p._name){cout Person(const Person p) endl;}protected:string _name; // 姓名
};class Student : public Person
{
public:// 严格说Student拷贝构造默认生成的就够用了// 如果有需要深拷贝的资源才需要自己实现Student(const Student s)//注意这里写成了初始化列表的方式
//但请不要混淆这里并不是初始化的意思
//但如果写成了初始化列表的方式这里没有写Person(s)的话将会调用Person类的默认构造函数:Person(s)//必须显式调用父类的拷贝构造函数, _num(s._num), _addrss(s._addrss){// 深拷贝}protected:int _num 1; //学号string _addrss 武汉市洪山区;
}; 3.子类中的赋值运算符重载
class Person
{
public:Person operator(const Person p){cout Person operator(const Person p) endl;if (this ! p)_name p._name;return *this;}protected:string _name; // 姓名
};class Student : public Person
{
public:// 严格说Student赋值重载默认生成的就够用了// 如果有需要深拷贝的资源才需要自己实现Student operator(const Student s){if (this ! s){// 注意规定父类和子类的operator构成隐藏关系故这里需要指定类域。Person::operator(s);//这里要显式调用父类的operator()函数//这里的s变量传给父类的函数用到了刚才讲的父类与子类间的转换的知识_num s._num;_addrss s._addrss;}return *this;}protected:int _num 1; //学号string _addrss 武汉市洪山区;
}; 4.子类中的析构函数
class Person
{
public:~Person(){cout ~Person() endl;}protected:string _name; // 姓名
};class Student : public Person
{
public:// 严格说Student析构默认生成的就够用了// 如果有需要显示释放的资源才需要自己实现// 析构函数都会被特殊处理成destructor() ~Student(){_addrss.~string();// 注意规定子类的析构和父类析构函数也构成隐藏关系// 规定子类中不需要显示调用父类的析构子类析构函数之后会自动调用父类析构// 这样保证析构顺序先子后父显示调用取决于实现的人不能保证先子后父// 先子后父//Person::~Person();//指定类域因为隐藏}
protected:int _num 1; //学号string _addrss 国庆快乐 祖国万岁;
};8.不能被继承的类
C11新增了⼀个final关键字final修改基类派生类就不能继承了。
// C11的⽅法
class Base final
{
public:void func5() { cout Base::func5 endl; }
protected:int a 1;
}; 9.继承与友元
友元关系不能继承到子类。 10.继承与静态成员
基类定义了static静态成员则整个继承体系里面只有⼀个这样的成员。无论派生出多少个派生类都只有⼀个static成员实例。 11.多继承及其菱形继承问题 0.简单介绍
单继承⼀个派⽣类只有⼀个直接基类时称这个继承关系为单继承
多继承⼀个派⽣类有两个或以上直接基类时称这个继承关系为多继承多继承对象在内存中的模型是先继承的基类在前⾯后⾯继承的基类在后⾯派⽣类成员在放到最后⾯。
菱形继承菱形继承是多继承的⼀种特殊情况。菱形继承的问题从下⾯的对象成员模型构造可以看出菱形继承有数据冗余和⼆义性的问题在下图的Assistant的对象中Person成员会有两份。⽀持多继承就可能会有菱形继承像Java就直接不⽀持多继承规避掉了这⾥的问题所以实践中我们也是不建议设计出菱形继承这样的模型的。 1.单继承模型 2.多继承模型 3.菱形继承模型 4.二义性例子
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:string _majorCourse; // 主修课程
};int main()
{// 编译报错error C2385: 对“_name”的访问不明确 Assistant a;//a._name peter;会报错// 需要显⽰指定访问哪个基类的成员可以解决⼆义性问题但是数据冗余问题⽆法解决 a.Student::_name xxx;a.Teacher::_name yyy;return 0;
} 5.虚继承
class Person
{
public:string _name; // 姓名
};// 使⽤虚继承Person类
class Student : virtual public Person
{
protected:int _num; //学号
};// 使⽤虚继承Person类
class Teacher : virtual public Person
{
protected:int _id; // 职⼯编号
};// 教授助理
class Assistant : public Student, public Teacher
{
protected:string _majorCourse; // 主修课程
};int main()
{// 使⽤虚继承可以解决数据冗余和⼆义性 Assistant a;a._name peter;return 0;
} 举个例子 注意谁有数据冗余和二义性就在继承它的时候加上virtual使用虚继承可以解决数据冗余和⼆义性
由上图可以看出A有数据冗余和二义性所以就在继承A的位置B和C加上virtual 12.继承和组合