现在的位置: 首页 > 综合 > 正文

谭浩强C++笔记(7-10章)

2013年07月17日 ⁄ 综合 ⁄ 共 7868字 ⁄ 字号 评论关闭

第七章      结构体、共同体和枚举类型

1.结构体在定义时分配内存空间,可以嵌套定义,引用时分层引用,一个结构体中可以有另外一个结构体。同类型结构可以直接赋值,一个结构体中可以有另一个结构体,可以定义结构体数组。

2.包含有static的结构体只能在函数外部定义,即把它当成一个全局变量,若要在函数中引用静态变量值,必须在函数外部对它初始化(赋值操作是全局),只能用结构体名字::去引用。结构体中的static成员:表示所有结构体成员共同使用一个存储空间,早已将它内存准备好。比如student
s1,s2.(student
中的int num是静态的),则s1,s2共同使用一块内存用num.引用时student::num.静态变量缺省值为0.

3.共用体:union。引用union
student
(联合体和结构体中的名字写到后面表示是其中一个变量,并且是全局变量(若在全局定义),放到前面表示是数据类型)中的数据:student.name。共用体中成员是最后一次放入的成员,不能在定义时赋值。不能使用它来传递参数,只能使用它的指针变量传递。结构体中可以有共用体,共用体中可以有结构体,引用其成员时要用共用体名字.引用。

4.枚举类型:如enum weekday 
{sun, mon, tue, wed, thu,  fri, 
sat};
或者enum {sun, mon, tue, wed, thu, 
fri,  sat} weekday
weekend.,定义时与结构体和共用体类似。作整数时,第一个数为0,依次推,当人为的赋值时,从人为赋值的地方加1.若要人为的改变,(人为改变后,后面的数依次改变)则要在定义的时候同时=改变。当把枚举中元素赋给整数时,加或者减操作,要做强制类型转换,引用时直接为weekend=sun等,直接赋值。

 

第八章       指针和引用

1.++--*优先级相同。都是右结合性(当右边有时,先结合右边)*p++=*(p++)

2.一般用指针做为参数传递,在被调函数中,对指针所指向的值做操作。

3.数组指针和指向数组的指针变量:数组名就是数组的起始地址。

4. int a[10];int *p=a;p+ia+i都表示a[i]的地址。注意*(p+i)*p+i是不一样的,前者表示a[i]值,后者表示对*p值加i.*p++也可以对数组下一个元素取值。cout<<*p<<(*p++)<<endl;cout<<i++性质一样。先改变,再输出。

5.当用数组名作参数时,实参形参共用同一块内存,任何一个改变了所指向的内存中数据,都会使数据改变。用数组名作形参,因为接收的是地址,所以可以不指定具体的元素个数,如int
add(int a[],int n)
。也可以是指针类型。

6.指向多维数组的指针和指针变量:int
a[3][4]
。输出各个数:cout<<*(a[i]+j)a[i][j]中的数。cout<<*a[i];输出每行中第一个数。a[0]+11表示元素的第12个数的地址。地址减的时候,差值是元素个数。p+1行指针、*(p+2)列指针*(p+1)+2列指针、*(*p+2)元素。(*(p+1))[2]表示第2行第3列元素,列指针和一维数组没有区别。*(a[1])表示第二行第一个元素,*(a+1)表示第二行元素首地址。[]功能是使行指针变列指针,列指针取数据。

7.a[1][2]例:a+1行指针
*(a+1)
是列指针 *(*(a+1)+2)是元素。a[1]是列指针,*(a[1]+2)是该元素。(*(a+1))[2]也是该元素。用多维指针传参数时,要注意是行指针还是列指针。行指针只能赋值给行指针,列指针只能赋值给列指针。将a这个行指针赋值给p时,可用如下两种方法:int
p[1][2], p=a
int (*p)[2]; p=a;(或者int
(*p)[2]=a;)p
为行指针,可以直接赋值。

8.字符串指针:char
*pstr="i love you";cout<<pstr<<endl;
字符串名字表示首地址。或者用strcpy函数。对字符串操作都是通过首地址进行的。Char
*pstr[3].

9.函数名代表函数入口地址,专门存放函数地址的指针变量叫做指向函数的指针变量。int
(*p)(int,int).(
定义了指向函数地址的指针,它指向的一类函数的指针) int max(int x,int y), p=max。在使用时:p(2,3),就是用p代替maxP只能定义一种类型。

10.指向函数指针:求二分法过程中,由于函数具有通用性,因此用指向函数指针作参数,例子如下:

float devide(float (*fun)(float ))//用指向函数的指针变量作参数(*fun)(float
)
是个整体

{ float 
x1, x2, x0;

do

{   cout<<"Input two number/n";

cin>>x1>>x2;

}while ( fun(x1) * fun(x2) >0);

do

{  x0=(x1+x2)/2;      

if ( fun(x1) * fun(x0) <0 )

x2=x0;

else  x1=x0;

}while (fabs ( fun(x0) )>=1e-6);

return  x0;

}

 

float f1(float x)

{return x*x-3;}

 

float f2(float x)

{return 3*x*x-5*x-3;}

 

void main( )

{

   float y1,y2;

   y1=devide(f1);

   y2=devide(f2);

      cout<<y1<<'/t'<< y2;

}

11.返回指针的函数:如int
*max(int *a,int *b) {return *a>*b?a:b;},
main函数中用指针来接收函数返回值,被调函数指向空间是在主调函数中开辟的。如果函数写成int
*max(int a,int b)
则结果出不来,因为实参和形参占用不同的内存空间,当返回时,返回形参中的地址,在实参中仍用这个地址去取值,但是此时形参变量生命周期已到,里面值已经成随机,而此时取这里面值,结果不正确,因此参数只能用指针(因为此时形参和实参用的同一份内存空间,所以形参值生命周期到后,回到实参中,地址仍存在)。

12.指针数组:一维指针数组:int
*p[5];p
为数组名,有5个元素,都是int型指针,*p[i]=*(*(p+i))。可以通过p这个行指针,将一维数组变成二维数组(p[i]=a[i*3];),在变二维数组时,p中每个元素存放数组每个行列首地址int
(*p)[4]
p为指向有四个int型元素的一维数组的行指针。

13.字符指针数组:数组中的元素为每个字符串首地址,可节省空间。char
*pstr[];

14.指向指针的指针:int
**pp;int i ;int *p=&i ;pp=&p ;
取数据时,**pp= 3 ;看下面一个指向数组指针的指针数组:
char *s[]={“1995”,”1996”,”1997”,”1998”};s
是列指针。
char **sp[ ]={s+3,s+2,s+1,s}; int a[3][4],
a[0]
a[1]a[2]a[3]等是列指针,指向每一行的第一个元素,转换时为对应的行指针转换为相应列的第一个元素指针(反转)。int
a[3][4],*p[3],(*pp)[4],**prt;
其中a,pp是行指针,p,prt是列指针。pp=appa是一样的了*(pp[1]+2)=pp[1][2];实际上pp[0]里面值是a[0]pp[1]里面值是a[1]pp[2]里面值是a[2]pp[3]里面值随机(因为此二维数组只有3行)。),prt=p;*(p[1]+2)=p[1][2]=pp[1][2]=*(prt[1]+2),在赋值时,行指针只能给行指针赋值,列指针只能给列指针赋值。*p[3];p[i]=a[i];p值是a中首地址。prt=p;*(*(prt+1)+2)*(prt[1]+2)。对数组存地址值时,只能存地址每行首地址。字符数组也是同样道理。

15.main函数参数:其它函数由main函数调用,main函数由DOS系统调用,main函数实参值是由DOS系统命令行给出,是随着文件的运行命令一起给出的。可执行文件名实参1实参2
...
实参n.例:test CHINA JAPAN
CANADA.test
是可执行文件名。main(int argc,char *argv[])main(int
argc,char **argv),argc
为命令行中参数个数(包括文件名),argv为指向命令行中参数(字符串)的指针数组。在上面中argc=4.argv指向三个字符串。

16.指针和内存说明:指针可以有空值,即指针不指向任何地址。int
*p=0;
指向同一数组的两指针可以相减,相减结果为元素个数,不可相加。指向同一数组的两指针可以比较大小。在定义变量或数组同时为其内存开辟指定的固定空间,一经定义,即为固定地址的空间,在内存不能被别的变量所占用。

17.动态分配内存:利用new,格式为new数据类型[单位数],如new
int[4](
16字节)并返回这4个空间首地址。int
*p=new int[4];for (int i=0;i<4;i++) *(p+i)=i;
new开辟的内存单元没有名字,指向其首地址的指针是引用其唯一途径,若指针变量重新赋值,则用new开辟的内存单元就引用不到了,别的程序也不能占用这段单元,直到重新开机为止。new开辟的内存空间不能在分配空间时进行初始化。delete
p
用来将动态分配的内存空间还给系统。而且只能用new开辟的空间才能用delete删除,如int
*p=new int;delete p;
或者int *p=new int[4];delete []p.当内存中没有足够空间给分配时,pNULL(0)。在delete使用之前,不能给p重新赋值。

18.引用:int
b=1;int &a=b;a
b共用同一块内存单元,在定义引用的时候就要初始化,int
&a=5
是错误的,必须是对已定义一个变量引用。当对引用赋给指针时,就是用被引用变量地址。引用可以用在新开辟的地址空间,引用主要用来函数传参(主要用在形参上)。引用不能用在数组,指针,和引用本身。

19:指针与引用区别:1、指针是通过地址间接访问某个变量,而引用是通过别名直接访问某个变量。2、引用必须初始化,而一旦被初始化后不得再作为其它变量的别名。当&a的前面有类型符时(如int
&a
),它必然是对引用的声明;如果前面无类型符(如cout<<&a,则是取变量的地址。以下的声明是非法的1、企图建立数组的引用
int & a[9];2
、企图建立指向引用的指针int & *p;3、企图建立引用的引用
int &&px;

20.函数的返回值为引用类型int
&f(int x)
它和f
返回值是一样的f就是返回值别名,下面操作都是正确操作f(t)=20(就算这个赋值操作,f这个函数也执行)(或者f(5)=20;;int
t=f(3)
若函数返回值为引用函数必须有返回值。一般在此函数中对全局变量操作,且返回值也是全局函数,或者函数中用static定义的变量。函数不能返回自动变量和形参,因为返回是在函数运行结束后产生的。返回变量必须是全局变量或静态局部变量,即存储在静态区中的变量。

21.const:有const常量和const指针。在定义的时候就要初始化。指向常量的指针const
int *p
&aa有无值无关紧要,有值就是a值,无值就是随机数),(另起一行p=&a;也可以)p=&b;然后b值可以随意修改,表示该指针指向常量,但常量数值大小可以变。int
* const p=&a;(
指针常量,禁写指针,只能指向一个地址)必须在定义的时候赋初值。*p=4是正确的。const
Int *const p ;
在定义时赋初值。

22.用指针处理链表:链接头head无数据,只有一个指针指向第一个结点地址,链接尾end无指针(指针为NULL),有数据。其余结点有数据,指针指向下一个结点。每个结点是个结构体类型,各个结点类型相同,但其地址不一定连续,具体结点个数根据需要动态开辟,

22 typedef float FL;对已存在的类型赋别名。可以全局定义,也可以局部定义,不过建议全局定义。typedef
struct  student STU;
中间的struct可以加,也可以不加。typedef
char* CHARP;
CHARP ps1
,char STRING[81],typedef char 
STRING[8];STRING s1, s2, s3;
#define PI 3.14(#undef PI
),编译前处理,简单字符代替。Typedef
float FL.
编译时处理,定义一个类型替代原有类型。Typedef struct时也可以将其写到前面,在结构体实现最后写上别名。

 

第九章        类和对象

.类具有封装性,并且类只是定义了一种结构,所以类中的任何成员数据均不能使用关键字extern auto register限定其存储类型。在定义时,只是定义了一种导出的数据类型,并不能为类分配存储空间,所以,在定义类中的数据成员时,不能对其初始化。

.在C++中,结构体类型只是类的一个特例,区别:在类中,成员缺省是private,在结构体中,成员缺省都是public.

.内联成员函数:一种是在类定义时直接给出函数实现,这里编译时,函数是作为内联函数来实现的。一种是先在类内说明,在类外实现,如:inline
void A::add(int x,int y).
在函数体前面加上inline,说明该函数是内联函数。

.定义类时,并不为类分配内存空间,在定义对象时,才分配内存空间,而且只分配用于保存数据成员的内存空间,成员函数代码为该类的每一个对象所共享。在类定义后面写的是全局对象。

..只能访问类公有成员,不能访问私有成员或者保护成员。若要访问私有成员,通过公有函数获取。同类型类对象之间可以整体赋值,这种赋值与对象的成员访问权限无关。

可以定义类对象指针、引用、一维数组或者多维数组、指针数组等。一个类的对象,可以作为另一个类的成员,类之间可以相互嵌套。

类作用域:在函数外定义的,为文件作用域,在函数内定义的,为块作用域。道理同变量一样。在定义一个类时,在其内部可以嵌套另一个类的完整定义,为类的嵌套定义。若类A中嵌套定义了类B对象,则在类A定义中,并不为B对象来分配内存空间,在定义类A的对象时,才分配内存,类B作用域在类A定义结束时结束。

通过公有函数为私有函数赋值。调试信息:member function definition looks like a ctor, but name does not
match enclosing class
原因是函数没有定义类型,加上void/int/float等之类的。当显示是case跳过时,表示在switch中定义了局部变量。

利用指针访问私有数据成员:void Getxy(int 
*px,  int *py) {*px=x;*py=y;} //
提取x,y值。要在函数实现中对它的值做修改。利用函数访问私有成员,getx(){return
x;}
利用引用访问:void Getxy(int &px, 
int &py) //
提取x,y值。调试信息:cannot
convert from 'char *' to 'char [20]'
要用strcpy来赋值,不能直接赋值。

类成员函数的重载:可以有缺省参数的成员函数,void set(float a,float b=3.4),定义类的指针,类指针加1指向下一个类对象。

线性表的应用:线性表是一组存储单元,存储单元里面只能存储int型,有一个指向首地址的指针,一个int数说明里面有多少个单元,一个int数说明里面实际存了多少个数。

this指针:不同对象占据内存中的不同区域,它们所保存的数据各不相同,但对成员数据进行操作的成员函数的程序代码均是一样的。当对一个对象调用成员函数时,编译程序先将对象的地址赋给this指针,然后调用成员函数,每次成员函数存取数据成员时,也隐含使用this指针。用static修饰的成员函数不具有this指针,this指针具有如下形式的缺省说明:类名
*const this
this指针是指针常量,但是它里面值是可以改变的,即this指针可以指向不同对象。void
S::Copy(S  &s)  { 
if(&s==this)
if(s==*this) cout<<“不能复制本身/n};this为正在调用该函数的对象的地址。

类继承是,若子类中继承了父类的私有成员,可以通过诸如:base::fun1将其放到子类公有区域中,可以通过作用域符::访问类的公有成员,私有和保护成员不允许(static初始化除外)。类2基于类1,类3不能写为基于类1和类2,只能写成基于类2.

 

第十章       构造函数和析构函数

1.构造函数:构造函数没有返回值和函数类型。构造函数是类的成员函数,系统约定构造函数名必须与类名相同。构造函数提供了初始化对象的一种简单的方法。对构造函数,说明以下几点:1.构造函数的函数名必须与类名相同。构造函数的主要作用是完成初始化对象的数据成员以及其它的初始化工作。2.在定义构造函数时,不能指定函数返回值的类型,也不能指定为void类型。3.一个类可以定义若干个构造函数。当定义多个构造函数时,必须满足函数重载的原则
4.
构造函数可以指定参数的缺省值。5.若定义的类要说明该类的对象时,构造函数必须是公有的成员函数。如果定义的类仅用于派生其它类时,则可将构造函数定义为保护的成员函数。

由于构造函数属于类的成员函数,它对私有数据成员、保护的数据成员和公有的数据成员均能进行初始化。每一个对象必须要有相应的构造函数。若没有显式定义构造函数,系统默认缺省的构造函数。对于局部对象,每次定义对象时,都要调用构造函数。对于静态对象,是在首次定义对象时,调用构造函数的,且由于对象一直存在,只调用一次构造函数。在定义类时,若没有定义类的构造函数,则编译器自动产生一个缺省的构造函数,缺省的构造函数并不对所产生对象的数据成员赋初值;即新产生对象的数据成员的值是不确定的。

关于缺省的构造函数,说明以下几点:1、在定义类时,只要显式定义了一个类的构造函数,则编译器就不产生缺省的构造函数2、所有的对象在定义时,必须调用构造函数。不存在没有构造函数的对象!

构造函数与new运算符:可以使用new运算符来动态地建立对象。建立时自动调用构造函数,以便完成初始化对象的数据成员。最后返回这个动态对象的起始地址。用new运算符产生的动态对象,在不再使用这种对象时,必须用delete运算符来释放对象所占用的存储空间。new建立类的对象时,可以使用参数初始化动态空间。
pa1=new A(3.0, 5.0);delete  p
动收回由new开辟的空间。释放时,先调用析构函数,再delete空间。如A
*pa,(
不调用构造函数)pa1=new A(3.0,
5.0);//
调用构造函数,delete pa1; 
//
调用析构函数。A  *pa1; pa1=new 
A[3];//
调用构造函数,delete [ ]pa1; 
//
调用析构函数。

调试技巧:在forbreakreturn

【上篇】
【下篇】

抱歉!评论已关闭.