MATLAB中白噪声的WGN和AWGN函数的使用如下: MATLAB中产生高斯白噪声非常方便,可以直接应用两个函数,一个是WGN,另一个是AWGN.WGN用于产生高斯白噪声,AWGN则用于在某一 信号中加入高斯白噪声. 1. WGN:产生高斯白噪声y = wgn(m,n,p) 产生一个m行n列的高斯白噪声的矩阵,p以dBW为单位指定输出噪声的强度.y = wgn(m,n,p,imp)
Python是一门优秀的综合语言, Python的宗旨是简明、优雅、强大,
在人工智能、云计算、金融分析、大数据开发、WEB开发、自动化运维、测试等方向应用广泛
2、通过什么途径学习的Python?
Python:轻量级、易学、***/开放源码软件、可移植性、支持面向对象、丰富的库、规范的代码。 Java:优点:开源性,功能强大,库多 缺点:编译速度 比较慢,不完全 PHP:优点:性能很强,配合简单,稳定,容易部署。 缺点:函数命名不规范,驼峰法和下划线,传参位置不一。 C: 优点:能操纵底层,能细粒度优化性能。 缺点:1、是面向过程的,2、运行时类型检查不可用,3、不提供命名空间功能,4、构 造函数和析构函数不可用。 C#: 优点: 强大的.NET Framework托管代码集合类,较简单的语言特性。WEB应用程序 缺点:底层和高性能不合适,Windows平台以外支持有限。 C++: 优点:性能比较高,可进化型。
4、简述解释型和编译型编程语言?
5、Python解释器种类以及特点?
位:"位(bit)"是电子计算机中最小的数据单位。每一位的状态只能是0或1。 字节:8个二进制位构成1个"字节(Byte)",它是存储空间的基本计量单位。1个字节可以储存1个英文字母或者半个汉字,换句话说,1个汉字占据2个字节的存储空间。
8、请至少列举5个 PEP8 规范(越多越好)。
9、通过代码实现如下转换:
十进制转换成十六进制:v = 87
10、请编写一个函数实现将IP地址转换成一个整数。
11、python递归的最大层数?
14、字节码和机器码的区别?
机器码(machine code),学名机器语言指令,有时也被称为原生码(Native Code),是电脑的CPU可直接解读的数据。 通常意义上来理解的话,机器码就是计算机可以直接执行,并且执行速度最快的代码。 用机器语言编写程序,编程人员要首先熟记所用计算机的全部指令代码和代码的涵义。手编程序时,程序员得自己处理每条指令和每一数据的存储分配和输入输出,还得记住编程过程中每步所使用的工作单元处在何种状态。这是一件十分繁琐的工作,编写程序花费的时间往往是实际运行时间的几十倍或几百倍。而且,编出的程序全是些0和1的指令代码,直观性差,还容易出错。现在,除了计算机生产厂家的专业人员外,绝大多数的程序员已经不再去学习机器语言了。 机器语言是微处理器理解和使用的,用于控制它的操作二进制代码。8086到Pentium的机器语言指令长度可以从1字节到13字节。尽管机器语言好像是很复杂的,然而它是有规律的。存在着多至100000种机器语言的指令。这意味着不能把这些种类全部列出来。总结:机器码是电脑CPU直接读取运行的机器指令,运行速度最快,但是非常晦涩难懂,也比较难编写,一般从业人员接触不到。 字节码(Bytecode)是一种包含执行程序、由一序列 op 代码/数据对 组成的二进制文件。字节码是一种中间码,它比机器码更抽象,需要直译器转译后才能成为机器码的中间代码。 通常情况下它是已经经过编译,但与特定机器码无关。字节码通常不像源码一样可以让人阅读,而是编码后的数值常量、引用、指令等构成的序列。 字节码主要为了实现特定软件运行和软件环境、与硬件环境无关。字节码的实现方式是通过编译器和虚拟机器。编译器将源码编译成字节码,特定平台上的虚拟机器将字节码转译为可以直接执行的指令。字节码的典型应用为Java bytecode。 字节码在运行时通过JVM(JAVA虚拟机)做一次转换生成机器指令,因此能够更好的跨平台运行。 总结:字节码是一种中间状态(中间码)的二进制代码(文件)。需要直译器转译后才能成为机器码。
15、三元运算规则以及应用场景?
三元运算又称三目运算,是对简单的条件语句的简写: 为真时的结果 if 判断条件 else 为假时的结果(注意,没有冒号)
Python2默认的字符编码是ASCII,默认的文件编码也是ASCII ; print语句没有了,取而代之的是print()函数。 在python 2.x中/除法整数相除的结果是一个整数,把小数部分完全忽略掉,浮点数除法会保留小数点的部分得到一个浮点数的结果。 在python 3.x中/除法不再这么做了,对于整数之间的相除,结果也会是浮点数。 Py3.X去除了long类型,现在只有一种整型——int,但它的行为就像2.X版本的long
python2有非浮点数准备的int和long类型。int类型最大值不能超过sys.maxint,而且这个最大值是平台相关的。
可以通过在数字的末尾附上一个L来定义长整型,显然,它比int类型表示的数字范围更大。
python3里,只有一种整数类型int,大多数情况下,和python2中的长整型类似。
python2里,有两种方法获得一定范围内的数字:range(),返回一个列表,还有xrange(),返回一个迭代器。
21、列举布尔值为False的常见值?
22、字符串、列表、元组、字典每个常用的5个方法?
23、lambda表达式格式以及应用场景?
lambda函数就是可以接受任意多个参数(包括可选参数)并且返回单个表达式值得函数。 # 将上述一般函数改写为匿名函数: 应用:1.lambda函数比较轻便,即用即仍,适合完成只在一处使用的简单功能。 2.匿名函数,一般用来给filter,map这样的函数式编程服务 3.作为回调函数,传递给某些应用,比如消息处理。
*args代表位置参数,它会接收任意多个参数并把这些参数作为元组传递给函数。 **kwargs代表的关键字参数,允许你使用没有事先定义的参数名。 位置参数一定要放在关键字参数的前面。 作用:使用*args和**kwargs可以非常方便的定义函数,同时可以加强扩展性,以便日后的代码维护。
==是python标准操作符中的比较操作符,用来比较判断两个对象的value(值)是否相等; is也被叫做同一性运算符,这个运算符比较判断的是对象间的唯一身份标识,也就是id是否相同。
27、简述Python的深浅拷贝以及应用场景?
1、回收计数引用为0的对象,释放其占用空间 2、循环垃圾回收器。释放循环引用对象
29、Python的可变类型和不可变类型?
可变类型:list、dict、set、可变集合
# Python 字典 fromkeys() 函数用于创建一个新字典,以序列 seq 中元素做字典的键,value 为字典所有键对应的初始值。
32、列举常见的内置函数?
34、一行代码实现9*9乘法表
35、如何安装第三方模块?以及用过哪些第三方模块?
36、至少列举8个常用模块都有那些?
1、sys:用于提供对解释器相关的访问以及维护,并有很强的交互功能 3、os:用于提供操作系统模块 4、ashlib:用于加密相关的操作 5、random:生成随机变量 6、pickle:用于python特有的类和pthon的数据类型间进行转换 8、re:正则表达式模块
38、什么是正则的贪婪匹配?
因为b是可变类型,每次调用这个方法b不会每次都初始化[] 想每次执行只输出[1] ,默认参数应该设置为None
46、一行代码实现删除列表中重复的值 ?
集合的主要作用是去重和关系运算.
47、如何在函数中设置一个全局变量 ?
48、logging模块的作用?以及应用场景?
python的logging模块提供了标准的日志接口,你可以通过它存储各种格式的日志。
49、请用代码简单实现stack(栈)。
50、常用字符串格式化哪几种?
51、简述 生成器、迭代器、可迭代对象 以及应用场景?
生成器(generator):列表元素可以按照某种算法推算出来(有规律的数组),则可以在循环的过程中不断推算出后续的元素。这种方式就不必创建完整的list,可以节省大量的空间。python中,这种一边循环一边计算的机制,称为生成器:generator。 迭代器(Iterator):可以被next()函数调用并不断返回下一个值得对象称为迭代器(Iterator)。 可迭代对象(Iterable):可以直接作用于for循环的对象(其中包含集合数据类型:list\tuple\dict\set\str;还包含生成器表达式和生成器函数)。可以使用isinstance()判断一个对象是否是Iterable对象。
52、用Python实现一个二分查找的函数。
53、谈谈你对闭包的理解?
在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。 通俗易懂的说法:当某个函数被当成对象返回时,夹带了外部变量,就形成了一个闭包。 闭包存在的意义:它夹带了外部变量(私货)。同一个的函数夹带了不同的私货,就实现了不同的功能。
54、os和sys模块的作用?
os 模块提供了很多允许你的程序与操作系统直接交互的功能。 sys模块能帮助程序员访问与python解释器联系紧密的变量和函数。
55、如何生成一个随机数?
random模块可以很容易生成随机数和随机字符串。 string模块可以生成随机字符串
56、如何使用python删除一个文件?
57、谈谈你对面向对象的理解?
OOP(Object Oriented Programing)编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述。与面向过程机械式的思维方式形成鲜明对比,面向对象更加注重对现实世界而非流程的模拟,是一种“上帝式”的思维方式。 核心就是对象二字,对象就是特征与技能的结合体。 1、使程序更加容易扩展和易更改,使开发效率变得更高(对某个对象类属性的修改,会立刻反映到整个体系中) 2、基于面向对象的程序可以使他人更加容易理解代码逻辑。 编程复杂度高、可控性差(无法像面向过程程序那样精准预测问题处理过程与结果,对象之间的交互,比较难预测最终的结果) 应用于需求经常变化的软件中,一般需求的变化都集中在用户层,互联网应用,企业内部软件,游戏等都是面向对象的程序设计大显身手的好地方。
58、Python面向对象中的继承有什么特点?
在python中,新建的类可以继承一个或多个父类(其他语言只能继承一个父类),父类又可以称为基类或者超类。 仅在python2中才分新式类和经典类,在python3中,无论是否继承object,都默认继承object,即python3中所有类均为新式类。
59、面向对象深度优先和广度优先是什么?
在子类继承多个父类时,属性查找方式分深度优先和广度优先两种。 当类是经典类时,多继承情况下,在要查找属性不存在时,会按照深度优先方式查找下去。 当类是新式类时,多继承情况下,在要查找属性不存在时,会按照广度优先方式查找下去。
60、面向对象中super的作用?
super() 函数是用于调用父类(超类)的一个方法。 super 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。 MRO 就是类的方法解析顺序表, 其实也就是继承父类方法时的顺序表。 1、根据 mro 的顺序执行方法 2、主动执行Base类的方法
61、是否使用过functools中的函数?其作用是什么?
Python的functools模块用以为可调用对象(callable objects)定义高阶函数或操作。
简单地说,就是基于已有的函数定义新的函数。 所谓高阶函数,就是以函数作为输入参数,返回也是函数。
62、列举面向对象中带双下划线的特殊方法,如:__new__、__init__
__dict__:查出一个字典,所有实例共享的变量和函数,dir()的子集 __name__:查看类的名字(这里的类名是字符串类型的) __init__:为对象定制自己独有的特征 __base__:只查看从左到右继承的第一个子类 __bases__:查看所有继承的父类 __format__:自定制格式化字符串
__call__:方法由对象后加括号触发,即:对象() 或者 类()()
63、如何判断是函数还是方法?
函数:函数是封装了一些独立的功能,可以直接调用,python内置了许多函数,同时可以自建函数来使用。 方法:方法和函数类似,同样封装了独立的功能,但是方法是需要通过对象来调用的,表示针对这个对象要做的操作,使用时采用点方法。
64、静态方法和类方法区别?
绑定方法:绑定给谁,就应该由谁来调用,谁来调用就会把调用者当作第一个参数自动传入 绑定到对象的方法:在类内定义的没有被任何装饰器修饰的,自动将对象当做第一个参数传入(属于类的函数,类可以调用,但是必须按照函数的规则来,没有自动传值那么一说) 绑定到类的方法:在类内定义的被装饰器@classmethod修饰的方法。自动将类当做第一个参数传入(其实对象也可以调用,但仍将类作为第一个参数传入) 非绑定方法:在类中用@staticmethod装饰器装饰的方法。不与类或对象绑定,类和对象都可以调用,但是没有自动传值那么一说。
65、列举面向对象中的特殊成员以及应用场景
66、1、2、3、4、5 能组成多少个互不相同且无重复的三位数
67、什么是反射?以及应用场景?
反射的概念是由Smith在1982年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力(自省)。 python面向对象中的反射:通过字符串的形式操作对象相关的属性。python中的一切事物都是对象(都可以使用反射)
实现可插拔机制,常用于web框架的CBV配置文件获取类。
元类是类的类,是类的模板。 元类作用:是控制如何创建类的,正如类是创建对象的模板一样,而元类的主要目的是为了控制类的创建行为。
69、用尽量多的方法实现单例模式。
# 单例:即单个实例,指的是同一个类实例化多次的结果指向同一个对象,用于节省内存空间 # 如果我们从配置文件中读取配置来进行实例化,在配置相同的情况下,就没必要重复产生对象浪费内存了 #方式一:定义一个类方法实现单例模式 #方式二:定制元类实现单例模式 # 事先先从配置文件中取配置来造一个Mysql的实例出来 # 上述两步可以合成下面一步 obj1=Mysql() # 没有传值则默认从配置文件中读配置来实例化,所有的实例应该指向一个内存地址 #方式三:定义一个装饰器实现单例模式
70、装饰器的写法以及应用场景。
71、异常处理写法以及如何主动跑出异常(应用场景)
72、什么是面向对象的mro
对于你定义的每一个类,python会计算出一个方法解析顺序(MRO)列表,这个MRO列表就是一个简单的所有基类的线性顺序列表。 python会在MRO列表上从左到右开始查找基类,直到找到第一个匹配这个属性的类为止。
75、json序列化时,可以处理的数据类型有哪些?如何定制支持datetime类型?
76、json序列化时,默认遇到中文会转换成unicode,如果想要保留中文怎么办?
77、什么是断言?应用场景?
assert断言——声明其布尔值必须为真判定,发生异常则为假。 设置一个断言目的就是要求必须实现某个条件。
with语句的作用是通过某种方式简化异常处理,它是所谓的上下文管理器的一种 当你要成对执行两个相关的操作的时候,这样就很方便,以上便是经典例子,with语句会在嵌套的代码执行之后,自动关闭文件。
这种做法的还有另一个优势就是,无论嵌套的代码是以何种方式结束的,它都关闭文件。
如果在嵌套的代码中发生异常,它能够在外部exception handler catch异常前关闭文件。
如果嵌套代码有return/continue/break语句,它同样能够关闭文件。
79、使用代码实现查看列举目录下的所有文件。
当一个函数中出现yield关键字的时候,那么这个函数就是一个生成器(generator)。 函数转化为generator后,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。
容器(Containers):容器存储数据
分配器(Allocators):分配器为容器分配内存
算法(Algorithms):算法处理容器的数据
迭代器(Iterators):迭代器为算法提供访问容器的方式
适配器(Adapters):仿函数适配器为仿函数使用算法提供支持
仿函式(Functors):仿函数为类似不同的类相加减提供支持
从语言层面来讲,STL的六大组件,其他的都是类模板(class template),只有算法属于函数模板(function template)
从功能流程上来说:算法是看不见容器的,它对容器一无所知,只能通过迭代器来获得所有需要的信息;
迭代器需要能够回答算法的所有需求,这样才能实现算法的功能;
迭代器是一种“能够遍历某个序列内的所有元素”的对象。它可以透过与一般指针一致的接口来完成自己的工作。不同的迭代器具有不同的”能力“(行进和存取能力)
仿函数(functor),就是使一个类的使用看上去像一个函数。具体实现就是类中重载了一个operator(),由此生成的类对象,在使用时就有了类似函数的行为,就是一个仿函数类了。
1.仿函数比一般函数更灵巧,因为它可以拥有状态。
2.每个仿函数都有其型别。因此可以将仿函数的型别当做template参数传递。
3.执行速度上,仿函数通常比函数指针更快。
容器适配器:典型代表就是 stack 和 queue,因为两者都是继承了sequence这种底层容器,然后只提供了empty()、size()等几种函数操作接口,这种改造就可以视为是一种容器适配器。
偏特化:有数量上的特化,和范围上的特化。
函数返回值是一个对象或者对象的引用,我们都可以用对象来接受返回值:
每写一个函数都应该去考虑是返回Reference还是Object,如果返回Reference那返回const reference会不会更好?关于返回值详细讨论如下:
如果只是存储确定或不确定的objects,而不去删除它们,我们可以选用vector。就是因为vector是数组的替代品,是连续内存的,不适合频繁的删除。
如果是key-value形式的,可以使用map。但是需要注意的是map的key是唯一的。
数据量很大,不在乎他们的排序,那么我们可以选用hash
首先需要明确每个stl容器的数据结构、以及优缺点。
其次是要明确自己对数据的操作,数量确定还是不确定,需不需要频繁的插入和删除,需不要进行排序,需不需要进行快速的查找,以及数据量是什么级别,几千条还是百万级等等。
接下来就是完成自己的工作了。
很多时候,在stl的世界里,只有两种人,一种是使用vector,另一种是使用list。
所有的根源还是在于数组和链表的区别:
插入效率低和插入效率高
删除效率低和删除效率高
排序效率高和排序效率低
这里补充一下map和set的区别:
说实话,在工作中我从来没有接触过set这个容器。
set(集合)——包含了经过排序了的数据,这些数据的值(value)必须是唯一的。
map(映射)——经过排序了的二元组的集 合,map中的每个元素都是由两个值组成,其中的key(键值,一个map中的键值必须是唯一的)是在排序或搜索时使用,它的值可以在容器中重新获取;而 另一个值是该元素关联的数值。比如,除了可以ar[43] = “overripe”这样找到一个数据,map还可以通过ar[“banana”] = “overripe”这样的方法找到一个数据。如果你想获得其中的元素信息,通过输入元素的全名就可以轻松实现。
map是映射集合中的元素不能重复,set可以进行集合的各种操作(交并补等),当然你也可以用list或vector实现set,但是效率会很低。set一般是用平衡树或哈西表实现的。
映射是一种一一对应的关系,哈西表也可以看作是映射的一种。映射通常可用来实现字典结构(dictionary)
和malloc,free函数不同,new和delete都是c++的运算符,所以就像+和=,类是可以进行重载即:运算符重载
当我们使用关键字new在堆上动态创建一个对象时,它实际上做了三件事:获得一块内存空间、调用构造函数、返回正确的指针
// 调用 pa 指向对象的析构函数,对打开的文件进行关闭。。
// 通过上面提到的标准库函数 operator delete 来释放该对象的内存,传入函数的参数为 pa 的值
禁止在堆或者栈上构造对象
在C++中,有这样一条规则:当用户试图在栈上创建对象时,编译器会查找匹配且可以访问的构造函数和析构函数,如果其中一个无法访问,编译就会报错。
使析构函数私有化,可以禁止在栈上创建对象。
将new和delete操作符声明为私有的即可
出错原因: 代码片段没有写在函数中。
解决方法: 将代码片段写进函数中。
出错原因: main.cpp中没有找到对应的函数名声明,没有在.cpp引用包含该函数名的头文件.h。
解决方法: 引入对应头文件。
类中的静态成员必须在类内声明,类外初始化,c++的静态成员属于整个类,而不属于某个对象,如果在类内初始化,会导致每个对象都包含该静态成员,这是矛盾的。
声明:指定变量的名字和类型,可以多次声明。
定义:为该成员变量分配存储空间,有且仅有一个定义。
在类的声明中,静态成员变量仅完成了声明过程,并没有进行定义和赋初值。
静态成员变量在编译时存储在静态存储区,即定义过程应该在编译时完成,因此一定要在类外进行定义,但可以不初始化。
最后一句话是不得体的。
std::size_t 可以用,但要考虑是否会出现0--情况,结果小于0则会溢出,变为一个特别大的数
atoi和strtol函数均是把字符串转换成整数,两者的不同点主要是:
1. atoi的返回值无法区分是正常的返回还是错误的返回,如:
两者返回的val均为0,因此无法区分哪个是正确parse后的值。
2. strtol函数对异常的返回可以设置errno,从而可以发现异常的返回,如:
3. strtol函数支持不同进制的转换,而atoi只支持十进制的转换。
信号是由操作系统传给进程的中断,会提早终止一个程序。在 UNIX、LINUX、Mac OS X 或 Windows 系统上,可以通过按 Ctrl+C 产生中断。
有些信号不能被程序捕获,但是下表所列信号可以在程序中捕获,并可以基于信号采取适当的动作。这些信号是定义在 C++ 头文件 <csignal> 中。
C++ 信号处理库提供了 signal 函数,用来捕获突发事件。以下是 signal() 函数的语法:
这个看起来有点费劲,以下语法格式更容易理解:
这个函数接收两个参数:第一个参数是一个整数,代表了信号的编号;第二个参数是一个指向信号处理函数的指针。
不管你想在程序中捕获什么信号,都必须使用 signal 函数来注册信号,并将其与信号处理程序相关联。看看下面的实例:
当上面的代码被编译和执行时,它会产生下列结果:
现在,按 Ctrl+C 来中断程序,您会看到程序捕获信号,程序打印如下内容并退出:
函数 raise() 发送信号,该函数带有一个整数信号编号作为参数,语法如下:
linux线程执行和windows不同,pthread有两种状态joinable状态和unjoinable状态,如果线程是joinable状态,当线程函数自己返回退出时或pthread_exit时都不会释放线程所占用堆栈和线程描述符(总计8K+)。只有当你调用了pthread_join之后这些资源才会被释放。若是unjoinable状态的线程,这些资源在线程函数退出时或pthread_exit时自动会被释放。
3. 其实简单的说就是在线程函数头加上 pthread_detach(pthread_self())的话,线程状态改变,在函数尾部直接 pthread_exit线程就会自动退出。省去了给线程擦屁股的麻烦。
这里主要讨论两种状态下,线程终止以及资源释放的问题
joinable状态下,当线程函数自己返回退出时或pthread_exit时都不会释放线程所占用堆栈和线程描述符。只有当你调用了pthread_join之后这些资源才会被释放,这是需要main函数或者其他线程去调用pthread_join函数。
其中jion就是等待线程执行完毕,可能还会有一些资源清理,比如返回的时候让操作去回收线程,等
其次 当你thread t(...)的时候这个线程就已经被创建,就已经向操作系统申请cpu资源了,并不是join后才开始执行
最后从单个线程来看,语句的执行顺序是不变的,比如int a; int b; thread t(...);那就是先执行a,再b,最后再创建线程t,不可肯定线程t的函数会比定义a,b先执行!
还有其实,sleep让线程沉睡,就是告诉操作系统,把这个线程加入到一个类似等待队列的地方,等若干秒再被喊醒,这个计时是由操作系统完成的,而不是说是某个线程来计时
你看操作系统,做了好多事呢
以追加的形式向文件写入,并且如果文件存在清空之前内容
关于初始化的方法消耗,做对比
再分析在构造函数初始化成员变量的时候(打印的所有调用情况全都是关于Wigdet的):
但是为什么多了一次默认构造,按理说初始化不应该只有copy assignment吗?
其实这不是对w的初始化,这是对w的赋值,在进入构造函数体之前默认构造已经被调用了
在写while的时候,Widget w;如果写在循环体外,则发生1次构造,1次析构和n次copy assignment,若写在循环体内则发生n次构造,n次析构 自己具体情况衡量哪种消耗更大。
1.对象在全局作用域或为静态局部对象时,则类的内置成员变量被初始化为0
2.对象在局部作用域定义时,则类的内置成员变量不被初始化
3.对于类类型成员按照其自身的(合成)默认构造函数进行初始化
SIGSEGV:在POSIX兼容的平台上,SIGSEGV是当一个进程执行了一个无效的内存引用,或发生段错误时发送给它的信号。SIGSEGV的符号常量在头文件signal.h中定义
无论何种数据,是否需要delete关键看其空间是否使用new分配
函数中定义的局部变量指针,它就是一个局部变量,所以是不用delete;同理,如果类中有一个指针数据成员pt并且pt的所有使用场景中没有为它new一块内存,那么该类的析构函数中没有必要去写delete pt的;
(手机打字真费劲) 所以在类或函数中,int* ,char* 类似这些只要不是new的,就不用释放,系统会自动把他们占的内存释放掉,还是那句只有new的空间才用手动的去delete
函数中如下定义 char* pt;类似这种都是局部变量,它们(pt)存在于栈上,比如int *p;p在栈上,而且p的值也是栈的一个地址
再举个例子,比如在某函数中有int *p = new int ;这时候,p这个变量是在栈上的,但p的值(是一个地址)是堆上的一个地址,如果函数结束而没有delete p,栈上的p已经被系统回收,但它指向的堆上的地址还一直被占用着,不能被其它使用,也无法删除了,这样就造成了内存泄漏,所以我们用完new出来的地址,一定要记得把这个地址释放掉(上面说的还有一种情况,比如那个函数返回值是int*,然后返回p,那可能就不会发生内存泄露了)
顺便说一下区别,new做两件事,一是分配内存,二是调用类的构造函数,delete会调用类的析构函数和释放内存,而malloc和free只是分配和释放内存(也就是说不会调用构造函数或者析构函数
顺便提一下在C语言中都是用malloc与free进行内存管理,同样一个malloc就一定有个free
但是如果当你用memcpy/strcpy,但之前你没有为name分配空间就不对了!
举个常见的内存溢出的例子吧
解释1:strcpy,会一直复制内存,直到遇到\0
解释2:内存溢出就是内存越界,内存越界有一种很常见的情况是调用栈溢出(stackoverflow),虽然这种情况可以看成是栈内存不足的一种体现,但内存溢出未必跟内存分配有什么关系,因为还有一种情况叫缓冲区溢出,就是下面要举的例子:
很显然之前为name分配的空间不够,而且编译器也提示了,所以运行的时候发生了错误
翻译:Stack Smashing实际上是gcc用来检测缓冲区溢出攻击的一种保护机制
很明显cin.get()就是读取一个字符,就是缓冲区的东西可以被cin拿走也可以被cin.get()拿走,拿走一个就少一个
比如输入 12 3 然后回车,其实你输入的就是 “12 空格 3 回车” 这四个字符都在缓冲区,12要是被cin拿走了,那cin.get只能拿空格了
但是嘞,要是两连续的cin 会拿12 和 3而不会拿空格 因为cin遇到回车与空格结束读取
引用是C++引出的概念,C中并没有这个概念!
引用声明完毕后,相当于目标变量名有两个名称,如ra=1; 等价于 a=1;
声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。故:对引用求地址,就是对目标变量求地址。&ra与&a相等。
而指针,是一个变量,编译器会为它分匹配内存单元!
大致意思是:赋值操作的左操作数必须使用左值!
注意是赋值操作(=)的左操作数,必须是左值,既然报错了用脚趾头想都知道左操作数(a = b)是右值!
让我们分析一下这个问题
在C++下:= 操作符返回的是个引用(左值)(a = b)返回a的引用,a的引用是个左值自然可以有a=10;
为什么在C下会报错?直接原因是=操作符返回的是rvalue
我理解的左值就是内存,右值就是常量,再通俗点,一般我们的变量(等号左侧的就是左值),比如int a=3,a就是一块内存,它是左值,3就是右值,你没见过3=a;这样的操作吧!
所以总结:可以取地址,或者是一块内存(这两句话貌似意思一样)都是左值
为什么说这?因为在想为什么copy assignment运算符应该返回对象的引用而不是对象本身!
通过上面的我想你也知道为什么需要返回引用了吧!
因为赋值操作会改变左值,而 operator+ 之类的运算符不会改变操作数[1],所以说赋值运算符重载要返回引用以用于类似 (a=b)=c 这样的再次对a=b[2]进行写操作的表达式。
operator+ 返回一个临时对象是合情合理的 ,你若返回引用大多数情况下也不会出错,但这就使(a+b)=c这样的表达式可以出现,这就有点不符合约定了[3]
对于三种继承,对基类里的private对象处理方式都是一致的,即对派生类都是不可访问的,但它确实被继承了,可以通过基类里的函数来访问。
三种继承的不同之处在于:公有继承是将基类的public和protected成员直接继承不改变其属性(即继承后依然是public和potected的),保护继承将基类的public和protected成员直接继承为派生类的protected成员。而私有继承则直接将基类的protected和public成员继承为派生类的private成员。
第二点的本质原因不是基类的private变量不能被继承,而是基类的private成员不能被子类直接访问。可以通过在基类中增加一个public函数用来返回基类的private成员,再在子类用调用该public函数来访问基类的private成员。
方法:把copy assignment操作符和copy构造函数声明为私有,不予实现。
需要注意的是这个做法并不安全,因为member函数和friend函数可以调用你的private函数!
有了上述当用户企图拷贝A对象,编译器会阻挠他!如果你不慎在member或friend函数之内那么做,轮到连接器发出抱怨!
将链接 期间错误移至编译器时可能的(而且那是好事,毕竟错误越早侦测越好)!怎么解决呢?只需将copy构造和copy assignment操作符声明为private就可以,但不是在A类自身,而是在一个专门为了阻止copying动作而设计的base class内,很简单:
为了组织A对象被拷贝,唯一需要做的就是继承Uncopyable
这行得通,任何人即便是member函数或者friend函数尝试拷贝A对象,编译器就会尝试生成一个copy构造函数和copy assignment操作符,正如条款12所说,这些函数的“编译器生成版”会尝试调用其base class的对应兄弟,哪些调用会被编译器拒绝,因为其base class的拷贝函数是private!
当用户没有显式定义析构函数时, 编译器同样会为对象生成一个默认的析构函数, 但默认生成的析构函数只能释放类的普通数据成员所占用的空间, 无法释放通过 new 或 malloc 进行申请的空间, 因此有时我们需要自己显式的定义析构函数对这些申请的空间进行释放, 避免造成内存泄露。
如果函数的形参是类的对象,则在进行函数调用时,将自动调用复制构造函数,这也是copy构造函数中的形参如果不是对象引用就会造成无限循环调用的原因。
我觉得需要提到的有两点:
比如一个类一段代码为:
用本质是const 指针),然后一个const objectValue也可以改变,着太荒谬了,所以如果你的类中有这些,编译器会拒绝为你生成operator=,你只能自己显式声明定义!
copy 构造函数是个重要的函数,它定义一个对象如何passed by value ,
拷贝构造函数被用来“以同型对象初始化自我对象”,copy assignment操作符被用来“从另一个同型对象中拷贝其值到自我对象”
当你看到赋值操作符要小心,有可能调用拷贝构造
如果有个新对象被定义,一定有个构造函数被调用(拷贝构造/默认构造/...
如果没有对象被定义,就不会有构造函数被调用,所以赋值操作当然会被调用
Effective C++ 中提到:除非我有一个更好的理由允许构造函数被用于隐式转换否则我会把它声明为explicit
有点困惑就去查了一下什么是隐式转换:
C++中,一个参数的构造函数(或者除了第一个参数外其余参数都有缺省值的多参构造函数),承担了两个角色
例如一个类A的构造函数A(int i)就是,既可以用来作为构造器,又可以实现隐式转换A a=1;因为1可以通过构造函数A(int i)转换为一个类A的对象
如果你不想让类似情况发生,可以在构造函数前加explicit,这可以阻止构造函数被用来执行隐式转换!
this是指向自身对象的指针,*this是自身对象
也就是说return *this返回的是当前对象的克隆或者本身(若返回类型为A, 则是克隆, 若返回类型为A&, 则是本身 )
return this返回当前对象的地址(指向当前对象的指针)
智能指针是利用RAII机制实现的!
智能指针是一个类,这样其对象就可以使用构造函数和析构函数对引用计数进行维护
它有指针的行为,使用起来像普通指针一样(所以要有*操作符重载,解引用
它们都是只把指针所指向的内存释放掉了,并没有把指针本身干掉。在free和delete之后,都需要把指向清理内存的指针置为空,即p=NULL,否则指针指向的内存空间虽然释放了,但是指针p的值还是记录的那块地址,该地址对应的内存是垃圾,p就成了“野指针”。同样会使人认为p是个合法的指针,如果程序较长,我们通常在使用一个指针前会检查p!=NULL,这样就起不到作用了。此时如果再释放p指向的空间,编译器就会报错,因为释放一个已经被释放过的空间是不合法的。而将其置为NULL之后再重复释放就不会产生问题,因为delete一个0指针是安全的。
在这里关于指针和动态申请的内存空间总结如下:
1. 指针消亡了,并不表示它指示的动态内存会自动释放;
2. 动态内存释放掉了,如果这个内存是一个动态对象,则并不表示一定会调用这个对象的析构函数;
动态内存释放掉了,并且调用了析构函数,并不表示指针会消亡或者自动变成了NULL
流的定义如下:通过设备驱动程序与键盘、屏幕、文件、打印机等进行交互, iostream 类提供与之交互的方法。
可能对经过/不经过缓冲区有些困惑:
自己去运行感受一下区别!!(感觉笔记记这些有些跑题了)
std::endl 输出一个换行符,并立即刷新缓冲区(缓冲区内存的一部分)。
\n只是换行的转义字符。
总结:在没有必要刷新输出流的时候应尽量使用 cout << ‘\n’, 过多的 endl 是影响程序执行效率低下的因素之一。