[TOC]
成都创新互联拥有10余年成都网站建设工作经验,为各大企业提供网站建设、成都网站设计服务,对于网页设计、PC网站建设(电脑版网站建设)、成都app开发、wap网站建设(手机版网站建设)、程序开发、网站优化(SEO优化)、微网站、域名与空间等,凭借多年来在互联网的打拼,我们在互联网网站建设行业积累了很多网站制作、网站设计、网络营销经验,集策划、开发、设计、营销、管理等网站化运作于一体,具备承接各种规模类型的网站建设项目的能力。继承基本知识
定义:
继承是面向对复用的重要手段。通过继承定义一个类,继承是类型之间的关系建模,共享公有的东西,实现各自本质不同的东西。
继承关系:
三种继承关系下基类成员的在派生类的访问关系变化(图)
举个栗子(公有继承)
```c++
class Person
{
public :
Person(const string& name)
: _name(name )
{}
void Display ()
{
cout<<_name <
protected :
string _name ; // 姓名
string _sex ;
};
class Student : public Person //公有继承
{
protected :
int _num ; // 学号
};
### 继承图例解释:
![这里写图片描述](https://img-blog.csdn.net/2018042122253862?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4NjQ2NDcw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
**私有继承和保护继承很少用到,我们重点要掌握公有继承**
### 继承与转换--赋值兼容规则--public继承
1. 子类对象可以赋值给父类对象(切割/切片)
2. 父类对象不能赋值给子类对象
3. 父类的指针/引用可以指向子类对象
4. 子类的指针/引用不能指向父类对象(可以通过强制类型转换完成)
```C++
class Person
{
public:
void Display()
{
cout << "AA" << endl;
}
protected:
string _name; // 姓名
};
class Student : public Person
{
public:
int _num; // 学号
};
int main()
{
Person a;
Student b;
a = b; //子类对象赋值给基类对象(切片)这个特性是编译器支持的
b = a; //父类对象不能赋值给子类对象
Person *p1 = &b; //特性3
//Person &a1 = b; //特性3
Student *p2 = (Student*)&a; //特性4
Student& b1 = (Student&)a; //特性4
getchar();
return 0;
}
继承体系中的作用域
- 在继承体系中基类和派生类都有独立的作用域。
- 子类和父类中有同名成员(成员函数,成员变量)子类成员将屏蔽父类对成员的直接访问。(在子类成员函数中,可以使用 基类::基类成员 访问)--隐藏(重定义)
- ==注意在实际中在继承体系里面最好不要定义同名的成员==。
class Person { public: Person(const char *name = "",int num = 0) :_name(name) ,_num(num) {}
protected:
string _name; // 姓名
int _num;
};
class Student : public Person
{
public:
Student(const char* name = "", const int num1 = 0, int num2 = 0)
:Person(name,num1)
,_num(num2)
{}
void Display()
{
cout << _num << endl;
cout <
protected:
int _num; // 学号
};
int main()
{
Person a("boday",15);
Student b("crash",1502,17);
b.Display();
return 0;
}
**运行结果:**
![这里写图片描述](https://img-blog.csdn.net/20180422193345134?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4NjQ2NDcw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
可以很明显看出此时打印的是子类的成员,而隐藏掉了父类的成员,(```这就是隐藏```)
### 派生类的默认成员函数
在继承关系里面,在派生类中如果没有显示定义这六个成员函数,编译系统则会默认合成这六个默认的成员函数。
![这里写图片描述](https://img-blog.csdn.net/20180421222558568?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4NjQ2NDcw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70)
**来个栗子说说默认成员函数的前四个(` 后两个不常用`)**
```cpp
class Person
{
public:
Person(const char *name = "",int num = 0) //父类构造函数
:_name(name)
,_num(num)
{}
~Person()//父类析构函数
{
cout << "~Person()" << endl;
}
Person(const Person& p)//父类拷贝构造函数
:_name(p._name)
,_num(p._num)
{}
Person& operator=(const Person& p)//父类赋值运算符重载
{
if (this != &p)
{
_name = p._name;
_num = p._num;
}
return *this;
}
protected:
string _name; // 姓名
int _num;
};
class Student : public Person
{
public:
Student(const char* name = "", const int num1 = 0, int num2 = 0)//子类构造函数
:Person(name,num1)
,_num(num2)
{}
~Student()//子类析构函数
{
cout << "~Student()" << endl;
}
Student(const Student& s)//子类拷贝构造函数
:Person(s)
,_num(s._num)
{}
Student& operator=(const Student& s)//子类赋值运算符重载
{
Person::operator=(s); //显示调用父类赋值运算符重载
_num = s._num;
}
protected:
int _num; // 学号
};
先调用父类构造函数,在调用基类构造函数;析构函数调用顺序与构造函数相反(先构造后析构,这个和栈的规则有关(先入后出))
继承方式(单继承,多继承,菱形继承)
1.单继承
定义:一个子类只有一个直接父类时称这个继承关系为单继承
代码示例:
class A
{
protected:
int _a;
};
class B : public A //B类 继承 A类
{
protected:
int _b;
};
2.多继承
定义:一个子类有两个或以上直接父类
时称这个继承关系为多继承
代码示例:
class A
{
protected:
int _a;
};
class B
{
protected:
int _b;
};
class C : public A,B
{
protected:
int _c;
};
3.菱形继承
代码示例:
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; // 主修课程
};
void Test()
{
// 显示指定访问哪个父类的成员(二义性问题)
Assistant a;
a.Student::_name = "xxx";
a.Teacher::_name = "yyy";//数据冗余问题
}
很明显菱形继承存在问题,存在二义性和数据冗余的问题。为了解决这个问题就引入了虚继承。
虚继承:解决菱形继承的二义性和数据冗余的问题
在声明派生类时,指定其继承方式时声明为虚继承的方式。如
class A
{
public:
int _a;
};
class B : virtual public A //声明为虚基类
{
protected:
int _b;
};
class C : virtual public A //声明为虚基类
{
protected:
int _c;
};
class D : public B,public C
{
protected:
int _d;
};
看看测试效果:
void Test()
{
D d;
d._a = 10;
}
是不是很疑惑到底是如何解决的?那就要深入到底层探索下
这里在虚继承时用一个虚基表存放偏移量,这样B和C类同时使用一个虚基表存放A相对于B和C的偏移量,当发生虚继承时A会存放在一个公共区域,这就很好的解决了二义性问题,同时也节省了空间。
虚继承很好的解决了菱形继承带来的问题。
这里建议大家写下代码调试一下,同时查看内存变化。
另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。
网站标题:C++中的继承详解-创新互联
文章分享:http://scgulin.cn/article/djpjhi.html