《C++(一)--类》
C++(一)–类
c++是面向对象的语言,和面向过程的C语言相比,更加符合人的思维方式,于是就有了C++中的类;一般来说,应该将类的结构定义在头文件,类方法的实现放在源文件中,这是一种常见的编程方法。
参考文献:C++ Primer Plus 第六版
https://pan.baidu.com/s/175oHy40JuiN6kCLv1bf7Mw
提取码:2gql
目录
- 一、 类的实现
- 1.1 类成员函数
- 1.2 访问控制
- 1.3 内联函数
- 1.4 this指针
- Tips:
- 二、 类的继承
- Tips:
- 三、 多态性
- 四、 静态成员
一、 类的实现
C++类的实现通过关键字class来实现,实现的方法为:
class 类名
{类体
};
创建对象的方法:
类名 对象名;
例:
class MyObject
{public:MyObject();};
MyObject::MyObject()
{cout<<"MyObject constructed!\n";
}int main()
{MyObject object;//实例化对象return 0;
}
1.1 类成员函数
类成员函数和普通函数类似,可以有返回类型和参数。
定义成员函数,使用作用域解析运算符(::)来标识函数所属的类
类方法可以访问类private成员
定义方法:
类体内:函数类型 函数名(参数);
类体外:函数类型 类名::函数名(参数)
{函数体
}
1.2 访问控制
主要由private
和public
关键字来描述,使用类对象的程序可以直接访问公有部分(public
修饰),但是只能通过公有成员函数或者友元函数来访问对象的私有成员(private
修饰),公有成员函数是程序和对象的私有成员之间的桥梁。
类对象的默认访问控制方式是private;
例:
class MyObject
{public:MyObject();void getValue();private:int a;};
MyObject::MyObject()
{cout<<"MyObject constructed!\n";
}
void MyObject::getValue()
{this->a=7;//访问私有成员变量
}int main()
{MyObject object;object.getValue();//object.a;//变量a是私有成员,无法在程序中直接访问return 0;
}
1.3 内联函数
内联函数可以解决程序中调用效率的问题,一般不超过七八行,尽量不使用循环和递归
1. 位于类体内的声明的函数会被默认为内联函数
class MyObject
{public:MyObject();void getValue();//内联函数void setValue(){this->a = a-2;}private:int a;
};
2. 位于类体外声明的函数可以通过inline关键字声明
class MyObject
{public:MyObject();void setValue1();private:int a;
};
inline void MyObject::setValue1()
{this->a = a-3;
}
1.4 this指针
this指针指向用来调用成员函数的对象。每一个成员函数都有一个this指针指向调用对象。
Tips:
1. this指针是一个对象的地址,要返回一个对象本身,需要使用*this
,即return *this
2. 可以在成员函数中使用delete this
,编译器能够通过,但是当在成员函数中实现delete this时,相当于执行了一次析构函数,回收内存资源,所以这个时候不能再访问成员变量,但成员函数还可以实现。
3. 不能在析构函数中使用delete this
,delete
实质就是调用析构函数,回收内存资源,但如果在析构函数中使用,则会造成无限递归,无限消耗资源,会造成程序卡死。
二、 类的继承
C++提供了类继承的方法来实现对原有类特征的继承,是一种更高层次的代码重用性,可以在已有类的基础上添加新功能。
原始类称为基类,继承的类称为派生类。
例:
class Base
{public:Base();~Base(){cout<<"基类析构函数执行\n";}void setValueA(int value=1);int a;
};Base::Base()
{cout<<"基类被创建!\n";
}
void Base::setValueA(int value)
{a = value;
}class child:public Base//公有继承
{public:child();~child(){cout<<"子类析构函数执行\n";}void setValueB(int value=2);int b;
};child::child()
{cout<<"子类被创建!\n";
}
void child::setValueB(int value)
{b = value;
}int main()
{child childOne;//实例化childOne.setValueB(7);cout<<childOne.b<<endl;return 0;
}
创建子类childOne
时,会先调用基类的构造函数,再调用子类的构造函数;对于析构函数刚好相反,在执行delete
之后,先调用子类的析构函数,再调用基类的析构函数,是一种入栈出栈 的方式,先进后出 的原则。
输出结果:
Tips:
1. 对于公有继承,基类的成员的访问保持现状,公有继承派生类对象可以访问基类的公有成员,但不能访问基类的保护成员和私有成员,同时派生类成员函数 可以访问基类保护成员和公有成员;
2. 对于私有继承,基类的公有成员和保护成员变为派生类的私有成员,基类的私有成员只能通过基类接口访问;派生类对象不能访问基类所有成员,派生类成员函数 可以访问基类保护成员和公有成员;
3. 保护继承是特殊的私有继承,基类的公有成员和保护成员都会变为派生类的保护成员。当从派生类再派生一个类时,私有继承和保护继承的区别就体现出来了,使用私有继承时,第三代类不能使用基类的接口(一般指类成员函数),因为已经成为了派生类(第二代类)私有方法;但如果是保护继承,第三代类依旧可以使用它们。派生类对象不能访问基类所有成员,派生类成员函数 可以访问基类保护成员和公有成员;
4. 无论是哪种继承方式,基类的私有成员只能通过基类的接口来访问。要想访问基类私有成员,可以使用友元类来访问;
5. 公有继承和保护继承可以隐式向上转换,即将基类的指针或引用指向派生类对象,但保护继承只能在派生类中实现。保护继承、私有继承在派生类外都不支持隐式向上转换。
例:
class Base
{public:Base();~Base(){cout<<"基类析构函数执行\n";}void setValueA(int value=1);int a;
};Base::Base()
{cout<<"基类被创建!\n";
}
void Base::setValueA(int value)
{a = value;
}class child:public Base
{public:child();~child(){cout<<"子类析构函数执行\n";}void setValueB(int value=2);int b;
};child::child()
{cout<<"子类被创建!\n";
}
void child::setValueB(int value)
{b = value;
}int main()
{//基类的指针指向派生类对象Base *A = new child;delete A;return 0;
}
执行结果:
但是我们发现一个问题,派生类的析构函数并没有被执行,不会释放新类成员指向的内存,因为析构函数的目的就是释放内存。所以为了实现不仅仅对A指针的清理,还要清理基类子对象的存,需要将析构函数申明为虚函数(virtual关键字,只需在基类声明即可),即虚析构函数。
参考:c++ Primer Plus P501(4.为何需要虚析构函数) P505(2.析构函数)
class Base
{public:
Base();
//虚析构函数virtual ~Base(){cout<<"基类析构函数执行\n";}void setValueA(int value=1);int a;
};Base::Base()
{cout<<"基类被创建!\n";
}
void Base::setValueA(int value)
{a = value;
}class child:public Base
{public:child();~child(){cout<<"子类析构函数执行\n";}void setValueB(int value=2);int b;
};child::child()
{cout<<"子类被创建!\n";
}
void child::setValueB(int value)
{b = value;
}int main()
{//实例化对象Base *A = new child;delete A;return 0;
}
执行结果:
三、 多态性
C++通过虚函数(virtual
关键字修饰)来实现多态性,其主要的思想就是通过重写(override
)基类的成员函数来实现对方法的修改、重写。
指向基类的引用和指针可以引用派生类对象,不必进行显式类型转换:
child childOne;
Base *pb = &childOne;
Base &rb = childOne;
将派生类的引用或指针转换为基类引用或指针被称为向上强制转换,同理,将基类引用或指针转换为派生类的引用或指针,称为向下强制转换。如果不使用显式类型转换,向下强制转换是不允许的。(一般向上强制转换的需要虚析构函数)
(C++ Primer Plus P502 13.4.1 指针和引用类型的兼容性)
例如以下程序:
#include <iostream>using namespace std;class Base
{public:Base(){cout<<"基类被创建!\n";}virtual ~Base(){cout<<"基类析构函数执行\n";}void setValue(int value=1){cout<<"Base is :"<<value<<endl;}
};class child:public Base
{public:child(){cout<<"子类被创建!\n";};~child(){cout<<"子类析构函数执行\n";}void setValue(int value=2){cout<<"child is :"<<value<<endl;};
};int main()
{Base *A = new child;A->setValue();delete A;return 0;
}
基类和派生类中都有setValue
函数,在main
函数中,当执行setValue
函数时,我们的意图是执行派生类的setValue
函数,但是执行的结果为:
为了重写setValue函数,执行派生类的setValue
,我们需要在基类的函数中声明virtual
,这时候就能执行派生类的函数。我们预测的输出为child is :2
,但执行的结果为:
为什么value不是2呢?
因为重写虚函数时,默认的形参是不会被重写的,会继承基类的默认形参,所以输出的是1而不是2。
四、 静态成员
将成员函数声明为静态(static
关键字修饰),则不能通过对象来调用静态成员函数,同时静态成员函数不能够使用this
指针。另外,静态成员函数可以访问静态数据成员,但是不能访问类内其他数据成员。
要调用静态成员函数,需要作用域解析运算符(::)来标识函数所属的类。静态成员函数并不属于某个对象,它只属于某个类,与任何对象无关。
class test
{public:static int changeValue(int num)
{//this->b = 8//静态成员函数没有this指针,也不能调用成员变量bnum = num *2;num = num + a;return num;}
static int a;
int b;
};
int test::a = 8;//初始化静态数据变量int main()
{int b = test::changeValue(3);cout<<"b is :"<<b<<endl;return 0;
}
输出结果:
若创建一个对象test object;则在静态成员函数中访问静态成员变量a的方法有:
int changeValue {object.a = 8;}int changeValue { a = 8;}int changeValue {test::a = 8;}不能通过this指针访问
《C++(一)--类》相关推荐
- ComeFuture英伽学院——2020年 全国大学生英语竞赛【C类初赛真题解析】(持续更新)
视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...
- ComeFuture英伽学院——2019年 全国大学生英语竞赛【C类初赛真题解析】大小作文——详细解析
视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...
- 信息学奥赛真题解析(玩具谜题)
玩具谜题(2016年信息学奥赛提高组真题) 题目描述 小南有一套可爱的玩具小人, 它们各有不同的职业.有一天, 这些玩具小人把小南的眼镜藏了起来.小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的 ...
- 信息学奥赛之初赛 第1轮 讲解(01-08课)
信息学奥赛之初赛讲解 01 计算机概述 系统基本结构 信息学奥赛之初赛讲解 01 计算机概述 系统基本结构_哔哩哔哩_bilibili 信息学奥赛之初赛讲解 02 软件系统 计算机语言 进制转换 信息 ...
- 信息学奥赛一本通习题答案(五)
最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...
- 信息学奥赛一本通习题答案(三)
最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...
- 信息学奥赛一本通 提高篇 第六部分 数学基础 相关的真题
第1章 快速幂 1875:[13NOIP提高组]转圈游戏 信息学奥赛一本通(C++版)在线评测系统 第2 章 素数 第 3 章 约数 第 4 章 同余问题 第 5 章 矩阵乘法 第 6 章 ...
- 信息学奥赛一本通题目代码(非题库)
为了完善自己学c++,很多人都去读相关文献,就比如<信息学奥赛一本通>,可又对题目无从下手,从今天开始,我将把书上的题目一 一的解析下来,可以做参考,如果有错,可以告诉我,将在下次解析里重 ...
- 信息学奥赛一本通(C++版) 刷题 记录
总目录详见:https://blog.csdn.net/mrcrack/article/details/86501716 信息学奥赛一本通(C++版) 刷题 记录 http://ybt.ssoier. ...
- 最近公共祖先三种算法详解 + 模板题 建议新手收藏 例题: 信息学奥赛一本通 祖孙询问 距离
首先什么是最近公共祖先?? 如图:红色节点的祖先为红色的1, 2, 3. 绿色节点的祖先为绿色的1, 2, 3, 4. 他们的最近公共祖先即他们最先相交的地方,如在上图中黄色的点就是他们的最近公共祖先 ...
最新文章
- 王者荣耀服务器维护1月9号,王者荣耀1月9日维护到几点 王者荣耀1月9日几点能上游戏?...
- 庞锋 OpenCV 视频 学习进度备忘
- git 拉取远程分支及修改远程仓库地址
- Vue+Openlayers+el-radio实现切换地图显示
- mysql网页化_页面化操作数据库
- 【CodeForces - 227C】Flying Saucer Segments (思维)
- java nio有哪些功能_如何真正理解java中的NIO?
- 企业需求的Java程序员是什么样子的
- 在线HTML编辑器 KindEditor
- jquery实现页面加载进度条(转)
- React+TypeScript+webpack4多入口项目搭建
- 为什么我们选择 Flutter 开发移动应用?
- 开源操作系统 OpenBSD 被曝四个严重的认证绕过和提权漏洞(详情)
- c#解决TCP“粘包”问题
- 城域容灾体系的突破性进展
- LINUX编译Android doubango
- centos7:安装配置 virtualbox 增强功能 VBoxGuestAdditions,并实现物理机脚本控制虚拟机
- python怎么爬取新浪微博数据中心_新浪微博数据爬取研究
- 自然语言处理的会议、论文集下载
- Django文档学习
热门文章
- android 加载过程,Android View (2) View的加载过程
- qt mysql驱动不能用了,Qt使用msvc编译MySQL驱动_MySQL
- python热成像_matplotlib实现热成像图colorbar和极坐标图的方法
- Java基础,使用switch分支实现出计算器计算机功能,简单易理解
- java的addattribute_Java AttributeSet.addAttributeListener方法代码示例
- VideoView播放视频会引起其它音乐播放器暂停问题解决
- RHCS集群 切换时defunct僵尸进程解决方案
- 批处理命令调用WINRAR对文件进行压缩
- 嗯嗯------摘抄
- Collection与Map