面向对象的三大基本特性是:封装、继承、多态,本文会详细介绍一下C++中继承的实现。
一、继承体系
继承是面向对象复用的一个手段,通过继承定义一个类,进行类型之间的关系建模,共享公有的东西,各自实现本质不同的东西。
举一个简单例子:
#include<iostream>
#include<string.h>
using namespace std;
class Person
{
public:
Person(const string&name)
:_name(name)
{}
protected:
string _name;
};
class Student:public Person
{
protected:
int _id;
};
int main()
{
string _name ;
int _id;
}
此处:
三种继承关系下,父类在子类的访问关系变化:
总结:
1.基类的私有成员在派生类中是不能被访问的,如果一些基类成员不想被基类对象访问,但需要在派生类中访问,就定义为保护成员。由此可得:保护成员限定符是因继承关系才出现的。
2.public继承是一个接口继承,保持is-a原则,每个父类可用的成员对子类也可用,因为每一个子类对象也是一个父类对象。
3.protected/private继承是一个实现继承,基类的部分成员并未完全成为派生类接口的一部分,是has-a的原则,所以一般情况下不会使用这两种继承关系。
4.无论哪种继承方式,在派生类内部都可以访问基类的公有成员和保护成员,基类的私有成员在派生类中不可见。
5.使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,最好显示地写出继承方式。
6.实际应用中一般使用public继承,很少使用protected/private继承。
继承与转换---赋值兼容性规则(在public情况下)
1.子类对象可以赋值给父类对象(切割/切片)。
2.子类的指针/引用不可以指向父类对象(可以通过强制转换完成)。
3.父类对象不能赋值给子类对象。
4.父类的指针/引用可以指向子类对象。
请看下例:
#include<iostream>
#include<string.h>
using namespace std;
class Person
{
public:
Person(const int age=5)
{
_age = age;
}
void Display()
{
cout << _age << endl;
}
private:
int _age;
};
class Student:public Person
{
public:
Student(int num=0)
:_num(num)
{}
private:
int _num;
};
int main()
{
Student s;
Person p;
s.Display();
/*p = s;*/ //子类对象可以赋值给父类对象
//s = p;//父类对象赋值给子类,编译器报错
//Person *p1 = &s;//父类指针可以指向子类
//Student *p1 = &p;//子类指针不能直接指向父类
Student *p2 = (Student*)&p;//子类的指针/引用可以通过强制转换指向父类对象
return 0;
}
各种情况运行结果:
继承体系中的作用域
1.在继承体系中父类和子类都有独立的作用域
2.子类和父类中有同名成员,子类将屏蔽父类对成员的直接访问。(在子类体系中,可以使用 基类::基类成员 进行访问)——隐藏/重定义
3.在实际应用中继承体系最好不要定义同名成员。
同名实例:
#include<iostream>
#include<string.h>
using namespace std;
class Person
{
public:
Person(int age = 5, int num = 0)
{
_age = age
, _num = num;
}
void Display()
{
cout << _num << endl;
}
private:
int _age;
int _num;
};
class Student :public Person
{
public:
Student(int num = 4)
:_num(num)
{}
void Display()
{
cout << _num << endl;
}
private:
int _num;
};
int main()
{
Student s(8);
Person p(4, 4);
return 0;
}
运行结果:
二、继承体系中的派生类
派生类的默认成员函数
在继承体系中,如果派生类没有明显定义这6个成员函数,则编译器会自动合成这6个默认的成员函数。
完整示例:
#include<iostream>
#include<string.h>
using namespace std;
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
{
public:
Student(const char*name, int num)
:Person(name)
, _num(num)
{
cout << "Student()" << endl;
}
Student(const Student&s)
:Person(s)
, _num(s._num)
{
cout << "Student()" << endl;
}
Student(const Student&s)
:Person(s)
, _num(s._num)
{
cout << "Student(const Student&s)" << endl;
}
Student& operator=(const Student&s)
{
cout << "Student&operator=(const Student&s)" << endl;
if (this != &s)
{
Person::operator=(s);
_num = s._num;
}
return *this;
}
~Student()
{
cout << "~Student()" << endl;
}
private:
int _num;
};
void Test()
{
Student s1("jack", 18);
Student s2(s1);
Student s3("rose", 17);
s1 = s3;
}
三、多种继承形式:单继承&多继承&菱形继承
单继承:一个子类只有一个直接父类
多继承:一个子类有两个及以上直接父类
菱形继承:菱形继承建立在单继承和多继承基础之上,具体形式看下图
类D中有两份类A的对象成员,菱形继承存在二义性和数据冗余的问题。指定作用域可以解决二义性的问题,但数据冗余的问题依然存在。
若解决菱形继承的二义性和数据冗余问题,可以采用虚继承
(虚继承使用形式,以上图为例,在B、C继承A时定义,如:class B:virtual public A)
虚继承是怎样解决菱形继承体系的二义性和数据冗余问题的?点击打开链接
虚继承的优势和劣势:
优:虚继承解决了在菱形体系中包含多份父类对象的数据冗余和浪费空间问题。
缺:体系复杂,影响效率,在解决数据冗余问题的同时带来了性能损耗,所以不到万不得已的时候不定义菱形结构的虚继承体系。
四、友元与继承
友元关系不能继承,即基类友元不能访问子类私有和保护成员。
示例:
#include<iostream>
#include<string.h>
using namespace std;
class Person
{
public:
Person()
:_name(_name)
{}
friend void Display(Person &p);
protected:
int _name;
};
class Student:public Person
{
public:
Student()
:_id(123)
{}
protected:
int _id;
};
void Display(Person& p)
{
cout << p._name << endl;
}
int main()
{
Person p1;
Display(p1);
}
五、继承与静态成员
基类定义了static成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员。
#include<iostream>
#include<string.h>
using namespace std;
class Person
{
public:
Person()
{
++_count;
}
protected:
string _name;
public:
static int _count;
};
int Person::_count = 0;
class Student :public Person
{
protected:
string _majorcourse;
};
void main()
{
Student s1;
Student s2;
cout << "人数" << Person::_count << endl;
}