C++知识点总结.docx

上传人:b****6 文档编号:8828161 上传时间:2023-05-15 格式:DOCX 页数:17 大小:151.37KB
下载 相关 举报
C++知识点总结.docx_第1页
第1页 / 共17页
C++知识点总结.docx_第2页
第2页 / 共17页
C++知识点总结.docx_第3页
第3页 / 共17页
C++知识点总结.docx_第4页
第4页 / 共17页
C++知识点总结.docx_第5页
第5页 / 共17页
C++知识点总结.docx_第6页
第6页 / 共17页
C++知识点总结.docx_第7页
第7页 / 共17页
C++知识点总结.docx_第8页
第8页 / 共17页
C++知识点总结.docx_第9页
第9页 / 共17页
C++知识点总结.docx_第10页
第10页 / 共17页
C++知识点总结.docx_第11页
第11页 / 共17页
C++知识点总结.docx_第12页
第12页 / 共17页
C++知识点总结.docx_第13页
第13页 / 共17页
C++知识点总结.docx_第14页
第14页 / 共17页
C++知识点总结.docx_第15页
第15页 / 共17页
C++知识点总结.docx_第16页
第16页 / 共17页
C++知识点总结.docx_第17页
第17页 / 共17页
亲,该文档总共17页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

C++知识点总结.docx

《C++知识点总结.docx》由会员分享,可在线阅读,更多相关《C++知识点总结.docx(17页珍藏版)》请在冰点文库上搜索。

C++知识点总结.docx

C++知识点总结

 

C++知识点总结

类和对象初步

1.类的定义在定义外成员函数的实现

2.类的成员函数之间可以相互调用,类的成员函数也可以重载,也可设默认参数值

3.一般来讲,一个对象占用的内存空间的大小等于其成员变量的体积之和。

每个对象都有自己的存储空间(成员变量),但成员函数只有一份对象名.成员名指针->成员名引用名.成员名

4.private:

一个类的私有成员,只能在该类的成员函数中才能访问public:

proteced:

5.class默认privatestruct默认public

6.内联成员函数:

成员函数名前加inline或函数体写在类定义内部的成员函数。

执行更快,但会带来额外的内存开销

 

构造函数

1.构造函数全局变量在堆上,系统自动初始化为零。

局部变量在栈上,初始值是随机的,需要初始化。

2.构造函数:

对对象进行初始化。

构造函数执行时对象的内存空间已经分配,构造函数的作用是初始化这片空间。

可重载,不写的话有默认构造函数,但是如果编写了构造函数,那默认构造函数不会再执行。

是一类特殊的成员函数。

不写返回值类型,函数名为类名。

3.对象在生成时一定会调用某个构造函数,一旦生成,不再执行构造函数。

4.P183Ctest*pArray[3]={newCtest(4),newCtest(1,2)}

 

5.复制构造函数:

其是构造函数的一种,只有一个参数,为本类的引用,防止混淆,构造函数不能以本类的对象作为唯一的参数。

默认复制构造函数。

6.复制构造函数被调用的三种情形:

1用一个对象去初始化另一个对象时ComplexC1(C2)ComplexC2=C1;2函数的参数是类A的对象。

形参未必等于实参函数中用对象的引用不会调用复制构造函数voidFunction(constComplex&c)3函数的返回值是类A的对象

7.类型转换构造函数:

除复制构造函数外,只有一个参数的构造函数C=6

 

8.析构函数:

在对象消亡时调用,可以定义其做善后工作。

是一类特殊的成员函数,一个类有且只有一个构造函数。

默认析构函数

9.注意:

函数的参数对象以及作为函数返回值的对象,在消亡时也会调用析构函数

10.构造函数析构函数变量的生存期:

全局变量在main函数开始执行前初始化。

函数调用结束后静态局部对象不消亡。

main函数结束时,静态局部对象先消亡,全局对象再消亡

 

11.静态成员变量和静态成员函数:

实质是全局变量和全局函数,被所有的同类共享。

生成在对象生成之前。

计算体积时不会将静态成员变量算入其中老师的coeblocksC013静态成员变量必须在类定义外进行声明,声明的同时也可初始化。

因为静态成员函数内部不作用于某个对象,所以不能访问非静态成员

12.常量对象和常量成员函数:

常量对象一旦初始化后,值再也不能改变。

常量对象和普通对象都可调用常量成员函数。

通过常量对象调用常量成员函数。

常量成员函数内不能调用同类的非常量成员函数,静态成员函数除外。

 

13.封闭类:

包含成员对象的类。

在定义封闭类的构造函数时,用初始化列表的方式初始化。

封闭类对象生成时,先执行所有成员对象

 

友元和this

1.友元函数:

把全局函数和其它类的成员函数声明为友元函数后,可直接在友元函数内部访问该类的私有成员;但不能把其它类的私有成员函数声明为友元。

2.全局函数声明为友元:

friend返回值类型函数名(参数表);

3.其它类的成员函数声明为友元:

friend返回值类型其它类的类名:

成员函数名(参数表);

4.友元类:

类A将类B声明为自己的友元,则类B中所有的成员函数都可访问类A对象中的私有成员。

私有成员函数也可访问。

5.注意:

友元关系不能传递。

6.this指针:

非静态成员函数的实际形参比编写的多一个,就是this指针,指向成员函数作用的对象。

通过this指针找到对象所在的地址,继而找到对象非静态成员变量的地址。

7.注意:

因为静态成员函数不作用于某个对象,所以在其内部无this指针,不能使用。

 

运算符重载

1.运算符重载:

是对已有的运算符赋予多重含义,使不同的运算符作用于不同的类型的数据时导致的不同的行为。

实质是编写运算符作为名称的函数。

2.返回值类型operator运算符(形参表){......}

3.使用了被重载的运算的表达式,会被编译成对运算符函数的调用,实参是运算符的·操作数,运算的结果是函数的返回值。

运算符可多次被重载,可重载为全局函数和成员函数。

重载为全局函数时,参数个数等于操作符的个数;重载为成员函数时,参数的个数等于操作数的个数减1.a—b被重载为(b);

4.类名(构造函数参数表)这个写法表示生成一个临时对象。

该临时对象没有名字,生存期到包含它的语句执行完为止。

5.C++规定=只能重载为成员函数.赋值运算符可以连用。

6.constchar*c_str()const{returnstr);两种错误的情形:

char*p=();strcpy(),"tianggong1");

7.a=b="hello";等价于==("hello"));

8.(a=b)=c;等价于=(b)).operator=(c);

9.深复制和浅复制:

两个对象的指针成员变量指向同一个地方,指向两个不同的地方。

10.将运算符重载为友元函数

11.重载插入运算符和流提取运算符:

cout是ostream类的对象。

ostream类和cout都是在iostream头文件中声明的。

ostream类将"<<"多次重载为成员函数。

比如:

ostream&ostream:

:

operator<<(constchar*s){...return*this;)ostream&ostream:

:

operator<<(intn){...return*this;)

12.返回值*this是cout的引用cout<<"starwar"<<5;等价于<<("starwar")).operator<<(5);

13.重载强制类型转换运算符:

(类型名)对象等价于对象.operator类型名()

14.重载自增,自减运算符obj++CDemooperator++(int)

15.++objCDemooperator++(int)

 

继承与派生

1.继承与派生的关系:

A类派生出B类,B类继承了A。

派生类是通过对基类进行扩充和修改后得到的。

基类所有成员自动成为派生类成员。

在派生类的成员函数中,不能访问基类的私有成员。

派生类对象的体积等于基类对象的体积加上自己成员变量的体积。

在派生类对象中,包含着基类对象,且基类对象的存储位置位于新增的成员变量之前。

2.基类的构造函数和析构函数不能被派生类继承,需要自己重新定义。

如果基类构造函数没有定义,派生类也可不定义,用缺省。

如果基类的构造函数定义,派生类构造函数一定要定义。

3.注意:

在基类和派生类中有同名成员,在派生类成员函数或派生类对象中访问同名成员,除非特别之指出,访问的是派生类的成员。

以下两种情况访问的是基类的成员:

:

:

PrintInfo();p->CStudent:

:

PrintInfo();

4.复合关系和继承关系:

复合关系,有,表现为封闭类,但不一定只是封闭类。

即一个类以另一个类的对象作为成员变量。

继承关系,是,类B继承类A,满足B所代表的事物也是A所代表的事物

5.类A知道类B:

类A的成员变量是类B的指针

内联函数体内不能包含任何静态变量,不能使用循环语句、switch;不能递归。

2.内联函数的定义必须出现在第一次被调用之前。

3.如果函数返回类型为void,则不能有return语句。

二:

指针通过指针引用数组元素inta[10];int*p;p++是合法的,而a++是错误的。

a是数组名,它是数组的首地址,是常量;

指向函数的指针变量:

存放函数入口地址,指向的是程序代码存储区。

1、函数调用可以通过函数名调用,也可以通过指向函数的指针变量调用。

2、(*p)()表示定义一个指向函数的指针变量,在程序中把哪个函数的地址赋给它,它就指向哪一个函数。

3、给函数指针变量赋值时,只需给出函数名而不必给出参数。

p=max4、用函数指针变量调用函数时,只需将(*p)代替函数名,在(*p)之后的括弧中根据需要写实参。

c=(*p)(a,b)5、对指向函数的指针变量不能运算

constpointer

一个指针涉及到两个变量,一个是指针本身,另一个是指向的变量

1.指向常量的指针const放在指针类型前,在程序中不能通过指针来间接修改指针所指向的内存空间的值,但可以改变指向的空间inta=10;constintb=20;constint*pa=&a;constint*pb=&b;a=100;针常量const放在”*”和指针名之间,不能改地址能改所指向的值。

intb=28;int*constpb=&b;

*pb=100;向常量的指针常量const在上述两个地方都加。

既不允许修改指针值,也不允许修改指针变量所指向的值constinta=10;constint*constpa=&a;

a=100;的静态数据成员为该类的所有对象所共享。

2.必须在类外文件作用域中的某个地方对静态数据成员赋初值(因为构造函数多次被调用,而静态数据成员只初始化1次):

<类型><类名>:

:

<静态数据成员>=<初值>

Const成员函数中确保不会修改任何本类对象的数据成员。

classconstfun{private:

inta;public:

voidnonconstFunc(){a=18;为最远的派生类提供唯一的基类成员,而不重复产生多次拷贝

类模板C++中实现多态的另一种方法是类模板类模板可以使用户为类定义一种模式,使得类中的一些数据成员和成员函数的参数,以及成员函数的返回值能够娶任意类型。

#include<>

templateclassTestClass{

public:

Tbuffer[10];

向常量的指针const放在指针类型前,在程序中不能通过指针来间接修改指针所指向的内存空间的值,但可以改变指向的空间inta=10;constintb=20;constint*pa=&a;constint*pb=&b;a=100;针常量const放在”*”和指针名之间,不能改地址能改所指向的值。

intb=28;int*constpb=&b;

*pb=100;向常量的指针常量const在上述两个地方都加。

既不允许修改指针值,也不允许修改指针变量所指向的值constinta=10;constint*constpa=&a;

 

内存模型描述的是程序中各变量(实例域、静态域和数组元素)之间的关系,以及在实际计算机系统中将变量存储到内存和从内存取出变量这样的低层细节.不同平台间的处理器架构将直接影响内存模型的结构.

首先介绍一下C++中有继承关系的类对象内存的布局:

在C++中,如果类中有虚函数,那么它就会有一个虚函数表的指针__vfptr,在类对象最开始的内存数据中。

之后是类中的成员变量的内存数据。

对于子类,最开始的内存数据记录着父类对象的拷贝(包括父类虚函数表指针和成员变量)。

之后是子类自己的成员变量数据。

对于子类的子类,也是同样的原理。

但是无论继承了多少个子类,对象中始终只有一个虚函数表指针。

为了探讨C++类对象的内存布局,先来写几个类和函数

首先写一个基类:

1.classBase

2.{

3.public:

4.virtualvoidf(){cout<<"Base:

:

f"<

5.virtualvoidg(){cout<<"Base:

:

g"<

6.virtualvoidh(){cout<<"Base:

:

h"<

7.intbase;

8.protected:

9.private:

10.};

然后,我们多种不同的继承情况来研究子类的内存对象结构。

1.无虚函数集继承

有一个虚函数继承

全部虚函数都继承

多重继承

多重继承,即类有多个父类,这种情况下的子类的内存结构和单一继承有所不同。

我们可以看到,当子类继承了多个父类,那么子类的内存结构是这样的:

子类的内存中,顺序

5.菱形继承

 

6.单一虚拟继承

虚拟继承的子类的内存结构,和普通继承完全不同。

虚拟继承的子类,有单独的虚函数表,另外也单独保存一份父类的虚函数表,两部分之间用一个四个字节的0x00000000来作为分界。

子类的内存中,首先是自己的虚函数表,然后是子类的数据成员,然后是0x0,之后就是父类的虚函数表,之后是父类的数据成员。

如果子类没有自己的虚函数,那么子类就不会有虚函数表,但是子类数据和父类数据之间,还是需要0x0来间隔。

因此,在虚拟继承中,子类和父类的数据,是完全间隔的,先存放子类自己的虚函数表和数据,中间以0x分界,最后保存父类的虚函数和数据。

如果子类重载了父类的虚函数,那么则将子类内存中父类虚函数表的相应函数替换。

7.菱形虚拟继承

结论:

(1)对于基类,如果有虚函数,那么先存放虚函数表指针,然后存放自己的数据成员;如果没有虚函数,那么直接存放数据成员。

(2)对于单一继承的类对象,先存放父类的数据拷贝(包括虚函数表指针),然后是本类的数据。

(3)虚函数表中,先存放父类的虚函数,再存放子类的虚函数

(4)如果重载了父类的某些虚函数,那么新的虚函数将虚函数表中父类的这些虚函数覆盖。

(5)对于多重继承,先存放第一个父类的数据拷贝,在存放第二个父类的数据拷贝,一次类推,最后存放自己的数据成员。

其中每一个父类拷贝都包含一个虚函数表指针。

如果子类重载了某个父类的某个虚函数,那么该将该父类虚函数表的函数覆盖。

另外,子类自己的虚函数,存储于第一个父类的虚函数表后边部分。

(6)当对象的虚函数被调用是,编译器去查询对象的虚函数表,找到该函数,然后调用。

到这c++类对象的内存模型就介绍完了,希望对大家有帮助。

C++作为一款C语言的升级版本,具有非常强大的功能。

它不但能够支持各种程序设计风格,而且还具有C语言的所有功能。

我们在这里为大家介绍的是其中一个比较重要的内容,C++内存区域的基本介绍。

一.在c中分为这几个存储区

1.栈

由编译器自动分配释放

2.堆

一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收

3.全局区(静态区)

全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。

-程序结束释放

4.另外还有一个专门放常量的地方。

程序结束释放

在函数体中定义的变量通常是在栈上,用malloc,calloc,realloc等分配内存的函数分配得到的就是在堆上。

在所有函数体外定义的是全局量,加了static修饰符后不管在哪里都存放在全局区(静态区),在所有函数体外定义的static变量表示在该文件中有效,不能extern到别的文件用,在函数体内定义的static表示只在该函数体内有效。

另外,函数中的"adgfdf"这样的字符串存放在常量区。

比如:

inta=0;C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区

1.栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。

里面的变量通常是局部变量、函数参数等。

2.堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。

如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。

3.自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。

4.全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。

5.常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改)

三.谈谈堆与栈的关系与区别

具体地说,现代计算机(串行执行机制),都直接在代码底层支持栈的数据结构。

这体现在,有专门的寄存器指向栈所在的地址,有专门的机器指令完成数据入栈出栈的操作。

这种机制的特点是效率高,支持的数据有限,一般是整数,指针,浮点数等系统直接支持的数据类型,并不直接支持其他的数据结构。

因为栈的这种特点,对栈的使用在程序中是非常频繁的。

对子程序的调用就是直接利用栈完成的。

机器的call指令里隐含了把返回地址推入栈,然后跳转至子程序地址的操作,而子程序中的ret指令则隐含从堆栈中弹出返回地址并跳转之的操作。

C/C++中的自动变量是直接利用栈的例子,这也就是为什么当函数返回时,该函数的自动变量自动失效的原因。

和栈不同,堆的数据结构并不是由系统(无论是机器系统还是操作系统)支持的,而是由函数库提供的。

基本的malloc/realloc/free函数维护了一套内部的堆数据结构。

当程序使用这些函数去获得新的内存空间时,这套函数首先试图从内部堆中寻找可用的内存空间,如果没有可以使用的内存空间,则试图利用系统调用来动态增加程序数据段的内存大小,新分配得到的空间首先被组织进内部堆中去,然后再以适当的形式返回给调用者。

当程序释放分配的内存空间时,这片内存空间被返回内部堆结构中,可能会被适当的处理(比如和其他空闲空间合并成更大的空闲空间),以更适合下一次内存分配申请。

这套复杂的分配机制实际上相当于一个内存分配的缓冲池(Cache),使用这套机制有如下若干原因:

1.系统调用可能不支持任意大小的内存分配。

有些系统的系统调用只支持固定大小及其倍数的内存请求(按页分配);这样的话对于大量的小内存分类来说会造成浪费。

2.系统调用申请内存可能是代价昂贵的。

系统调用可能涉及用户态和核心态的转换。

3.没有管理的内存分配在大量复杂内存的分配释放操作下很容易造成内存碎片。

堆和栈的对比

从以上知识可知,栈是系统提供的功能,特点是快速高效,缺点是有限制,数据不灵活;而栈是函数库提供的功能,特点是灵活方便,数据适应面广泛,但是效率有一定降低。

栈是系统数据结构,对于进程/线程是唯一的;堆是函数库内部数据结构,不一定唯一。

不同堆分配的内存无法互相操作。

栈空间分静态分配和动态分配两种。

静态分配是编译器完成的,比如自动变量(auto)的分配。

动态分配由alloca函数完成。

栈的动态分配无需释放(是自动的),也就没有释放函数。

为可移植的程序起见,栈的动态分配操作是不被鼓励的!

堆空间的分配总是动态的,虽然程序结束时所有的数据空间都会被释放回系统,但是精确的申请内存/释放内存匹配是良好程序的基本要素。

1.碎片问题:

对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。

对于栈来

讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在他弹出之前,在他上面的后进的栈内容已经被弹出,详细的可以>参考数据结构,这里我们就不再一一讨论了。

2.生长方向:

对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。

3.分配方式:

堆都是动态分配的,没有静态分配的堆。

栈有2种分配方式:

静态分配和动态分配。

静态分配是编译器完成的,比如局部变量的分配。

动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。

4.分配效率:

栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:

分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。

堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。

显然,堆的效率比栈要低得多。

明确区分堆与栈:

在bbs上,堆与栈的区分问题,似乎是一个永恒的话题,由此可见,初学者对此往往是混淆不清的,所以我决定拿他第一个开刀。

首先,我们举一个例子:

1.voidf()

2.{

3.int*p=newint[5];

4.}

这条短短的一句话就包含了堆与栈,看到new,我们首先就应该想到,我们分配了一块堆内存,那么指针p呢他分配的是一块栈内存,所以这句话的意思就是:

在栈内存中存放了一个指向一块堆内存的指针p。

在程序会先确定在堆中分配内存的大小,然后调用operatornew分配内存,然后返回这块内存的首地址,放入栈中,他在VC6下的汇编代码如下:

1.00401028push14h

2.0040102Acalloperatornew(00401060)

3.0040102Faddesp,4

4.00401032movdwordptr[ebp-8],eax

5.00401035moveax,dwordptr[ebp-8]

6.00401038movdwordptr[ebp-4],eax

这里,我们为了简单并没有释放内存,那么该怎么去释放呢是deletep么澳,错了,应该是delete[]p,这是为了告诉编译器:

我删除的是一个数组,VC6就会根据相应的Cookie信息去进行释放内存的工作。

好了,我们回到我们的主题:

堆和栈究竟有什么区别

主要的区别由以下几点:

管理方式不同;

空间大小不同;

能否产生碎片不同;

生长方向不同;

分配方式不同;

分配效率不同;

管理方式:

对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memoryleak。

空间大小:

一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。

但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M(好像是,记不清楚了)。

当然,我们可以修改:

打开工程,依次操作菜单如下:

Project->Setting->Link,在Category中选中Output,然后在Reserve中设定堆栈的最大值和commit。

注意:

reserve最小值为4Byte;commit是保留在虚拟内存的页文件里面,它设置的较大会使栈开辟较大的值,可能增加内存的开销和启动时间。

堆和栈相比,由于大量new/delete的使用,容易造成大量的内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用户态和核心态的切换,内存的申请,代价变得更加昂贵。

所以栈在程序中是应用最广泛的,就算是函数的调用也利用栈去完成,函数调用过程中的参数,返回地址,EBP和局部变量都采用栈的方式存放。

所以,我们推荐大家尽量用栈,

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 初中教育 > 语文

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2