1、派生子类的语法:
class deriveClass :public BaseClass{}
class deriveClass :protected BaseClass{}
class deriveClass :private BaseClass{}
2、子类继承父类做了什么工作
子类对象存储了父类的数据成员
子类对象可以使用父类的方法
3、子类可以添加什么特性
子类可以添加自己需要的构造函数
子类可以按照需要添加额外的数据成员和成员函数
4、c++ 的继承有三种方式
4.1、公有继承 ----------is a关系,即派生类对象也是基类对象
4.2、保护继承 ----------
4.3、私有继承
5、继承时必须指明继承方式,public,protected,或者private,否则,默认是private继承
二、构造函数和析构函数
1、派生类不能直接访问基类的私有成员,必须通过基类方法进行访问
1.1、创建派生类对象时,首先创建基类对象,这意味着基类对象应当在程序进入派生类构造函数之前被创建。c++使用成员初始化列表语法来完成这种工作
2.2、如果没有提供成员初始化列表,怎么办?
必须首先创建基类对象,如果不调用基类构造函数,程序将使用默认的基类构造函数
除非要使用默认构造函数,否则应该显式的调用正确的基类构造函数
派生类必须给新成员和继承的成员提供数据
示例代码:
//person.h 文件,包含基类和派生类的声明
#ifndef PERSON_H
#define PERSON_H
#include <string>
using namespace std;
//这是一个基类
class Person
{
public:
Person();
Person(string name,bool sex,unsigned int age);
virtual ~Person();
private:
string name_;
bool sex_;
unsigned int age_;
};
//这是一个子类
class Student:public Person
{
private:
float score_;
public:
Student(float score);//构造函数不给基类构造函数提供初始值
};
//子类
class Teacher:public Person
{
private:
float salary_;
public:
Teacher(string name,bool sex,unsigned int age,float salary);//构造函数给基类构造函数提供初始值
};
#endif // PERSON_H
person.cpp,推荐使用成员初始化列表
// 包含构造函数的信息
#include "Person.h"
#include <string>
using namespace std;
Person::Person() : name_("test"),sex_("male"),age_(22)
{
}
//基类的构造函数
Person::Person(string name,bool sex,unsigned int age) : name_(name),sex_(sex),age_(age)
{
}
//派生类的构造函数,省略成员初始化列表
Student::Student(float score)//:Person()
{
score_ = score;
}
/*
//派生类的构造函数,省略成员初始化列表,那么默认调用无参的构造函数,等同于上面的构造函数
Student::Student(float score):Person()
{
score_ = score;
}
*/
//派生类的构造函数,提供成员初始化列表
Teacher::Teacher(string name,bool sex,unsigned int age,float salary) : Person(name,sex,age)
{
salary_ = salary;
}
/*
//派生类的构造函数,提供成员初始化列表,同时派生类的构造函数也采用成员初始化列表来完成,作用和上面一个构造函数一样
Teacher::Teacher(string name,bool sex,unsigned int age,float salary) : Person(name,sex,age),salary_(salary)
{
}*/
Person::~Person()
{
//dtor
}
Student::~Student()
{
}
Teacher::~Teacher()
{
}
编译时,会有警告
F:\codeblocks\面对对象_继承\src\Person.cpp|16|warning: 'Student::score_' should be initialized in the member initialization list [-Weffc++]|
F:\codeblocks\面对对象_继承\src\Person.cpp|29|warning: 'Teacher::salary_' should be initialized in the member initialization list [-Weffc++]|
2.1、创建派生类对象时,首先创建基类对象
2.2、派生类构造函数应通过成员初始化列表将基类信息传递给基类构造函数
2.3、派生类构造函数应该初始化新增的数据成员
3、关于析构函数
释放对象的顺序与创建对象的顺序相反
首先调用派生类的析构函数,然后调用基类的析构函数
三、使用派生类
1、使用派生类时,程序必须要能够访问基类的声明
可以把基类和派生类放在一个.h和一个.cpp文件中
2、派生类可以使用基类的非私有方法,也可以将派生对象赋给基类对象
3、基类指针可以在不进行显示转换的情况下指向派生类对象,基类引用可以在不进行显示类型转换的情况下引用派生类对象
4、基类指针或引用只能用于调用基类方法。
四、多态公有继承
1、多态公有继承的实现方式
主要有两种:
在派生类中重新定义基类方法
使用虚函数
2、虚函数
如果没有关键字virtual,程序将根据引用类型或指针类型选择对应的方法
但是有关键字virtual,程序将根据引用或指针指向的对象的类型来选择方法
virtual只用于函数原型中声明使用,函数定义时不需要
代码:
//person.h
#ifndef PERSON_H
#define PERSON_H
#include <string>
using namespace std;
//这是一个基类
class Person
{
public:
Person();
Person(string name,bool sex,unsigned int age);
void testWithNoVirtualFuction();
virtual void testWithVirtualFuction_2();
virtual ~Person();
private:
string name_;
bool sex_;
unsigned int age_;
};
//这是一个子类
class Student:public Person
{
private:
float score_;
public:
Student(float score);//构造函数不给基类构造函数提供初始值
void testWithNoVirtualFuction();
virtual void testWithVirtualFuction_2();
virtual ~Student();
};
class Teacher:public Person
{
private:
float salary_;
public:
Teacher(string name,bool sex,unsigned int age,float salary);//构造函数给基类构造函数提供初始值
void testWithNoVirtualFuction();
virtual void testWithVirtualFuction_2();
virtual ~Teacher();
};
#endif // PERSON_H
person.cpp
#include "Person.h"
#include <string>
#include <iostream>
using namespace std;
Person::Person() : name_("test"),sex_("male"),age_(22)
{
}
//基类的构造函数
Person::Person(string name,bool sex,unsigned int age) : name_(name),sex_(sex),age_(age)
{
}
void Person::testWithNoVirtualFuction()
{
cout<<"This is the Person Function with no virtual keyword ... "<<endl;
}
void Person::testWithVirtualFuction_2()
{
cout<<"This is the Person Function_2 with virtual keyword ... "<<endl;
}
//派生类的构造函数,省略成员初始化列表
Student::Student(float score) : score_(score)
{
}
void Student::testWithNoVirtualFuction()
{
cout<<"This is the Student Function with no virtual keyword ... "<<endl;
}
void Student::testWithVirtualFuction_2()
{
cout<<"This is the Student Function_2 with virtual keyword ... "<<endl;
}
//派生类的构造函数,提供成员初始化列表
Teacher::Teacher(string name,bool sex,unsigned int age,float salary) : Person(name,sex,age),salary_(salary)
{
}
void Teacher::testWithNoVirtualFuction()
{
cout<<"This is the Teacher Function with no virtual keyword ... "<<endl;
}
void Teacher::testWithVirtualFuction_2()
{
cout<<"This is the Teacher Function_2 with virtual keyword ... "<<endl;
}
Person::~Person()
{
//dtor
}
Student::~Student()
{
}
Teacher::~Teacher()
{
}
main.cpp
#include <iostream>
#include "Person.h"
using namespace std;
int main()
{
//三个对象
Person p("zhangsan",true,30);
Student s(87.5);
Teacher t("Mir Li",false,33,8000);
//三个引用
Person & rer_p = p;
Student & rer_s = s;
Teacher & rer_t = t;
//三个指针
Person * point_p = &p;
Student * point_s = &s;
Teacher * point_t = &t;
//基类指针指向派生类对象
Person * point_p_s = &s;
//testWithNoVirtualFuction不是虚函数,那么调用的是 point_p_s类型(person)的函数
//打印的是person。。
point_p_s->testWithNoVirtualFuction();
//testWithVirtualFuction_2是虚函数,调用的是point_p_s所指对象(Student)的函数
//打印的是student
point_p_s->testWithVirtualFuction_2();
//引用同理
Person & rer_p_t = t;
rer_p_t.testWithNoVirtualFuction();
rer_p_t.testWithVirtualFuction_2();
return 0;
}
3、一般会在基类中将要在派生类中重载的函数定义为虚函数,那么在派生类中将自动成为虚方法
4、虚析构函数
如果析构函数不是虚的,则将只调用对应指针类型的析构函数
如果析构函数是虚的,则将调用对应对象的析构函数
使用虚的析构函数,主要是为了保证正确的析构函数序列被调用