C++ Primer Plus 学习之 类继承
主要介绍了类的继承、虚函数、类继承的动态内存分配问题、继承与友元函数。
公有派生
- 基类的公有成员和私有成员都会成为派生类的一部分。
- 基类的私有成员只能通过基类的公有或者保护方法访问。但是,基类指针或引用只能用于调用基类方法,不能调用派生类方法。(这种兼容性使得可以用派生类对象来初始化基类对象,也可以将派生类对象赋给基类对象。)
- 基类的指针或引用可以在不显示类型转换的情况下指向派生类。
- 派生类的构造函数。
- 首先会创建基类的对象,派生类的构造函数应通过成员初始化列表将基类信息传递给基类的构造函数。
- 基类有指针成员或者动态内存分配,则需要定义基类的复制构造函数。
1
2
3
4
5Point::Point(int tk,const Base &b):Base(b)
{
k=tk;
}
- 基类有指针成员或者动态内存分配,则需要定义基类的复制构造函数。
派生类的析构函数
- 派生类对象过期时,程序将首先调用派生类析构函数,然后再调用基类析构函数。
多态-动态多态
定义
- 同一个方法在派生类和基类中的行为是不同的。
- 方法取决于调用该方法的对象。
- 前面所学的重载和函数模板是在编译时间便确定的联编,称为静态多态。
重写基类方法
- 基类的方法可以在派生类中重写 – 使用classname::来说明是基类的还是派生类的。
可在派生类中使用基类名作为限定符调用同名的基类函数。
1
2
3
4
5void Point::show()
{
Base::show();
cout<<z<<endl;
}如果基类中的函数有多个重载,则继承过来的时候不能只重新定义一个版本的,则会导致另外的会被隐藏。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37class
{
public:
void show(int a)const
{
cout<<a<<endl;
}
void show()const
{
cout<<10<<endl;
}
void show(double a)const
{
cout<<a<<endl;
}
};
class P : public Base
{
public:
void show(int a)const
{
cout<<a+10<<endl;
}
/*void show()const
{
cout<<20<<endl;
}
void show(double a)const
{
cout<<a+10<<endl;
}*/
};
int main()
{
P a;
a.show();//invalid
}
实现多态公有继承的两种方法
- 在派生类重新定义基类的方法。
- 使用虚方法。
虚函数
- 声明前加virtual,定义不用加。
- 如果方法是通过引用或指针而不是对象调用的,程序将根据引用或指针指向的对象的类型来选择方法。反之如果没用virtual声明,程序直接使用声明的对象的类型的方法。
- 如果没有加virtual(一般方法),那么程序将根据引用或指针的类型来选择方法。
- 友元函数不能是虚函数,因为友元函数不是类成员,而只有成员函数才能是虚函数。
可以创建指向基类的指针数组,那么这个数组既可以指向基类,也可以指向派生类。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16int main() {
Base b(10, 20);
Point x(1, 2, 3);
Base* bs[3];
bs[0] = new Base(1, 2);
bs[1] = &x;
bs[2] = &b;
rep(i, 0, 3) bs[i]->show(), cout << endl;
return 0;
}
/*
output:
大专栏 C++ Primer Plus 学习之 类继承ss="line">1 2
1 2 3
10 20
*/注意:基类需要声明一个虚析构函数,这样做是为了保证在释放对象时,可以调用相应对象类型的析构函数。
- 没有重新定义
- 重新定义一个不接受参数的show函数,那么将会隐藏同名基类的方法。
- 两条经验规则
- 如果重新定义继承的方法,应确保与原来的原型完全相同。
- 如果返回类型是基类引用或指针,则可以修改为指向派生类的引用或指针(这种例外是新出现的)。这种特性被称为返回类型协变。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15class
{
public:
virtual void show(int a)const;
...
};
class Point : public Base
{
public:
virtual void show()const;
...
};
Point tmp;
tmp.show()//valid
tmp.show(1);//invalid
- 如果返回类型是基类引用或指针,则可以修改为指向派生类的引用或指针(这种例外是新出现的)。这种特性被称为返回类型协变。
静态联编和动态联编
- 将源代码中的函数调用解释为执行特定的函数代码块被称为函数名联编。
- 函数重载和函数模板C/C++编译器可以在编译过程完成这种联编。在编译过程中进行联编被称为静态联编。
- 虚函数,因为编译器不知道用户将选择哪种类型的对象。所以编译器必须生成能够在程序运行时选择正确的虚函数方法,这被称为动态联编。
- C++默认选择为静态联编,因为效率更高。
虚函数的实现原理
- 给每个对象添加一个隐藏成员。隐藏成员中保存了一个指向函数地址数组的指针。
- 该地址数组称为虚函数表。
- 对于基类来说,基类对象包含了一个指针,该指针指向基类中所有虚函数的地址表。
- 对于派生类来说,派生类对象也包含了一个指针,该指针指向派生类中所有虚函数的地址表。
- 如果派生类没有重新定义某虚函数,则此表保留此虚函数基类版本的地址。
- 如果派生类重新定义了某虚函数,则此表将更新此虚函数的新地址。
- 如果派生类增加了新的虚函数,则此表也增加该函数的地址。
- 不管虚函数有多少个,都只需要在对象里添加一个指针成员。
- 调用虚函数时:
- 程序将查看存储在对象中的虚函数表头地址。
- 然后转身相应的函数地址表。
- 然后根据该虚函数在类中声明的位置找到其在表中的位置。
- 然后跳到该地址指向的函数地址,执行函数。
抽象基类(abc)
- abstract base class
使用纯虚函数提供未实现的函数 – 在声明的结尾处加 =0
1
2
3
4
5
6class {
private:
...;
public:
virtual double get_area() = 0;
};包含纯虚函数的类只用作基类 ,不能实例化 , 但是能声明(但不初始化)指针。
- 即ABC必须至少包含一个纯虚函数。
- 如果在基类中声明了纯虚函数,而派生类中并没有对其定义,则该函数仍为纯虚函数,派生类也为抽象类。
- 基类中也可以定义(实现)纯虚函数,但在派生类中必需也要定义并且显示地调用(使用类名限定符)。
- 这样可以将不同子类中公共的事务放在父类中完成。
- 只有声明而没有定义的纯虚函数派生类是无法调用的。
- 如果要把基类的析构函数声明为纯虚函数(有时候这么做只是为了说明此类为抽象类),则必须定义这个纯虚析构函数的实现 ,因为派生类析构时会自动调用它。
- 纯虚函数作为一种“接口约定”, 在基于组件的编程模式中很常见。
- 派生组件至少会实现基类组件的所有接口(纯虚函数)。
继承和动态内存分配
如果基类使用了动态内存分配 – 即在构造中使用new分配空间
- 该基类需要声明其构造函数, 析构函数,复制构造,赋值运算符。
如果此时派生类中没有使用new分配的内存
- 此派生类默认的复制构造会显式地调用基类的复制构造, 同时根据成员变量类型进行复制。
- 此派生类默认的赋值运算符会显式地调用基类的赋值运算符。
- 此派生类不需要显示定义构造函数, 析构函数,复制构造,赋值运算符。
如果子类使用new分配的内存
- 必须为子类定义显式析构函数。
- 必须为子类定义复制构造函数。
- 基类的复制构造函数中的参数是基类的引用,所以可以传递进来派生类对象。
1
2
3
4
5Point::Point(const Point &a):Base(a)
{
z=a.z;
}
`
- 基类的复制构造函数中的参数是基类的引用,所以可以传递进来派生类对象。
必须为子类重载赋值运算符。
- 显式调用基类的赋值运算符,以完成基类部分的赋值。
1
2
3
4
5
6
7
8
9String& String::operator=(const String& s) {
if(this == &s) return *this;
Base::operator=(s); // 注意此句 -- 必须显示调用基类的赋值运算符
delete[] str;
len = s.len;
str = new char[len+1];
strcpy(str, s.str);
return *this;
}
- 显式调用基类的赋值运算符,以完成基类部分的赋值。
继承与友元函数
- 在派生类的友元函数中, 只能访问派生类的派生部分,而不能访问基类的私有成员。
- 可使用基类的友元函数来负责对派生类的基类部分的访问。
- 因为友元函数不是成员函数,不能使用作用域解析运算符,因此需要强制类型转换。
C++ Primer Plus 学习之 类继承的更多相关文章
- C++ primer(十三)--类继承、构造函数成员初始化、虚函数、抽象基类
一.基类 从一个类派生出另一个类时,原始类称为基类,继承类称为派生类. 派生类对自身基类的private成员没有访问权限,对基类对象的protected成员没有访问权限,对派生类对象的(基类之 ...
- Javascript学习6 - 类、对象、继承
原文:Javascript学习6 - 类.对象.继承 Javasciprt并不像C++一样支持真正的类,也不是用class关键字来定义类.Javascript定义类也是使用function关键字来完成 ...
- iOS学习——iOS 整体框架及类继承框架图
整理自:IOS 整体框架类图值得收藏 一 整体框架 在iOS开发过程中,对iOS的整理框架的了解和学习是必不可少的一个环节,今天我们就好好来了解一下iOS的整体框架.首先贴一个关于iOS的框架介绍:i ...
- C++学习笔记(十二):类继承、虚函数、纯虚函数、抽象类和嵌套类
类继承 在C++类继承中,一个派生类可以从一个基类派生,也可以从多个基类派生. 从一个基类派生的继承称为单继承:从多个基类派生的继承称为多继承. //单继承的定义 class B:public A { ...
- Android(java)学习笔记118:类继承的注意事项
/* 继承的注意事项: A:子类只能继承父类所有非私有的成员(成员方法和成员变量) B:子类不能继承父类的构造方法,但是可以通过super(马上讲)关键字去访问父类构造方法. C:不要为了部分功能而去 ...
- 重新开始学习javase_类再生(类的合成和继承)
一.合成在新类里简单地创建原有类的对象.我们把这种方法叫作“合成” 为进行合成,我们只需在新类里简单地置入对象句柄即可.举个例子来说,假定需要在一个对象里容纳几个 String对象.两种基本数据类型以 ...
- C++学习之路—继承与派生(三):多重继承与虚基类
(根据<C++程序设计>(谭浩强)整理,整理者:华科小涛,@http://www.cnblogs.com/hust-ghtao转载请注明) 多重继承是指一个派生类有两个或多个基类.例如,有 ...
- C++学习之路—继承与派生(一):基本概念与基类成员的访问属性
(本文根据<c++程序设计>(谭浩强)总结而成,整理者:华科小涛@http://www.cnblogs.com/hust-ghtao,转载请注明) 1 基本思想与概念 在传统的程序设计 ...
- C++学习之路—继承与派生(二):派生类的构造函数与析构函数
(根据<C++程序设计>(谭浩强)整理,整理者:华科小涛,@http://www.cnblogs.com/hust-ghtao转载请注明) 由于基类的构造函数和析构函数是不能被继承的,所以 ...
随机推荐
- restful的简单使用
根据http的不同方法,访问不同路由的相同控制器下的不同方法可以实现restful的使用 分别对应 路由方式 get put delete post 对应操作 获取 更新 删除 添加 其中如果要在非l ...
- 023.Python的随机模块和时间模块
一 random 随机模块 1.1 获取随机0-1之间的小数(左闭右开) 0<= x < 1 import random res = random.random() print(res) ...
- pip换源源
介绍 """ 1.采用国内源,加速下载模块的速度 2.常用pip源: -- 豆瓣:https://pypi.douban.com/simple -- 阿里:https:/ ...
- 配置完aop不能返回json
遇到一个bug,配置了记录controller层的aop,后来发现是因为我aop是写在controller层(在springmvc的容器中)而开启aop配置却写在spring中,所以不起作用,需要在s ...
- Underscore _.template 方法使用详解
为什么用「void 0」代替「undefined」 undefined 并不是保留词(reserved word),它只是全局对象的一个属性,在低版本 IE 中能被重写. 事实上,undefined ...
- Vue项目中跨域问题解决
后台更改header 使用http-proxy-middleware 代理解决(项目使用vue-cli脚手架搭建) Jquery jsonp 一.后台更改header header('Access-C ...
- CSS样式表-------第二章:选择器
二 .选择器 内嵌.外部样式表的一般语法: 选择器 { 样式=值: 样式=值: 样式=值: ...... } 以下面html为例,了解区分一下各种样式的选择器 <head> <met ...
- python语法基础-网络编程-HTTP协议
############### HTTP协议 ############## """ 当你在浏览器地址栏敲入“http://www.cnblogs.com/”, ...
- vue 起步(一)
准备 安装nodejs(下载),Windows 安装包(.msi) npm相关 打开cmd查看npm版本, npm -v,如果没有安装npm,执行npm install npm -g进行安装 查询当前 ...
- 信贷风控模型开发----模型流程&好坏样本定义
第二章 模型开发流程&好坏样本定义 2.1模型开发流程 2.1.1 评分模型流程图 2.1.2流程图阐述 该小结提出了一些数据指标,如果不明白没有关系,往后的文章笔者会一个个地解释这些指标的含 ...