1.继承的概念:代码的复用
继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用。
2.如何继承呢?
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
void print()
{
cout << _name << endl;
}
protected:
string _name = "djh";
};
class Student : public Person//public决定继承的方式
{
protected:
int _number = 99;// 学号
};
int main()
{
Person p;
Student s;
s.print();
return 0;
}
我们把Person叫做基类或者是父类,Student是继承自Person的所以他叫派生类或者子类。
继承之后父类的成员变量和成员函数都变成了子类的一部分,复用就是通过这种方式完成的。
我们从上面可以看到继承Person之前有一个访问限定符public。他决定了继承的方式。
和我们的访问限定符一样有三种。
三种继承方式和我们三种访问限定符组合就形成了冗余的九种变化。
常用的就是public继承。
3.继承中的切割问题
什么是继承中的切割问题呢,就是我们将子类赋值给父类的对象或指针或者引用,就相当于把子类切分了
int main()
{
Person p;
Student s;
p = s;
Person *pp = &s;
Person &sp = s;
return 0;
}
当我们把子类赋值给父类的时候那么只把从父类那继承的那部分赋值过去,而自己的那部分则不会赋值过去,所以可以称为把子类切割给父类。
可以用最上面的代码作为例子,此时会将_name 赋值给父类,但是学号number则不会赋值过去。
我们需要注意的是,父类的对象不能赋值给子类,除非经过强转,但强转会存在越界访问的问题
int main()
{
Person p;
Student s;
s = p; //错误
Student *s1 = (Student*)&p;//可以但是会存在越界访问的问题
return 0;
}
4.继承中的隐藏
什么又是隐藏呢?
我们先回顾一下函数的重载,就是同名函数参数或返回值不同,在同一作用域下即可构成函数重载,但是子类和父类是两个不同的作用域,如果我们依然出现同名函数,他不能构成重载,此时子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。
这时候我们无法在子类中直接访问父类的成员,要想访问的话必须显示的进行访问, 父类::成员
class Student : public Person
{
public:
void print()
{
Person::print();//显示调用
cout << _number << endl;
}
protected:
int _number = 99;// 学号
};
5.子类中的默认函数
父类如果不写默认函数,编译器会自动帮我们生成,那么子类呢?有以下六点
1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
2.派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
3. 派生类的operator=必须要调用基类的operator=完成基类的复制。
4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类
对象先清理派生类成员再清理基类成员的顺序。
5. 派生类对象初始化先调用基类构造再调派生类构造。
6. 派生类对象析构清理先调用派生类析构再调基类的析构。
用代码说来说明这六点
class Person
{
public:
Person(string name) :_name(name)
{
cout << "Preson" << endl;
}
Person(const Person &p) :_name(p._name)
{
cout << "Person copy" << endl;
}
Person& operator=(const Person& p)
{
if (this != &p)
{
cout << "Person = " << endl;
_name = p._name;
}
return *this;
}
~Person()
{
cout << "~Person" << endl;
}
protected:
string _name = "djh";
};
class Student : public Person
{
public:
Student(string name, int number) :_number(number), Person(name)
{
cout << "Studnet" << endl;
}//子类的构造必须完成父类的初始化
Student(const Student &s) :_number(s._number), Person(s)//必须完成父类的拷贝初始化
{
cout << "Studnet copy" << endl;
}
Student& operator(const Student &s)//子类的operator= 要调用父类的operator=
{
if (this != &s)
{
Person::operator=(s);
cout << "Student = " << endl;
_number = s._number;
}
return *this;
}
~Student()
{
cout << "~Studnet" << endl;
}
protected:
int _number = 99;// 学号
};
根据以上六点我们就可以设计出一个无法被继承的类出来
将构造函数私有化就无法被继承了。 在C++11中也提供了一个关键字 final 一旦类后面加上这个关键字就代表禁止继承