C++——类继承
合集下载
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
*派生类对象存储了基类的数据成员(派生类继承了基类的实现);
*派生类对象可以使用基类的方法(派生类继承了基类的接口)。
接下来,我们就可以在继承特性中添加下面的内容:
*派生类需要自己的构造函数;
*派生类可以根据需要添加额外的数据成员和成员函数。
在我们设计的Teacher类需要一个数据成员来存储工作的单位、工资以及所教授的课程。还应包括检查这些信息和重置这些信息的方 法:
3、使用派生类
要使用派生类,程序必须要能够访问基类声明。可以将基类和派生类的声明置于同一个头文件中,也可以将每个类放在独立的头文件 中,但由于这两个类是相关的,所以把其类声明放在一起更合适。
下面是Teacher的完整方法实现文件:
1 #include "Teacher.h" 2 Teacher::Teacher(const string & name, int age, const string & workUnit, float salary, const string & course):Person(name,age){ 3 workUnit_ = workUnit; 4 salary_ = salary; 5 course_ = course; 6} 7 Teacher::Teacher(const Person & per, const string & workUnit, float salary, const string & course):Person(per){ 8 workUnit_ = workUnit; 9 salary_ = salary; 10 course_ = course; 11 } 12 Teacher::Teacher(){ 13 workUnit_ = "none"; 14 salary_ = .0; 15 course_ = "none"; 16 } 17 void Teacher::setCourse(const string & course){ 18 course_ = course; 19 } 20 void Teacher::setWorkUnit(const string & workUnit){ 21 workUnit_ = workUnit; 22 } 23 void Teacher::setSalary(float salary){ 24 salary_ = salary; 25 } 26 string Teacher::getWorkUnit()const{ 27 return workUnit_; 28 } 29 string Teacher::getCourse()const{ 30 return course_; 31 } 32 float Teacher::getSalary()const{
Teacher::Teacher(const Person & per, const string & workUnit, float salary, const string & course):Person(per),workUnit_(workUnit),salary_(salary),course_(course){}
构造函数必须给新成员(如果有新成员)和继承的成员提供数据。
2、构造函数:访问权限的考虑
派生类不能直接访问基类的私有成员,而必须通过基类方法进行访问。例如,派生类构造函数不能直接设置继承来的成员,而必须使用 基类的公有方法来访问私有的基类成员。具体地说,派生类构造函数必须使用基类的构造函数。
类继承:从已有的类派生出新的类,派生类继承了原有类(称为基类)的特征,包括方法。
通过类继承可以完成的工作:
*可以在已有类的基础上添加功能;
*可以给类添加数据;
*可以修改类的行为。
继承机制只需要提供新特性,甚至不需要访问源代码就可以派生出类。
一、一个简单的基类
首先我们定义一个简单的基类Person,其设计如下:
提供复制构造函数、赋值运算符重载函数、析构函数。在Person类中,我们使用编译器提供的默认析构函数、默认复制构造函数和默认的赋 值运算符重载函数即可。
1、派生一个类
下面我们设计一个Teacher类继承自Person类。首先将Teacher类声明为从Person类派生而来:
1 #include <iostream> 2 #include "Person.h" 3 4 class Teacher:public Person{ 5 // ... 6 };
需要注意的一点是成员初始化列表将一个派生类b的引用传给基类a的复制构造函数这里使用了向上强制类型转换基类引用或指针可以指向派生类对象这样基类a的复制构造函数将使用派生类b引用对象参数共享的基类数据部分来构造新对象的共享基类数据部分
C++ ——类继承
类库:类库由类声明和实现构成。类组合了数据表示和类方法,因此提供了比函数库更加完整的程序包。
1 #include <iostream> 2 #include "Person.h" 3 4 class Teacher:public Person{ 5 private: 6 string workUnit_;//工作单位 7 float salary_;//工资 8 string course_;//教授的课程 9 public: 10 Teacher(const string & , int , const string &, float, const string &); 11 Teacher(const Person &, const string &, float, const string &); 12 Teacher(); 13 void setWorkUnit(const string & ); 14 void setSalary(float ); 15 void setCourse(const string &); 16 string getWorkUnit()const; 17 float getSalary()const; 18 string getCourse()const; 19 friend std::ostream & operator<<(std::ostream & os , const Teacher &); 20 };
提示:在设计一个类的时候,我们需要考虑一下几个问题:
*是否需要显式提供默认构造函数;
*是否需要显式提供析构函数;
*是否需要显式提供复制构造函数;
*是否需要显式提供赋值运算符重载函数;
*是否需要显式提供地址运算符函数;
一般来说,如果在类的构造函数中使用了new运算符,或者在其他成员函数中使用了new运算符来修改类的成员,那么就需要考虑显式
由于per的类型为Person,因此调用基类的复制构造函数。在这里,基类Person没有定义复制构造函数,如果需要复制构造函数但又没 有定义,编译器将生成一个。在这种情况下,执行成员复制的隐式复制构造函数是合适的,因为这个类没有使用动态内存分配。
同样,也可以对派生类使用成员初始化列表语法。在这种情况下,应在列表中使用成员名,而不是类名。所以,第二个构造函数可以按 照下面的方式编写:
除非要使用默认的构造函数,否则应显式调用正确的基类构造函数。
下面来看第二个构造函数的代码:
1 Teacher::Teacher(const Person & per, const string & workUnit, float salary, const string & course):Person(per){ 2 workUnit_ = workUnit; 3 salary_ = salary; 4 course_ = course; 5}
创建派生类对象时,程序首先创建基类对象。从概念上说,这意味着基类对象应当在程序进入派生类构造函数之前被创建。C++使用成 员初始化列表语法来完成这种工作。例如,下面是第一个Teacher类的构造函数代码:
1 Teacher::Teacher(const string & name, int age, const string & workUnit, float salary, const string & course):Person(name,age){ 2 workUnit_ = workUnit; 3 salary_ = salary; 4 course_ = course; 5}
冒号指出Teacher类的基类是Person类。上述特殊的生命头表明Person是一个公有基类,这杯称为公有派生。派生类对象包含基类对 象。
使用公有派生,基类的公有成员将成为派生类的公有成员;基类的私有部分也将成为派生类的一部分,但只能通过基类的公有和保护方 法访问。
派生类将具有以下特征:
Person.h
1 #include <iostream> 2 #include <string> 3 using std::string; 4 class Person{ 5 private: 6 string name_; 7 int age_; 8 public: 9 Person(const string & name = "none", int age = 0);//形参类型声明为const string &,那么实参既可以是string对象,也可以是字符串常量。 10 void setName(const string &name); 11 void setAge(int age); 12 string getName()const; 13 int getAge() const; 14 friend std::ostream & operator<<(std::ostream & os, const Person & p); 15 };
1 Teacher::Teacher(const string & name, int age, const string & workUnit, float salary, const string & course):Person(){ 2 workUnit_ = workUnit; 3 salary_ = salary; 4 course_ = course; 5}
必须首先创建基类对象,如果不调用基类构造函数,程序将使用默认的基类构造函数,因此下面的两段代码是等效的:
1 Teacher::Teacher(const string & name, int age, const string & workUnit, float salary, const string & course){ 2 workUnit_ = workUnit; 3 salary_ ቤተ መጻሕፍቲ ባይዱ salary; 4 course_ = course; 5}
有关派生类构造函数的要点有如下几点:
*首先创建基类对象;
*派生类构造函数应通过成员初始化列表将基类信息传递给基类构造函数;
*派生类构造函数应初始化派生类新增的数据成员。
这个例子没有提供显式析构函数,因此使用隐式析构函数。释放对象的顺序与创建对象的顺序相反,即首先执行派生类的析构函数,然 后自动调用基类的析构函数。
Person.cpp
1 #include "Person.h" 2 Person::Person(const string & name, int age){ 3 name_ = name; 4 age_ = age; 5} 6 std::ostream & operator<<(std::ostream & os, const Person & p){ 7 os << "name:" << _ << ", age:" << p.age_; 8 return os; 9} 10 void Person::setName(const string &name){ 11 name_ = name; 12 } 13 void Person::setAge(int age){ 14 age_ = age; 15 } 16 string Person::getName()const{ 17 return name_; 18 } 19 int Person::getAge()const{ 20 return age_; 21 }
*派生类对象可以使用基类的方法(派生类继承了基类的接口)。
接下来,我们就可以在继承特性中添加下面的内容:
*派生类需要自己的构造函数;
*派生类可以根据需要添加额外的数据成员和成员函数。
在我们设计的Teacher类需要一个数据成员来存储工作的单位、工资以及所教授的课程。还应包括检查这些信息和重置这些信息的方 法:
3、使用派生类
要使用派生类,程序必须要能够访问基类声明。可以将基类和派生类的声明置于同一个头文件中,也可以将每个类放在独立的头文件 中,但由于这两个类是相关的,所以把其类声明放在一起更合适。
下面是Teacher的完整方法实现文件:
1 #include "Teacher.h" 2 Teacher::Teacher(const string & name, int age, const string & workUnit, float salary, const string & course):Person(name,age){ 3 workUnit_ = workUnit; 4 salary_ = salary; 5 course_ = course; 6} 7 Teacher::Teacher(const Person & per, const string & workUnit, float salary, const string & course):Person(per){ 8 workUnit_ = workUnit; 9 salary_ = salary; 10 course_ = course; 11 } 12 Teacher::Teacher(){ 13 workUnit_ = "none"; 14 salary_ = .0; 15 course_ = "none"; 16 } 17 void Teacher::setCourse(const string & course){ 18 course_ = course; 19 } 20 void Teacher::setWorkUnit(const string & workUnit){ 21 workUnit_ = workUnit; 22 } 23 void Teacher::setSalary(float salary){ 24 salary_ = salary; 25 } 26 string Teacher::getWorkUnit()const{ 27 return workUnit_; 28 } 29 string Teacher::getCourse()const{ 30 return course_; 31 } 32 float Teacher::getSalary()const{
Teacher::Teacher(const Person & per, const string & workUnit, float salary, const string & course):Person(per),workUnit_(workUnit),salary_(salary),course_(course){}
构造函数必须给新成员(如果有新成员)和继承的成员提供数据。
2、构造函数:访问权限的考虑
派生类不能直接访问基类的私有成员,而必须通过基类方法进行访问。例如,派生类构造函数不能直接设置继承来的成员,而必须使用 基类的公有方法来访问私有的基类成员。具体地说,派生类构造函数必须使用基类的构造函数。
类继承:从已有的类派生出新的类,派生类继承了原有类(称为基类)的特征,包括方法。
通过类继承可以完成的工作:
*可以在已有类的基础上添加功能;
*可以给类添加数据;
*可以修改类的行为。
继承机制只需要提供新特性,甚至不需要访问源代码就可以派生出类。
一、一个简单的基类
首先我们定义一个简单的基类Person,其设计如下:
提供复制构造函数、赋值运算符重载函数、析构函数。在Person类中,我们使用编译器提供的默认析构函数、默认复制构造函数和默认的赋 值运算符重载函数即可。
1、派生一个类
下面我们设计一个Teacher类继承自Person类。首先将Teacher类声明为从Person类派生而来:
1 #include <iostream> 2 #include "Person.h" 3 4 class Teacher:public Person{ 5 // ... 6 };
需要注意的一点是成员初始化列表将一个派生类b的引用传给基类a的复制构造函数这里使用了向上强制类型转换基类引用或指针可以指向派生类对象这样基类a的复制构造函数将使用派生类b引用对象参数共享的基类数据部分来构造新对象的共享基类数据部分
C++ ——类继承
类库:类库由类声明和实现构成。类组合了数据表示和类方法,因此提供了比函数库更加完整的程序包。
1 #include <iostream> 2 #include "Person.h" 3 4 class Teacher:public Person{ 5 private: 6 string workUnit_;//工作单位 7 float salary_;//工资 8 string course_;//教授的课程 9 public: 10 Teacher(const string & , int , const string &, float, const string &); 11 Teacher(const Person &, const string &, float, const string &); 12 Teacher(); 13 void setWorkUnit(const string & ); 14 void setSalary(float ); 15 void setCourse(const string &); 16 string getWorkUnit()const; 17 float getSalary()const; 18 string getCourse()const; 19 friend std::ostream & operator<<(std::ostream & os , const Teacher &); 20 };
提示:在设计一个类的时候,我们需要考虑一下几个问题:
*是否需要显式提供默认构造函数;
*是否需要显式提供析构函数;
*是否需要显式提供复制构造函数;
*是否需要显式提供赋值运算符重载函数;
*是否需要显式提供地址运算符函数;
一般来说,如果在类的构造函数中使用了new运算符,或者在其他成员函数中使用了new运算符来修改类的成员,那么就需要考虑显式
由于per的类型为Person,因此调用基类的复制构造函数。在这里,基类Person没有定义复制构造函数,如果需要复制构造函数但又没 有定义,编译器将生成一个。在这种情况下,执行成员复制的隐式复制构造函数是合适的,因为这个类没有使用动态内存分配。
同样,也可以对派生类使用成员初始化列表语法。在这种情况下,应在列表中使用成员名,而不是类名。所以,第二个构造函数可以按 照下面的方式编写:
除非要使用默认的构造函数,否则应显式调用正确的基类构造函数。
下面来看第二个构造函数的代码:
1 Teacher::Teacher(const Person & per, const string & workUnit, float salary, const string & course):Person(per){ 2 workUnit_ = workUnit; 3 salary_ = salary; 4 course_ = course; 5}
创建派生类对象时,程序首先创建基类对象。从概念上说,这意味着基类对象应当在程序进入派生类构造函数之前被创建。C++使用成 员初始化列表语法来完成这种工作。例如,下面是第一个Teacher类的构造函数代码:
1 Teacher::Teacher(const string & name, int age, const string & workUnit, float salary, const string & course):Person(name,age){ 2 workUnit_ = workUnit; 3 salary_ = salary; 4 course_ = course; 5}
冒号指出Teacher类的基类是Person类。上述特殊的生命头表明Person是一个公有基类,这杯称为公有派生。派生类对象包含基类对 象。
使用公有派生,基类的公有成员将成为派生类的公有成员;基类的私有部分也将成为派生类的一部分,但只能通过基类的公有和保护方 法访问。
派生类将具有以下特征:
Person.h
1 #include <iostream> 2 #include <string> 3 using std::string; 4 class Person{ 5 private: 6 string name_; 7 int age_; 8 public: 9 Person(const string & name = "none", int age = 0);//形参类型声明为const string &,那么实参既可以是string对象,也可以是字符串常量。 10 void setName(const string &name); 11 void setAge(int age); 12 string getName()const; 13 int getAge() const; 14 friend std::ostream & operator<<(std::ostream & os, const Person & p); 15 };
1 Teacher::Teacher(const string & name, int age, const string & workUnit, float salary, const string & course):Person(){ 2 workUnit_ = workUnit; 3 salary_ = salary; 4 course_ = course; 5}
必须首先创建基类对象,如果不调用基类构造函数,程序将使用默认的基类构造函数,因此下面的两段代码是等效的:
1 Teacher::Teacher(const string & name, int age, const string & workUnit, float salary, const string & course){ 2 workUnit_ = workUnit; 3 salary_ ቤተ መጻሕፍቲ ባይዱ salary; 4 course_ = course; 5}
有关派生类构造函数的要点有如下几点:
*首先创建基类对象;
*派生类构造函数应通过成员初始化列表将基类信息传递给基类构造函数;
*派生类构造函数应初始化派生类新增的数据成员。
这个例子没有提供显式析构函数,因此使用隐式析构函数。释放对象的顺序与创建对象的顺序相反,即首先执行派生类的析构函数,然 后自动调用基类的析构函数。
Person.cpp
1 #include "Person.h" 2 Person::Person(const string & name, int age){ 3 name_ = name; 4 age_ = age; 5} 6 std::ostream & operator<<(std::ostream & os, const Person & p){ 7 os << "name:" << _ << ", age:" << p.age_; 8 return os; 9} 10 void Person::setName(const string &name){ 11 name_ = name; 12 } 13 void Person::setAge(int age){ 14 age_ = age; 15 } 16 string Person::getName()const{ 17 return name_; 18 } 19 int Person::getAge()const{ 20 return age_; 21 }