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 访问控制

主要由privatepublic关键字来描述,使用类对象的程序可以直接访问公有部分(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 thisdelete实质就是调用析构函数,回收内存资源,但如果在析构函数中使用,则会造成无限递归,无限消耗资源,会造成程序卡死。

二、 类的继承

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++(一)--类》相关推荐

  1. ComeFuture英伽学院——2020年 全国大学生英语竞赛【C类初赛真题解析】(持续更新)

    视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...

  2. ComeFuture英伽学院——2019年 全国大学生英语竞赛【C类初赛真题解析】大小作文——详细解析

    视频:ComeFuture英伽学院--2019年 全国大学生英语竞赛[C类初赛真题解析]大小作文--详细解析 课件:[课件]2019年大学生英语竞赛C类初赛.pdf 视频:2020年全国大学生英语竞赛 ...

  3. 信息学奥赛真题解析(玩具谜题)

    玩具谜题(2016年信息学奥赛提高组真题) 题目描述 小南有一套可爱的玩具小人, 它们各有不同的职业.有一天, 这些玩具小人把小南的眼镜藏了起来.小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的 ...

  4. 信息学奥赛之初赛 第1轮 讲解(01-08课)

    信息学奥赛之初赛讲解 01 计算机概述 系统基本结构 信息学奥赛之初赛讲解 01 计算机概述 系统基本结构_哔哩哔哩_bilibili 信息学奥赛之初赛讲解 02 软件系统 计算机语言 进制转换 信息 ...

  5. 信息学奥赛一本通习题答案(五)

    最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...

  6. 信息学奥赛一本通习题答案(三)

    最近在给小学生做C++的入门培训,用的教程是信息学奥赛一本通,刷题网址 http://ybt.ssoier.cn:8088/index.php 现将部分习题的答案放在博客上,希望能给其他有需要的人带来 ...

  7. 信息学奥赛一本通 提高篇 第六部分 数学基础 相关的真题

    第1章   快速幂 1875:[13NOIP提高组]转圈游戏 信息学奥赛一本通(C++版)在线评测系统 第2 章  素数 第 3 章  约数 第 4 章  同余问题 第 5 章  矩阵乘法 第 6 章 ...

  8. 信息学奥赛一本通题目代码(非题库)

    为了完善自己学c++,很多人都去读相关文献,就比如<信息学奥赛一本通>,可又对题目无从下手,从今天开始,我将把书上的题目一 一的解析下来,可以做参考,如果有错,可以告诉我,将在下次解析里重 ...

  9. 信息学奥赛一本通(C++版) 刷题 记录

    总目录详见:https://blog.csdn.net/mrcrack/article/details/86501716 信息学奥赛一本通(C++版) 刷题 记录 http://ybt.ssoier. ...

  10. 最近公共祖先三种算法详解 + 模板题 建议新手收藏 例题: 信息学奥赛一本通 祖孙询问 距离

    首先什么是最近公共祖先?? 如图:红色节点的祖先为红色的1, 2, 3. 绿色节点的祖先为绿色的1, 2, 3, 4. 他们的最近公共祖先即他们最先相交的地方,如在上图中黄色的点就是他们的最近公共祖先 ...

最新文章

  1. 王者荣耀服务器维护1月9号,王者荣耀1月9日维护到几点 王者荣耀1月9日几点能上游戏?...
  2. 庞锋 OpenCV 视频 学习进度备忘
  3. git 拉取远程分支及修改远程仓库地址
  4. Vue+Openlayers+el-radio实现切换地图显示
  5. mysql网页化_页面化操作数据库
  6. 【CodeForces - 227C】Flying Saucer Segments (思维)
  7. java nio有哪些功能_如何真正理解java中的NIO?
  8. 企业需求的Java程序员是什么样子的
  9. 在线HTML编辑器 KindEditor
  10. jquery实现页面加载进度条(转)
  11. React+TypeScript+webpack4多入口项目搭建
  12. 为什么我们选择 Flutter 开发移动应用?
  13. 开源操作系统 OpenBSD 被曝四个严重的认证绕过和提权漏洞(详情)
  14. c#解决TCP“粘包”问题
  15. 城域容灾体系的突破性进展
  16. LINUX编译Android doubango
  17. centos7:安装配置 virtualbox 增强功能 VBoxGuestAdditions,并实现物理机脚本控制虚拟机
  18. python怎么爬取新浪微博数据中心_新浪微博数据爬取研究
  19. 自然语言处理的会议、论文集下载
  20. Django文档学习

热门文章

  1. android 加载过程,Android View (2) View的加载过程
  2. qt mysql驱动不能用了,Qt使用msvc编译MySQL驱动_MySQL
  3. python热成像_matplotlib实现热成像图colorbar和极坐标图的方法
  4. Java基础,使用switch分支实现出计算器计算机功能,简单易理解
  5. java的addattribute_Java AttributeSet.addAttributeListener方法代码示例
  6. VideoView播放视频会引起其它音乐播放器暂停问题解决
  7. RHCS集群 切换时defunct僵尸进程解决方案
  8. 批处理命令调用WINRAR对文件进行压缩
  9. 嗯嗯------摘抄
  10. Collection与Map