C++ 继承(1): 继承方式(public, protected, private), 继承中的特殊关系(隐藏 , is-a) - 在水一方xym的博客 - CSDN博客
C++远征之继承篇 视频教程 笔记 方便自己查阅和复习,温故而知新。
目录
1 c++ 继承简介
代码示例
2 继承方式
总结
3 继承中的特殊关系
3.1 隐藏
代码示例
3.2 is-a
代码示例
4 多继承与多重继承
5 虚继承
参考资料
通常,类库是以源代码的方式提供的,这意味着可以对其进行修改,以满足要求。然而,C++提供了比修改代码更好的方法来拓展和修改类,这种方法称之为 类继承,它能够从已有的类派生出新的类,而派生类继承原有类的特征,包括方法。 例如继承一笔财产要比自己白手起家容易,通过继承派生出的类通常比设计新的类容易得多。
但是生活中的继承 和 C++中的继承不能等同。
那么为什么要继承呢? 下面举一个例子,比较这两种类,Worker类用到了Person类的数据成员以及成员函数,如果不去重新定义,而是直接拿来用,那将会很方便。
所以 我们可以 在Worker中 将Person继承过来,形式如下:
下面介绍一下类继承所用到的专有名词:
那么在内存中,子类和父类 的关系 如下图所示:
要求:查看 父类 和子类构造函数 执行地先后顺序
1 定义Person类,要求含有m_strNmae 和 m_iAge两个数据成员及构造函数 析构函数 eat函数
2 定义Worker类,要求共有继承Person类,含有数据成员m_iSalary , 构造函数 析构函数 work函数
-
//Person.h
-
#pragma once
-
#include
-
#include
-
using
namespace
std;
-
-
class Person
-
{
-
public:
-
Person();
//构造函数
-
~Person();
//析构函数
-
-
void eat();
-
string m_strName;
-
int m_iAge;
-
-
};
-
//Person.cpp
-
-
#include
-
#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
-
#include"Person.h"
-
-
using
namespace
std;
-
-
class Worker:
public Person
//公有继承
-
{
-
public:
-
Worker();
-
~Worker();
-
-
void work();
-
int m_iSalary;
-
};
-
//Worker.cpp
-
-
#include
-
#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
-
#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;
-
}
运行结果:
注:由实验结果可知:
构造函数: 先执行父类的构造函数 后执行子类的构造函数;
析构函数:先执行子类的析构函数 后执行父类的析构函数。
类继承 有三种方式:
例如,公有继承 如下:
公有继承的访问:
那么 对于 protected 只能继承 不能访问,如下图所示:
public 继承:
protected 继承:
private 继承:
继承中的特殊关系,子类的数据成员或成员函数 与 父类的数据成员或成员函数 同名,此时,父类的称之为:隐藏
这种隐藏 可以 概括为: 父子关系 成员同名 隐藏
那么 如何访问 子类和父类的 同名函数呢? 下面举一个例子来说明:
对于 子类和父类的 数据成员同名 也同样举一个例子,如下图所示:
继承关系中的 隐藏
要求:
1 定义Person类
数据成员: m_strName
成员函数:构造函数, play()
2 定义Soldier类:
数据成员: 无
成员函数:构造函数, play() worker()
-
//Person.h
-
#pragma once
-
#include
-
#include
-
using
namespace
std;
-
-
class Person
-
{
-
public:
-
Person();
//构造函数
-
~Person();
//析构函数
-
-
-
void play();
-
string m_strName;
-
int m_iAge;
-
-
};
-
//Person.cpp
-
-
#include
-
#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
-
#include"Person.h"
-
-
class Soldier:
public Person
-
{
-
public:
-
Soldier();
-
void play();
//与父类中的函数同名
-
void work();
-
-
protected:
-
};
-
//Soldier.cpp
-
-
#include
-
#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
-
#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;
-
}
运行结果:
如下图所示, 工人和人是一种 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
-
#include
-
using
namespace
std;
-
-
class Person
-
{
-
public:
-
Person(
string name=
"pesrson_001");
//构造函数
-
~Person();
//析构函数
-
-
void play();
-
-
protected:
-
string m_strName;
-
-
};
-
//Person.cpp
-
-
#include
-
#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
-
#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
-
#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
-
#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) 父类指针指向子类时,只能指向子类继承下来的成员;
(2) 在父类指针 指向 子类时,释放内存 只执行了父类的析构函数,这可能会造成内存泄漏 这时 需要用 虚函数(在父类和
子类前加入 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
-
#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()函数的效率更高。
由于视频教程知识点较多,所以分开记录,便于查找和复习。详见:C++ 继承(2): 多重继承, 多继承, 虚继承(virtual)
[1] C++远征之继承篇 (注:图片均来自视频中PPT)