学习C++的要点
<11.10>
一、 C++的特性,面向对象的三大特征
- 封装:关键在于成员的访问权限,实现安全和对外接口隔离(开闭原则),相关使用:友元函数
- 继承:减少重复代码,调用基类函数,菱形继承
- 多态:重载,重写,重定义很详细,具体的差别在于是否在同一作用域,是否为继承关系的同名函数,是否含有相同参数类型,是否定义为虚函数,虚函数实现为动多态,普通重载和函数模板都是属于静多态。
二、友元函数 friend
-
友元函数可以是不属于任何类的非成员函数,也可以是另一个类的成员函数。
-
友元函数的特点:1) 不是当前类的成员函数,但它可以访问该类的所有成员,包括私有成员、保护成员和公有成员。2) 友元函数可以定义在类内,也可以定义在类外。3)友元是单向的,是友元函数能共享友元类的所有成员。
三、声明和定义
- 为变量分配地址和存储空间的称为定义,不分配地址的称为声明。一个变量可以在多个地方声明,
但是只在一个地方定义。加入 extern 修饰的是变量的声明,说明此变量将在文件以外或在文件后面部分定义。说明:很多时候一个变量,只是声明不分配内存空间,直到具体使用时才初始化,分配内存空间,如外部变量。
四、static,extern和const
- 静态变量或者类的创建存放在静态区数据段,代码运行前声明的全局变量都会初始值为0,代码运行时定义的静态变量不会随临时函数运行结束或者类的析构而释放。
- static的变量只能在类的静态成员函数只能访问类的静态成员(变量或函数)
- extern “C” 声明语句
extern “C” { 声明语句块 }
extern 表明该变量在别的地方已经定义过了,在这里要使用那个变量. - const 修饰常量,常用于函数返回引用做参数,不怕被修改,要注意修饰的位置。
五、C、C++程序编译的内存分配情况
C、C++中内存分配方式可以分为三种:
(1) 从静态存储区域分配:
内存在程序编译时就已经分配好,这块内存在程序的整个运行期间都存在。速度快、不容易出错,因为有系统会善后。例如全局变量,static 变量等。
(2) 在栈上分配:
在执行函数时,函数内局部变量的存储单元都在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
(3) 从堆上分配:
即动态内存分配。程序在运行的时候用 malloc 或 new 申请任意大小的内存,程序员自己负责在何时用 free 或 delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活。如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内存泄漏,另外频繁地分配和释放不同大小的堆空间将会产生堆内碎块。
一个 C、C++程序编译时内存分为 5 大存储区:堆区、栈区、全局区、文字常量区、程序代码区。
<11.11>
六、虚函数和虚函数表
-
重载—编译期多态的体现
重载,是指在一个类中的同名不同参数的函数调用,这样的方法调用是在编译期间确定的。虚函数—运行期多态的体现
运行期多态发生的三个条件:继承关系、虚函数覆盖、父类指针或引用指向子类对象。 -
虚函数的实现过程:通过对象内存中的vptr找到虚函数表vtbl,接着通过vtbl找到对应虚函数的实现区域并进行调用。
-
虚函数表存放在含有虚函数对象最开始的位置,通过函数指针来调用函数,虚函数按照其声明顺序放于表中,父类的虚函数在子类的虚函数前面。
-
只要类里有虚函数就会有虚函数表,该类的派生类也会有虚函数表。
-
如果发生菱形继承,虚函数表会记录基类的虚函数表指针的偏移量而不会产生重复。
-
纯虚函数:派生类必须声明基类的纯虚函数。
virtual void print() = 0; -
含有纯虚函数属于抽象类,不能进行实例化,只能由派生类实例化。
-
构造函数不能为虚函数,析构函数要为虚函数。
七、内存泄漏
①内存泄漏:分配了内存而没有释放,逐渐耗尽内存资源,导致系统崩溃
②内存越界:向系统申请一块内存,使用的时候超出了申请的范围
③内存踩踏:也是内存重叠,一般在拷贝过程中覆盖了不属于该空间的内容
④内存溢出:想要的内存超出了系统能够提供的
⑤野指针:指向被释放内存或是访问受限内存的指针
八、引用头文件
#include “header.h”
//表明当前文件和"header.h"处于同一工程且同一目录
#include <header.h>
//表明header.h和当前文件不属于同一工程,是外部引用
#pragma once
#pragma once在想要保护的文件开头写入,一般由编译器提供保证:同一个文件不会被包含多次。这里所说的”同一个文件”是指物理上的一个文件,而不是指内容相同的两个文件。无法对一个头文件中的一段代码作#pragma once声明,而只能针对文件。此方式不会出现宏名碰撞引发的奇怪问题,大型项目的编译速度也因此提供了一些。
使用#ifdef、#ifndef、#else、#endif来避免头文件重复包含
#ifndef _STDIO_H_ //在文件的开头(如果没定义,则运行下列代码)
#define _STDIO_H_
... //文件内容写中间
#endif //在文件结尾
九、模板与泛型
处理不同参数类型但是具有相同操作的便捷方式,重点在于格式→学习链接
十、C++11的特性
十一、typedef 和define 区别
-
用法不同:
①typedef 用来定义一种数据类型的别名,增强程序的可读性。
②define 主要用来定义 常量,以及书写复杂使用频繁的宏。 -
执行时间不同
typedef 是编译过程的一部分,有类型检查的功能。
define 是宏定义,是预编译的部分,其发生在编译之前,只是简单的进行字符串的替换,不进行类型的检查。 -
作用域不同:
typedef 有作用域限定。
define 不受作用域约束,只要是在define 声明后的引用 都是正确的。
学习数据结构
学习服务器开发
学习网络通信
请你说说 DNS 解析过程以及 DNS 劫持
解题思路
得分点 接收到错误的IP地址 标准回答 DNS查询的过程简单描述就是:主机向本地域名服务器发起某个域名的DNS查询请求,如果本地域名服务器查询到对应IP,就返回结果,否则本地域名服务器直接向根域名服务器发起DNS查询请求,要么返回结果,要么告诉本地域名服务器下一次的请求服务器IP地址,下一次的请求服务器可能是顶级域名服务器也可能还是根域名服务器,然后继续查询。循环这样的步骤直到查询到结果,本地域名服务器拿到结果返回给主机。 在完成整个域名解析的过程之后,并没有收到本该收到的IP地址,而是接收到了一个错误的IP地址。比如输入的网址是百度,但是却进入了奇怪的网址,并且地址栏依旧是百度。在这个过程中,攻击者一般是修改了本地路由器的DNS地址,从而访问了一个伪造的DNS服务器,这个伪造的服务器解析域名的时候返回了一个攻击者精心设计的网站,这个网站可能和目标网站一模一样,当用户输入个人账户时,数据会发送给攻击者,从而造成个人财产的丢失。 加分回答 预防DNS劫持可以通过以下几种方法: 1. 准备多个域名,当某个域名被劫持时,暂时使用另一个 2. 手动修改DNS,在地址栏输入http://192.168.1.1,进入路由器配置,填写主DNS服务器为114.114.114.114,填写备用DNS服务器为8.8.8.8 3. 修改路由器密码, 4. 给运营商打投诉电话,讲明被劫持的情况
学习Linux
请你介绍一下死锁,产生的必要条件,产生的原因,怎么预防死锁
解题思路
得分点 争夺共享资源、相互等待、互斥条件、请求和保持条件、不剥夺条件、环路等待条件、竞争资源、进程间推进顺序非法、有序资源分配法、银行家算法 标准回答 1. 死锁 两个或两个以上的进程在执行过程中,因争夺共享资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁。这些永远在互相等待的进程称为死锁进程。 2. 产生死锁的必要条件 虽然进程在运行过程中,可能发生死锁,但死锁的发生也必须具备一定的条件,死锁的发生必须具备以下四个必要条件: - 互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放; - 请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放; - 不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放; - 环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合 {P0,P1,P2,···,Pn} 中的 P0 正在等待一个 P1 占用的资源;P1 正在等待 P2 占用的资源,……,Pn 正在等待已被 P0 占用的资源。 3. 产生死锁的原因 - 竞争资源 - 进程间推进顺序非法 4. 预防死锁 - 有序资源分配法 - 银行家算法
学习游戏开发相关
学习多线程编程
学习数据库
学习UML
UML使用
-
基类和派生类的使用(类与类之间的关系——继承(泛化))
-
1对多的关系(关联关系)
-
单向关联关系(A has B,彼此并不负责对方的声明周期)
-
聚合关系 (整体部分不负责局部对象的销毁,声明周期不受整体的关系,菱形)
-
组合关系(依赖关系)A use B 是偶然的,临时的,并非固定的
在代码上:B作为A的成员函数参数 B作为A的成员函数的局部变量(B作为A的成员函数返回值) A的成员函数调用B的静态方法
-
依赖关系(单例模式)
学习 设计模式六大原则理解
总结:
单一职责原则告诉我们实现类要职责单一;
里氏替换原则告诉我们不要破坏继承体系;
依赖倒置原则告诉我们要面向接口编程;
接口隔离原则告诉我们在设计接口的时候要精简单一;
迪米特法则告诉我们要降低耦合。
而开闭原则是总纲,他告诉我们要对扩展开放,对修改关闭。
最后总结一下如何去遵守这六个原则。
对这六个原则的遵守并不是是和否的问题,而是多和少的问题,也就
我们一般不会说有没有遵守,而是说遵守程度的多少。
任何事都是过犹不及,设计模式的六个设计原则也是一样,
制定这六个原则的目的并不是要我们刻板的遵守他们,而需要根据实
对他们的遵守程度只要在一个合理的范围内,就算是良好的设计。
内存空间占用
空类的大小是1,在C++中空类会占用1个字节,这是为了让对象的实例能够相互区别。具体来说,空类同样可以被实例化,并且每个实例在内存中都有独一无二的地址,因此,编译器会给空类隐含加上一个字节,这样空类实例化后就会拥有独一无二的内存地址。当该空白类作为基类时,该类的大小就优化为零了,子类的大小就是子类本身的大小。这就是所谓的空白基类最优化。
空类的实例大小就是类的大小,所以sizeof(a)=1字节,如果a是指针,则sizeof(a)就是指针的大小
对于虚函数,在64位机器上为8,因为有虚函数的类对象中都有一个虚函数表指针,其大小是8字节
静态成员存放在静态存储区,不占用类的大小,普通函数也不占用类的大小