精品课程网站,微信开发者工具安装教程,共青城网站建设,嘉兴做网站费用1. 继承的简单介绍
1.1 继承的概念 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段#xff0c;它允许我们在保持原有类特性的基础上进行扩展#xff0c;增加方法(成员函数)和属性(成员变量)#xff0c;这样产生新的类#xff0c;称派生类。 继承呈…1. 继承的简单介绍
1.1 继承的概念 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段它允许我们在保持原有类特性的基础上进行扩展增加方法(成员函数)和属性(成员变量)这样产生新的类称派生类。 继承呈现了面向对象程序设计的层次结构体现了由简单到复杂的认知过程。以前我们接触的函数层次的复用继承是类设计层次的复用。 简单来说被继承的类叫做父类或基类继承自父类的类叫做子类或派生类。
子类拥有父类的所有成员在此基础之上可以对父类进行拓展。
1.2 子类的定义方式
class 子类名 : 访问限定符 父类名
{// 拓展内容
}
通常来说父类和子类具有类别上的包含关系。
例如老师和同学不仅具有人的基本特点还在人的基础之上有了自己的拓展而老师和同学都属于人。
我们在用C进行描述的时候就可以将老师和同学设计成人的子类
人类父类
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; // 职称
};
1.3 继承方式
访问限定符限定的是继承的方式不同访问限定符下的继承方式如下
类成员 / 继承方式publicprotectedprivate父类的public成员子类的public成员子类的protected成员子类的private成员父类的protected成员子类的protected成员子类的protected成员子类的private成员父类的private成员子类无法直接显式访问子类无法直接显式访问子类无法直接显式访问 子类成员的访问权限public protected private 父类中被修饰为private 其中protected访问限定符是伴随着继承的出现而出现的。被它修饰的成员意味着无法在类外部进行访问而可以在类内部或其子类中被访问。
无论子类以何种方式继承在父类中被修饰为private的成员子类都不可直接显式访问。
继承方式用于限定继承下来的成员访问权限不能高于继承方式。
当不指定继承方式时class子类默认为private方式继承struct子类默认为public方式继承。
在实际运用中一般使用都是public继承几乎很少使用protetced/private继承也不提倡使用protetced/private继承因为protetced/private继承下来的成员都只能在派生类的类类里使用实际中扩展维护性不强。
可以采用protected/private继承的一个例子
#includevector
#define CONTAINER std::vectortemplateclass T
class stack : private CONTAINERT
{
public:void push(const T x){CONTAINERT::push_back(x);}void pop(){CONTAINERT::pop_back();}const T top(){return CONTAINERT::back();}bool empty(){return CONTAINERT::empty();}
};int main()
{stackint st;st.push(1);st.push(2);st.push(3);while (!st.empty()){cout st.top() ;st.pop();} return 0;
}
这里采用了继承vector的方式来实现stack但我希望用户只用stack的接口而不直接访问vector的成员函数此时就可以采取protected/private的继承方式。
1.4 继承类模板
相信细心的小伙伴已经发现了在上面stack的例子中我们每次调用vector的接口时都对其类域进行了指定否则会发生编译报错 error C3861: “push_back”: 找不到标识符 这是因为模板是按需实例化的当类模板中的函数没有被调用时其就不会实例化。
我们在实例化stackT对象时vectorT对象也跟着实例化。但是vectorT中只有成员变量和构造函数被实例化了。
我们在调用push_back()函数时由于this指针为stackT类型所以编译器并不会将vector模板中的push_back()函数实例化而是直接去寻找其定义或声明。
所以继承模板类时调用父类函数要注意指定类域。
2. 父类和子类的转换 1. public继承的子类对象可以赋值给父类的指针 / 基类的引用。这里有个形象的说法叫切片或者切割。寓意把子类中父类那部分切出来父类指针或引用指向的是子类中切出来的父类那部分。 2. 父类对象不能赋值给子类对象。 3. 父类的指针或者引用可以通过强制类型转换赋值给子类的指针或者引用。但是必须是父类的指针是指向子类对象时才是安全的。这里父类如果是多态类型可以使用RTTI(Run-Time TypeInformation)的dynamic_cast 来进行识别后进行安全转换 class Person
{
protected:string _name; // 姓名string _sex; // 性别int _age; // 年龄
};class Student : public Person
{
public:int _No; // 学号
};int main()
{Student sobj;// 1.派⽣类对象可以赋值给基类的指针/引⽤Person* pp sobj;Person rp sobj;// ⽣类对象可以赋值给基类的对象是通过调⽤后⾯会讲解的基类的拷⻉构造完成的Person pobj sobj;//2.基类对象不能赋值给派⽣类对象这⾥会编译报错sobj pobj;return 0;
} 3. 继承中的作用域及隐藏规则
隐藏规则 1. 在继承体系中父类和子类都有独立的作用域。2. 子类和基类中有同名成员子类成员将屏蔽父类对同名成员的直接访问这种情况叫隐藏。在子类成员函数中可以使用父类::父类成员显式访问 // 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;
}; 这里程序运行的结果为 3. 需要注意的是如果是成员函数的隐藏只需要函数名相同(分别在父类和子类中定义的函数只会触发隐藏而不会触发函数重载)就构成隐藏。4. 注意在实际中在继承体系里面最好不要定义同名的成员 class A
{
public:void fun(){cout func() endl;}
};class B : public A
{
public:void fun(int i){cout func(int i) i endl;}
};
此时B中的fun函数会隐藏掉A中的fun函数而不会构成重载。
4. 子类的默认成员函数
子类的默认成员函数主要是用于处理父类没有的成员成员变量父类自身的成员交由其自己的默认成员函数去处理。
4.1 构造函数
子类在构造函数的初始化列表中可以显式调用父类的构造函数对父类成员变量进行初始化若未显式调用则会调用父类的默认构造函数。
如果没有显式调用父类的构造函数且父类没有默认构造函数那么此时就会发生报错。
显式调用父类构造函数的方式是
父类名(参数列表)
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;
}
4.2 赋值重载
必须要在子类的operator中显式调用父类的operator才能按照预期正常的对父类成员进行拷贝否则只会完成浅拷贝。
要注意子类的operator会隐藏父类的operator调用父类的operator需要指定类域。
Student operator (const Student s)
{cout Student operator (const Student s) endl;if (this ! s){// 构成隐藏所以需要显⽰调⽤Person::operator (s);_num s._num;} return* this;
}
4.3 析构函数
父类的析构函数会在子类的析构函数被调用之后自动调用无需显式调用。