C++远征之继承篇 视频教程 笔记 方便自己查阅和复习,温故而知新。
目录
1 c++ 继承简介
通常,类库是以源代码的方式提供的,这意味着可以对其进行修改,以满足要求。然而,C++提供了比修改代码更好的方法来拓展和修改类,这种方法称之为 类继承,它能够从已有的类派生出新的类,而派生类继承原有类的特征,包括方法。 例如继承一笔财产要比自己白手起家容易,通过继承派生出的类通常比设计新的类容易得多。
但是生活中的继承 和 C++中的继承不能等同。
那么为什么要继承呢? 下面举一个例子,比较这两种类,Worker类用到了Person类的数据成员以及成员函数,如果不去重新定义,而是直接拿来用,那将会很方便。
所以 我们可以 在Worker中 将Person继承过来,形式如下:
下面介绍一下类继承所用到的专有名词:
那么在内存中,子类和父类 的关系 如下图所示:
代码示例
要求:查看 父类 和子类构造函数 执行地先后顺序
1 定义Person类,要求含有m_strNmae 和 m_iAge两个数据成员及构造函数 析构函数 eat函数
2 定义Worker类,要求共有继承Person类,含有数据成员m_iSalary , 构造函数 析构函数 work函数
//Person.h
#pragma once
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
Person();//构造函数
~Person();//析构函数
void eat();
string m_strName;
int m_iAge;
};
//Person.cpp
#include<iostream>
#include"Person.h"
using namespace std;
Person::Person()
{
cout << "Person() 构造" << endl;
}
Person::~Person()
{
cout << "~Person() 析构" << endl;
}
void Person::eat()
{
cout << "Person eat()" << endl;
}
//Worker.h
#pragma once
#include<iostream>
#include"Person.h"
using namespace std;
class Worker:public Person//公有继承
{
public:
Worker();
~Worker();
void work();
int m_iSalary;
};
//Worker.cpp
#include<iostream>
#include"Worker.h"
using namespace std;
Worker::Worker()
{
cout << "Worker() 构造" << endl;
}
Worker::~Worker()
{
cout << "~Worker() 析构" << endl;
}
void Worker::work()
{
cout << "Worker work()" << endl;
}
//main.cpp
#include<iostream>
#include"Worker.h"
using namespace std;
/*************************
类继承
要求:查看 父类 和子类 构造函数 执行地先后顺序
1 定义Person类,要求含有m_strNmae 和 m_iAge两个数据成员 及 构造函数 析构函数 eat函数
2 定义Worker类,要求共有继承Person类,含有数据成员m_iSalary , 构造函数 析构函数 work函数
**************************/
int main()
{
Worker *p = new Worker;
p->m_strName = "SB";
p->m_iAge = 3;
p->eat();
cout << "-------------------" << endl;
p->m_iSalary = 10000;
p->work();
delete p;
p = NULL;
cin.get();
return 0;
}
运行结果:
注:由实验结果可知:
构造函数: 先执行父类的构造函数 后执行子类的构造函数;
析构函数:先执行子类的析构函数 后执行父类的析构函数。
2 继承方式
类继承 有三种方式:
例如,公有继承 如下:
公有继承的访问:
那么 对于 protected 只能继承 不能访问,如下图所示:
总结
public 继承:
protected 继承:
private 继承:
3 继承中的特殊关系
3.1 隐藏
继承中的特殊关系,子类的数据成员或成员函数 与 父类的数据成员或成员函数 同名,此时,父类的称之为:隐藏
这种隐藏 可以 概括为: 父子关系 成员同名 隐藏
那么 如何访问 子类和父类的 同名函数呢? 下面举一个例子来说明:
对于 子类和父类的 数据成员同名 也同样举一个例子,如下图所示:
代码示例
继承关系中的 隐藏
要求:
1 定义Person类
数据成员: m_strName
成员函数:构造函数, play()
2 定义Soldier类:
数据成员: 无
成员函数:构造函数, play() worker()
//Person.h
#pragma once
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
Person();//构造函数
~Person();//析构函数
void play();
string m_strName;
int m_iAge;
};
//Person.cpp
#include<iostream>
#include"Person.h"
using namespace std;
Person::Person()
{
cout << "Person() 构造" << endl;
}
Person::~Person()
{
cout << "~Person()--析构" << endl;
}
void Person::play()
{
cout << "Person--play()" << endl;
}
//Soldier.h
#pragma once
#include<iostream>
#include"Person.h"
class Soldier:public Person
{
public:
Soldier();
void play();//与父类中的函数同名
void work();
protected:
};
//Soldier.cpp
#include<iostream>
#include"Soldier.h"
using namespace std;
Soldier::Soldier()
{
cout << "Soldier() 构造" << endl;
}
void Soldier::play()
{
cout << "Soldier---play()" << endl;
}
void Soldier::work()
{
cout << "Soldier---work()" << endl;
}
//main.cpp
#include<iostream>
#include"Soldier.h"
using namespace std;
/*************************
继承关系中的 隐藏
要求:
1 定义Person类
数据成员: m_strName
成员函数:构造函数, play()
2 定义Soldier类:
数据成员: 无
成员函数:构造函数, play() worker()
**************************/
int main()
{
Soldier soldier;
soldier.play();
cout << "-------------------" << endl;
soldier.Person::play();
soldier.work();
cin.get();
return 0;
}
运行结果:
3.2 is-a
如下图所示, 工人和人是一种 is-a 关系, 士兵和人同样也是 is-a 关系。但工人和士兵不是 is-a 关系
下面从内存角度 来说明 is-a 的关系:
代码示例
示例1:
要求:继承关系中的 is-a
1 定义Person类
数据成员: m_strName
成员函数:构造函数 析构函数 play()
2 定义Soldier类:
数据成员: m_iAge
成员函数:构造函数 析构函数 worker()
//Person.h
#pragma once
#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
Person(string name="pesrson_001");//构造函数
~Person();//析构函数
void play();
protected:
string m_strName;
};
//Person.cpp
#include<iostream>
#include"Person.h"
using namespace std;
Person::Person(string name)
{
m_strName = name;
cout << "Person()--构造" << endl;
}
Person::~Person()
{
cout << "~Person()--析构" << endl;
}
void Person::play()
{
cout << "Name: " << m_strName << endl;
cout << "Person--play()" << endl;
}
//Soldier.h
#pragma once
#include<iostream>
#include"Person.h"
class Soldier:public Person
{
public:
Soldier(string name = "soldier_001", int age = 20);
~Soldier();
void work();
protected:
int m_iAge;
};
//Soldier.cpp
#include<iostream>
#include"Soldier.h"
using namespace std;
Soldier::Soldier(string name, int age)
{
m_strName = name;
m_iAge = age;
cout << "Soldier()--构造" << endl;
}
Soldier::~Soldier()
{
cout << "~Soldier--析构" << endl;
}
void Soldier::work()
{
cout << "Name= "<< m_strName << endl;
cout << "Age= " << m_iAge << endl;
cout << "Soldier--work()" << endl;
}
//main.cpp
#include<iostream>
#include"Soldier.h"
using namespace std;
/*************************
继承关系中的 is-a
要求:
1 定义Person类
数据成员: m_strName
成员函数:构造函数 析构函数 play()
2 定义Soldier类:
数据成员: m_iAge
成员函数:构造函数 析构函数 worker()
**************************/
int main()
{
Soldier soldier1;
Person person1 ;
soldier1.play();
person1.play();
cout << "" << endl;
cout << "---------- 1 -----------" << endl;
cout << "" << endl;
Soldier soldier2;
Person person2 = soldier2;//soldier2初始化person2
person2.play(); //此时 person2中的m_strName被 soldier2初始化时更改了
//因为 m_strName是继承下来的 所以可以通过子类对父类继承下来的成员进行赋值
cout << "" << endl;
cout << "---------- 2 -----------" << endl;
cout << "" << endl;
//使用指针 对继承父类的成员进行赋值
Soldier soldier3;
Person *p1 = &soldier3;
p1->play();//与上面的效果一样,对父类的成员进行赋值了
cout << "" << endl;
cout << "---------- 3 -----------" << endl;
cout << "" << endl;
//看 释放内存 执行 哪一个析构函数
Person *p2 = new Soldier;//父类指针 指向子类
p2->play();
delete p2;
p2 = NULL;//只执行 父类的析构函数 可能造成内存泄漏 这时 需要用 虚函数(在父类和子类前加入 virtual)
cin.get();
return 0;
}
运行结果:
总结:
由结果可以知道:
(1) 子类可以赋值给父类,只能赋值继承下来的成员;
(2) 父类指针指向子类时,只能指向子类继承下来的成员;
(3) 在父类指针 指向 子类时,释放内存 只执行了父类的析构函数,这可能会造成内存泄漏 这时 需要用 虚析构函数 (在父类和子类的虚构函数前加入 virtual 关键字) 即可。
示例2:
继承关系中的 is-a 函数传递
要求:
1 定义Person类
数据成员: m_strName
成员函数:构造函数 析构函数 play()
2 定义Soldier类:
数据成员: m_iAge
成员函数:构造函数 析构函数 worker()
3 定义函数test1(Person p) test2(person &p) test3(Person *p)
这里只有main.cpp不同 其他函数文件均与示例1相同。
//main.cpp
#include<iostream>
#include"Soldier.h"
using namespace std;
/*************************
继承关系中的 is-a
要求:
1 定义Person类
数据成员: m_strName
成员函数:构造函数 析构函数 play()
2 定义Soldier类:
数据成员: m_iAge
成员函数:构造函数 析构函数 worker()
3 定义函数test1(Person p) test2(person &p) test3(Person *p)
**************************/
void test1(Person p)
{
p.play();
}
void test2(Person &p)
{
p.play();
}
void test3(Person *p)
{
p->play();
}
int main()
{
Person p;
Soldier s;
cout << "" << endl;
cout << "---------- 1 -----------" << endl;
test1(p);//接受值时 临时实例化 通过临时对象 调用函数play 结束后 对象自动销毁(调用析构函数)
cout << "------------------------" << endl;
test1(s);
cout << "" << endl;
cout << "---------- 2 -----------" << endl;
cout << "" << endl;
test2(p);//没有产生新的临时对象
cout << "------------------------" << endl;
test2(s);
cout << "" << endl;
cout << "---------- 3 -----------" << endl;
cout << "" << endl;
test3(&p); //没有产生新的临时对象
cout << "------------------------" << endl;
test3(&s);
cin.get();
return 0;
}
运行结果:
注:由实验结果可知,test1(). test2()和test3() 三个函数调用时,调用test1()函数,会产生新的临时对象,而test2() 和test3()函数不会,所以test2() 和test3()函数的效率更高。
4 多继承与多重继承
5 虚继承
由于视频教程知识点较多,所以分开记录,便于查找和复习。详见:C++ 继承(2): 多重继承, 多继承, 虚继承(virtual)
参考资料
[1] C++远征之继承篇 (注:图片均来自视频中PPT)