《C++ 继承详解:从基础到类模板继承,一篇就够了》
1.继承的概念:继承机制是面向对象程序设计使代码可以复用的最重要的⼿段,它允许我们在保持原有类特性的基础上进行扩展,增加方法(成员函数)和属性(成员变量),这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的函数层次的复⽤,继承是类设计层次的复用
问题:我们看到没有继承之前我们设计了两个类Student和Teacher,Student和Teacher都有姓名/地址/电话/年龄等成员变量,都有identity身份认证的成员函数,设计到两个类里面就是冗余的。当然他们也有⼀些不同的成员变量和函数,比如老师独有成员变量是职称,学生的独有成员变量是学号;学⽣的独有成员函数是学习,老师的独有成员函数是授课。
解决方案:下⾯我们公共的成员都放到Person类中,Student和teacher都继承Person,就可以复⽤这些成员,就不需要重复定义了,省去了很多麻烦。
2.继承方式有三种
public继承
protected继承
private继承
3访问方式有三种
public访问
protected访问
private访问
4.所以这两货组成方式有3*3=9种方式
5.首先,一般常用的都是公有继承,派生类在类外可以调用基类的公有成员函数
在类里面可以使用基类的保护成员变量跟成员函数
6.之前的是普通类继承,现在的是类模板继承
要记得,模板要实例化才能使用内部的东西
模板没有实例化,内部的东西就跟不存在一样
实例化一个类的时候,我看你调用哪个函数,我就实例化哪个成员函数,这个叫按需实例化
我们之前的普通的类,要用直接调,不需要指定类域,类模板需要的是“指定模板参数()”来实现实例化
7.
7.1通常情况下我们把⼀个类型的对象赋值给另⼀个类型的指针或者引⽤时,存在类型转换,中间会产⽣临时对象,所以需要加const,如:int a = 1; const double& d = a; public继承中,就是⼀个特殊处理的例外,派⽣类对象可以赋值给基类的指针/基类的引⽤,而不需要加const,这⾥的指针和引⽤绑定是派⽣类对象中的基类部分,如下图所示。也就意味着⼀个基类的指针或者引⽤,可能指向基类对象,也可能指向派⽣类对象
7.2派⽣类对象赋值给基类对象是通过基类的拷贝构造函数或者赋值重载函数完成的,这个过程就像派⽣类⾃⼰定义部分成员切掉了⼀样,所以也被叫做切割或者切⽚,如下图中所⽰
7.3基类对象不能赋值给派⽣类对象
7.4基类的指针或者引⽤可以通过强制类型转换赋值给派⽣类的指针或者引⽤。但是必须是基类的指针是指向派⽣类对象时才是安全的。这⾥基类如果是多态类型,可以使⽤RTTI(Run-TimeTypeInformation)的dynamic_cast 来进⾏识别后进⾏安全转换。(ps:这个我们后⾯类型转换章节再单独专⻔讲解,这⾥先提⼀下)
8.继承中的作用域
隐藏规则:
8.1. 在继承体系中基类和派⽣类都有独⽴的作⽤域。
8.2. 派⽣类和基类中有同名成员,派⽣类成员将屏蔽基类对同名成员的直接访问,这种情况叫隐藏。(在派⽣类成员函数中,可以使⽤基类::基类成员显⽰访问)
8.3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
8.4. 注意在实际中在继承体系⾥⾯最好不要定义同名的成员。
9.派生类的默认成员函数
6个默认成员函数,默认的意思就是指我们不写,编译器会变我们⾃动⽣成⼀个,那么在派⽣类中,这几个成员函数是如何⽣成的呢?
10.实现⼀个不能被继承的类
方法1:基类的构造函数私有,派⽣类的构成必须调⽤基类的构造函数,但是基类的构成函数私有化以后,派生类看不见就不能调用了,那么派生类就无法实例化出对象。
方法2:C++11新增了⼀个final关键字,final修改基类,派生类就不能继承了。
11.继承跟友元
基类的友元,派生类是不能使用的。友元关系不能继承,也就是说基类友元不能访问派⽣类私有和保护成员
12.静态成员变量
// 这里的运行结果可以看到非静态成员_name的地址是不一样的
// 说明派生类继承下来了,父派生类对象各有一份
//静态成员:所有对象共享一份 p._count 和 s._count 操作的是同一个变量
13.多继承,虚继承
class Person
{
public:
string _name; // 姓名
};
class Student : virtual public Person
{
protected:
int _num; //学号
};
class Teacher : virtual public Person
{
protected:
int _id; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected:
string _majorCourse; // 主修课程
};


//静态成员变量
class Person
{
public:
string _name = "shasha";
static int _count;
};
int Person::_count = 0;
class Student : public Person
{
protected:
int _stuNum;
};
int main()
{
Person p;
Student s;
// 这里的运行结果可以看到非静态成员_name的地址是不一样的
// 说明派生类继承下来了,父派生类对象各有一份
cout << &p._name << endl;
cout << &s._name << endl;
cout << &p._count << endl;
cout << &s._count << endl;
// 公有的情况下,父派生类指定类域都可以访问静态成员
cout << Person::_count << endl;
cout << Student::_count << endl;
return 0;
}
test.cpp
template<class T>
class stack : public std::vector<T>
{
public:
void push(const T& x)
{
// 基类是类模板时,需要指定⼀下类域,
// 否则编译报错:error C3861: “push_back”: 找不到标识符
// 因为stack<int>实例化时,也实例化vector<int>了
// 但是模版是按需实例化,push_back等成员函数未实例化,所以找不到
vector<T>::push_back(x);
//push_back(x);
}

总结就是他们都有人的特征。
#include<iostream>
#include<vector>
using namespace std;
//class Person
//{
//public:
// //初始化
// //修改
//
// //进入校园/图书馆/实验室刷二维码等身份认证
// void identity()
// {
// cout << "void identity()"<< _name << endl;
// }
////protected:
//private: //不可见
// string _name = "张三"; //姓名
// string _address; //地址
// string _tell; //电话
// int _age = 18; //年龄
//};
//
//
//class Student : public Person
//{
//public:
// //学习
// void study()
// {
//
// }
//
//protected:
// int _stuid; //学号
//};
//
//class Teacher :public Person
//{
//public:
// //授课
// void teaching()
// {
//
// }
//
//protected:
// string title; //职称
//};
//
//
//
//int main()
//{
// Student s;
// //s._name; 错误写法,不可访问
//
// Teacher t;
// s.identity();
// t.identity();
//
// return 0;
//}
//继承
//组合
//namespace shasha
//{
// //template<class T>
// //class vector
// //{};
// // stack和vector的关系,既符合is-a,也符合has-a
// template<class T>
// class stack : public vector<T>
// {
// public:
// void push(const T& x)
// {
// // 基类是类模板时,需要指定一下类域,
// // 否则编译报错:error C3861: “push_back”: 找不到标识符
// // 因为stack<int>实例化时,也实例化vector<int>了
// // 但是模版是按需实例化,push_back等成员函数未实例化,所以找不到
// vector<T>::push_back(x);
// //push_back(x);
// }
//
// void pop()
// {
// vector<T>::pop_back();
// }
//
// const T& top()
// {
// return vector<T>::back();
// }
//
// bool empty()
// {
// return vector<T>::empty();
// }
// };
//
//}
// int main()
// {
// shasha::stack<int> st;
// st.push(1);
// st.push(2);
// st.push(3);
// st.push(4);
// while (!st.empty())
// {
// cout << st.top() << " ";
// st.pop();
// }
// cout << endl;
// return 0;
// }
//class Person
//{
//public:
// string _name = "张三"; //姓名
// string _sex; //性别
// int _age; //年龄
//};
//
//class Student : public Person
//{
//public:
// int _No; //学号
//};
//
////有关类型转换
//int main()
//{
// int a = 1;
// const double& d = a;
//
// string s1 = "11111";
// const string& s2 = "11111";
//
// Student sobj;
// //1.派生类对象可以赋值给基类的指针/引用
// Person* pp = &sobj;
// Person& rp = sobj;
// rp._name = "李四";
//
// // 生类对象可以赋值给基类的对象是通过调用后面会讲解的基类的拷贝构造完成的
// //Person pobj = sobj;
//
// return 0;
//
//}
//class Person
//{
//protected:
// string _name = "小李子"; // 姓名
// int _num = 111; // 身份证号
//};
//
//class Student : public Person
//{
//public:
// void Print()
// {
// cout << " 姓名:" << _name << endl;
// cout <<" 学号:"<< _num << endl;
// cout <<" 身份证号:"<< Person::_num << endl;
// }
//protected:
// int _num = 999; // 学号
//};
//
//int main()
//{
// Student s1;
// s1.Print();
//
// return 0;
//};
//class A
//{
//public:
// void fun()
// {
// cout << "func()" << endl;
// }
//};
//
//class B : public A
//{
//public:
// void fun(int i)
// {
// cout << "func(int i)" << i << endl;
// }
//};
//
//int main()
//{
// B b;
// b.fun(10);
// b.A::fun();
//
// return 0;
//};
//基类的构造 拷贝构造 赋值重载
//class Person
//{
//public:
// Person(const char* name)
// : _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
//{
// // 默认成员函数 - 规则高度相似
// // 两份部分分开处理:
// // 1、基类成员(整体,调用基类构造)
// // 2、派生类成员(跟类和对象一样)
//public:
// Student(const char* name, int num, const char* address)
// : Person(name)
// ,_num(num)
// ,_address(address)
// {
// // 一般都要自己写
// }
//
// Student(const Student& s)
// :Person(s)
// ,_num(s._num)
// ,_address(s._address)
// {
// // 编译默认生成的就够用了
// // 存在深拷贝时,才自己写
// }
//
// Student& operator=(const Student& s)
// {
// // 编译默认生成的就够用了
// // 存在深拷贝时,才自己写
//
// if (this != &s)
// {
// Person::operator=(s);
// _num = s._num;
// _address = s._address;
// }
//
// return *this;
// }
//
// // 析构函数名字因为后续多态(重写)章节原因,会被处理成destructor
// // 所以派生类和基类析构构成隐藏关系
// ~Student()
// {
// // Person::~Person();
// } // 自动调用父类析构, 才能保证先子后父的析构顺序
//
// // 派生类析构调用后,会自动调用父类析构,所以自己实现析构时不需要显示调用
// // 构造初始化,先父类后子。析构清理资源,先子后父。
//protected:
// int _num; //学号
// string _address;
//};
//
//
// int main()
// {
// Student s1("张三", 1, "西安曲江");
// Student s2(s1);
//
// /*Student s3("李四", 2, "西安高新");
// s1 = s3;*/
//
// return 0;
// }
//class Base final
//{
//public:
// void func5() { cout << "Base::func5" << endl; }
//protected:
// int a = 1;
//private:
// //// C++98的方法
// //Base()
// //{}
//};
//
//class Derive :public Base
//{
// void func4() { cout << "Derive::func4" << endl; }
//protected:
// int b = 2;
//};
//
//
//int main()
//{
// //Derive d;
//
// return 0;
//}
//基类的友元不是派生类的友元
// 前置声明
//class Student;
//
//class Person
//{
//public:
// friend void Display(const Person& p, const Student& s);
//protected:
// string _name = "沙沙"; // 姓名
//};
//
//class Student : public Person
//{
// friend void Display(const Person& p, const Student& s);
//protected:
// int _stuNum = 1; // 学号
//};
//
//void Display(const Person& p, const Student& s)
//{
// cout << p._name << endl;
// cout << s._stuNum << endl;
//}
//
//int main()
//{
// Person p;
// Student s;
// // 编译报错:error C2248: “Student::_stuNum”: 无法访问 protected 成员
// // 解决方案:Display也变成Student 的友元即可
// Display(p, s);
//
// return 0;
//}
//静态成员变量
//class Person
//{
//public:
// string _name = "shasha";
// static int _count;
//};
//
//int Person::_count = 0;
//
//class Student : public Person
//{
//protected:
// int _stuNum;
//};
//
//int main()
//{
// Person p;
// Student s;
//
// // 这里的运行结果可以看到非静态成员_name的地址是不一样的
// // 说明派生类继承下来了,父派生类对象各有一份
// cout << &p._name << endl;
// cout << &s._name << endl;
//
// cout << &p._count << endl;
// cout << &s._count << endl;
//
// // 公有的情况下,父派生类指定类域都可以访问静态成员
// cout << Person::_count << endl;
// cout << Student::_count << endl;
//
// return 0;
//}
//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;
//}
//class Person
//{
//public:
// string _name; // 姓名
//};
//
//class Student : virtual public Person
//{
//protected:
// int _num; //学号
//};
//
//class Teacher : virtual public Person
//{
//protected:
// int _id; // 职工编号
//};
//
//class Assistant : public Student, public Teacher
//{
//protected:
// string _majorCourse; // 主修课程
//};
//
//// 数据冗余 二义性 -》virtual继承 --相当于把Student跟Teacher的Person划分出来,一般放在开头或者结尾,也就是偏移,等到a初始化的时候,只有Person调用了他的构造函数,Stedent跟Teacher在调用构造时,遇到Person会直接跳过,不去调用他的构造函数,此时所有_name都属于同一个,相当于是引用把
//int main()
//{
// // 编译报错:error C2385: 对“_name”的访问不明确
// Assistant a;
// a._name = "peter";
//
// // 由于虚继承,现在所有_name都相同了,也就是改了一个,所有都改
// a.Student::_name = "xxx";
// a.Teacher::_name = "yyy";
//
// return 0;
//}
//class Person
//{
//public:
// Person(const char* name)
// :_name(name)
// {}
// string _name; // 姓名
//};
//
//class Student : virtual public Person
//{
//public:
// Student(const char* name, int num)
// :Person(name)
// , _num(num)
// {}
//protected:
// int _num; //学号
//};
//
//class Teacher : virtual public Person
//{
//public:
// Teacher(const char* name, int id)
// :Person(name)
// , _id(id)
// {}
//protected:
// int _id; // 职工编号
//};
//
//// 不要去玩菱形继承
//class Assistant : public Student, public Teacher
//{
//public:
// Assistant(const char* name1, const char* name2, const char* name3)
// :Person(name3)
// , Student(name1, 1)
// , Teacher(name2, 2)
// , _majorCourse("xxxx")
// {}
//protected:
// string _majorCourse; // 主修课程
//};
//
//int main()
//{
// // 思考一下这里a对象中_name是"张三", "李四", "王五"中的哪一个?
// Assistant a("张三", "李四", "王五");
//
// return 0;
//}
更多推荐

所有评论(0)