正文

C/C++ 学习笔记2006-11-10 21:14:00

【评论】 【打印】 【字体: 】 本文链接:http://blog.pfan.cn/embed/20173.html

分享到:

《数据与算术运算》笔记

转于http://colormoon.programfan.com
 

1.整数值

整数前面可以带(或不带)正、负号,但不允许带小数点、逗号或其他特殊符号(如美元符号)。

不同的数据类型在不同设备的存储空间是不同的。

要存储超过设备内定存储空间的整数,必须使用整数限定符。

2.浮点数

同整数值一样,浮点数也不允许带小数点、逗号或其他特殊符号。浮点数分为“float”、“double”、“long double”。它们的区别在于存储空间的大小。

浮点数后面加上“F”或“f”表示这是一个单精度浮点数,加上“L”或“l”表示这是一个双精度长浮点数,如果不加后缀则表示是双精度浮点数。

浮点数也可以用指数表示法表示。如1625 可以表示为 1.625e3,即 1.625x103

3.字符值

A的ASC码值为65,a的ASC码值为97。

4.算术运算

%是取模运算符,该运算结果是取余数。

向输出流中插入数据可以在多个行中完成,以分号为结束。如:

cout<<"The resoult is "

      <<(22.5+5)<<endl;

但是包含在双引号里的字符串不能被分解为几行,且只有在最后一行中出现分号。

算术表达式的结果永远也不会是一个单精度浮点数,因为计算时,计算机临时将所有单精度数转换为双精度数。

5.整数的除法

两个整数相除,其结果是一个整数,小数部分被省去。

6.运算符的优越级

    运算符                             结合性

-------------------------------------------------

--(一元的)                     从右到左

*、/ 、%                              从左到右

+、--                                  从左到右

 计算8+5*7%2*4,其结果为12。

7.自加(减)运算符前置和后置的区别:前置运算符表示在赋值前先进行自加或自减运算,而后置运算符则表示先赋值后进行自加或自减运算。

前置的语义是:使值加1然后再取它,后置的语义是:先取值然后再使原来的值加1

 

《变量与声明语句》笔记

 

1.变量名称遵循的原则

i.变量名称必须以字母或下划线开头,且只能含有字母、下划线和数字。不能含有任何其他空格、逗号或特殊符号。

ii.变量名称不能是关键字。

iii.变量名称不能由超过255个字符组成。

这些名称的选择同样适用于函数名称。

2.变量必须先声明后使用。具有相同类型的数据可以放在一条语句中声明。

3.声明语句说明:

int a;

int 告诉计算机要为一个整数保留足够的空间,a 则是告诉计算机要将保留的存储空间的第一个字节标记为名称“a”。

4.显示一个变量的地址,用地址运算符“&”。当该符号放在变量名前时,它代表的就是一个变量的地址。&price表示变量price的地址。

5.多数计算机中,short是2个字节,long是4个字节,而int可以是2个或4个字节(一般为4个字节),char是1个字节,float是4个字节,double是8个字节,bool是1个字节。

6.C++允许将定义与初始化混合使用。如:int a=5,b,c=6;

7.typedef(类型定义符)用来创建一个新名字,以取代冗长的类型定义符,如

 typedef unsigned shor int USHORT;

8.当无符号整型变量的值达到其最大值时,该数值将回绕,如当一个int类型变量达到值65536时,其再加一后的值将变为0;有符号整型变量在达到最大值时也会回绕,但它是由最大的正值变为最小的负值(32767变为-32768)。

 

《常量》笔记

1.C++有两种常用常量:字面常量和符号常量。字面常量指直接写入程序语句的数值,如:

int myAge=39;

2.符号常量是指用名字表示的常量,如同一个变量一样,不同的是在初始化之后其值不可以更改。C++有两种定义符号常量的方法:

i.用 #define 来定义。该方法定义的变量无类型,在程序中只进行简单的文本替换,即把程序中的名字用相应的数字来替代。

ii.用 const 来定义常量。该方法可以定义变量的类型,这就使得编译器可以根据类型来正确的使用常量。

3.枚举型常量。可以使用它来创建一些新的类型,再定义这些新类型的变量。如:

enum COLOR{WHITE,RED,BLUE,GREEN,BLACK};

COLOR myColor=BLUE;

每个枚举型常量都有一个整数值,若不特别指定,第一个常量值为0,其余依次增加。不过如果其中任何一个常量用一个特定的数值初始化,那么未被初始化的常量将在其前面已被初始化的常量的基础上增加。

《函数》笔记

1.函数原型(即函数声明)可以不包含参数名,而只包含函数类型。即原型中的参数名无实际意义。如:

long Area(int,int);

此外,若函数未声明返回值类型,则系统默认为返回值为整型。

2.若局部变量与全局变量同名,则在局部变量作用范围内,全局变量将被屏蔽。C++中尽量不要使用全局变量,而使用静态成员变量来替代。

3.任何一个合法的C++表达式都可以作为一个函数的变元(传递给函数以作为参数),包括常量、数学和逻辑表达式以及其他可返回值的函数,即也可以使用一个函数的返回值作为另一个函数的参数。

4.函数的值传递:即传递给函数的变元只是一个局部变量,其在函数中的变动都不影响调用函数中的值。亦即函数会为传递来的变元生成一个局部拷贝。

5.从一个函数中返回值须使用关键字 return,return后的表达式的值作为返回值传递给调用函数。return主语句执行后,其后所有的语句都不再执行。一个函数中允许有多个return语句,但只能执行一个。

6.函数默认值的声明,如:

int Function(int x=50);

因为参数名无实际意义,也可用  int Function(int =50);

对默认值的声明并不改变函数定义。但此种声明有一条限制,如果某个参数没有默认值,那该参数之前的所有参数均不可有默认值。

7.函数重载,各函数的参数要有区别,可以是参数类型、个数,或二者兼有。重载函数的返回值类型既可以相同,也可以不同。

8.内嵌函数,声明如:

inline int Area(int);

每一次调用时都会将函数代码复制到调用函数中,适用于函数体较小时。可以节省内存开销,但多次调用会增加函数体。

9.函数递归分为直接递归和间接递归。直接递归是指函数直接调用其本身。间接递归是指指函数调用了另一个函数,而被调用函数则又调用了第一个函数。

 

《类的定义》相关笔记

1.一个类可以包含各种类型的变量,也可由其他的类组成。类中的变量称为成员变量或数据成员。类中的函数称为成员函数或类的方法。

2.声明一个类并没有为该类分配内存,只有定义类的对象时,才为对象分配内存。

3.为对象赋值,而不是为类赋值。例如:

Cat是一个类,itAge是其成员变量,则以下程序不正确

Cat.itAge=5;

4.一个类的所有成员(数据和方法)默认时均为私有的。私有成员只有在类本身的方法内访问。公有成员则可以被类的所有对象访问。

5.构造函数可以带有参数,但不能有返回值,返回空值也不行。构造函数是一个与类同名的方法。构造函数创建并初始化类的对象。析构函数在对象撤除后清除并释放分配的内存。析构函数没有参数,也没有返回值。

如果没有为类创建构造函数和析构函数,编译器会自动创建一个,即为默认构造函数。没有参数的构造函数被称为默认构造函数。编译器创建的默认构造与析构函数不带任何参数,且不执行任何操作。

只要自己创建了构造函数,系统将不再提供构造函数。因此除非自己再创建默认的构造函数,否则程序将不会有默认的构造函数。

6.如果声明类方法为const,则该方法不能更改类任何一个成员变量的值。类方法声明为常量,如下所示:

void SomeFunction() const;

良好的编程习惯是将尽可能多的方法声明为const。

7.可能通过加关键字 inline 来实现成员函数的内嵌。也可以用其他类作为自定义类的成员变量。

8.C++中结构体与类相同,只是其成员默认是公有的。但结构体没有方法。

 

 

《程序流程》笔记

1.while循环语句中的测试条件句可以是任何有效的C++语句或语句块,它也可以包括使用逻辑运算符的表达式。

2.若需要在执行while循环中的所有语句之前返回到while循环的开始处,则可以用continue语句。

若需要在满足退出循环条件之前跳出循环,则可以用break语句。

3.do...while循环在执行测试条件之前先执行循环体,保证循环体至少被执行一次。

4.for循环语句的执行过程分析

for(initilization,test,action)

statement;

initilization语句用来初始化counter的值,或是为循环做好准备。test语句可以是任何C++表达式,在每次进入循环体时都要对它进行测试。如果test测试为真,则执行循环体,然后执行action语句。

4.空for循环语句。若循环体内不放任何语句,也切记要放一条空语句(;)。

5.switch表达式可以是任何C++合法的表达式,但是该表达式的值的类型只能是int、char、enum这三种类型。

《指针》学习笔记

1.指针是保存内存地址的变量。

int *pAge=0;

值为零的指针被称为空指针。所有指针在定义时都应该被初始化。没有初始化的指针称为失控指针。

int age=50;

*pAge=&age;

以上是为指针赋值的完整过程。

2.对于变量,类型会告诉编译器需要多少存储器去装载。但对于指针,类型并不这样做,所有指针均是4个字节。

3.使用间接引用运算符(*)来引用指针。当一个指针被间接引用时,就读取其所保存的地址处的值。

4.指针有以下三种用途:

i.处理自由存储区的数据;

ii.访问类的成员数据和函数;

iii.通过引用的方式向函数传递变量。

5.局部变量和函数形参位于栈中,寄存器则用于内存管理(如保存栈顶指针和指令指针),代码区位于代码区,全局变量区,其余的内存空间作为自由存储区,称为堆。

栈在函数返回时会自动清除。自由存储区在程序结束之前不会自动清除,所以在占用之后必须主动释放。

6.C++中使用关键字new来分配自由存储区中的内存。new的返回值是内存的地址,它必须被赋给一个指针。如:

unsigned short *pPointer=new unsigned short;

当不在使用一块内存时,使用关键字delete,它的作用是释放内存。

7.内存泄漏。

指针本身是一个局部变量,当声明指针的函数返回时,指针的作用域也就结束了,因此被丢弃了。然而由new申请的内存不会自动释放,于是这块内存就不能被其他数据使用,这种情况就称为“内存泄漏”。因此要记得使用delete来释放内存。释放后最好将指针赋值为0,变为空指针。

另一种造成内存泄漏的情况是:在没有删除一个指针之前就对其重新赋值。如:

unsigned short int *pPointer=new unsigned short int;

*pPointer=72;

pPointer=new unsigned short int;

pPonter=84;

这时保存72的内存空间变得不可用,也没办法再去释放它。应该是再次使用时,先释放。

8.也可以在自由存储区内为类的对象分配内存。如:

Cat *pCat=new Cat;

这条语句调用了默认构造函数(无参数的构造函数)。删除这类指针时,在内存释放之前会调用对象的析构函数。

也就是说,内存分配是在类对象的构造函数中进行的,而内存删除则是在类对象的析构函数中进行的。

9.每个类的成员都有一个隐藏的指针:this指针。该指针指向每一个单独的对象,它保存了对象的地址。

10.迷途指针(或称失控指针、悬浮指针):指对一个指针调用delete时(这样会释放它的内存)却并没有把它设置为空时产生的。若此时并没有重新赋值就试图再次调用它时,就会引起不可预料的结果。

好的解决办法是在删除一个指针后,把它设置为空指针(0)。

11.在指针的类型之前和之后可以使用关键字const,也可以在这两个位置上同时使用。

const int *pOne;

int * const pTwo;

const int * const pThree;

pOne是一个指向整型常量的指针,该指针指向的值是不能被改变的。

pTwo也是一个指向整型的常指针,它指向的整数可以改变,但这个指针不能指向其他变量。

pThree是一个指向整型常量的指针,它指向的值不能被改变,也不能指向其他变量。

保持正确的窍门是观察关键字的右边是什么,如果是类型,那么值就是常量,如果是变量名,那么指针变量本身就是常量。

12.一个指针可以与另一个指针相减。

 

9.类的对象在内存中的大小由类的成员变量的大小的总和来决定。类方法不占用为该对象所分配的那部分内存。

10.一个类定义了两个对象,那么两个对象可以相互访问对方的数据成员,也包括私有成员。

 

《引用》学习笔记

1.引用就是一个别名,当声明一个引用时,就必须把它初始化为另一个对象名,也就是目标。声明格式如下:

int &rSomRef = someInt;

对引用的操作等同于对原对象的操作。

2.对引用进行取址运算,返回的值是目标的地址,因为引用只是目标的别名。

3.引用不能够被重新赋值。若对引用重新赋值就相当于对目标重新赋值。

引用只能是对对象的引用,而不能是对类或类型的引用。

引用不能为空,即不能像空指针一样被赋为零。

4.使用引用传递参数,不是在函数的作用域中创建一个拷贝,而是直接把原对象传递给函数。因此在函数中对引用的改变也会反映到函数外。

5.指针声明的函数:    void swap(int *x , int *y);

   引用声明的函数:   void swap(int &x , int &y);

6.若要确保被传递的引用参数不被改变,则使用const指针或引用来传递。如:

const Cat& Function(const Cat& theCat);

7.在同一个参数列表中同时使用引用、指针及采用值传递是合法的。

要避免将局部的对象以引用的方式返回,因为局部对象在函数作用域之外就会被删除。

对于引用,不能使用delete运算符。

8.如果被返回的对象是局部的,那么就必须采用按值传递,否则就加返回一个不存在的对象的引用。

 

《高级函数》学习笔记

1.构造函数中初始化对象的成员变量。掌握这种方法。

CAT():

ItsAge(5),

ItsWeight(8)

{}

2.复制构造函数在每次复制一个对象时调用。所有的复制构造函数均有一个参数,即对同一个类的对象的引用。使这个引用成为常量最好,这样构造函数就不必改变传递进来的对象。如:

CAT(const CAT& theCat);

3.一般的复制构造函数,执行的是成员复制,简单的将成员变量复制到新的对象中,这样容易造成内存泄漏(迷途指针)。因此,复制构造函数采取开辟新的内存区来避免这种问题的发生,这也叫深层复制。如:

CAT(const CAT& rhs)

{itsAge=new int;itsWeight=new int;

  *itsAge=rhs.GetAge();*itsWeight=rhs.GetWeight();}

4.运算符重载。让对象之间(其实是对象的成员变量)可以直接进行加减乘除等运算。

重载前置运算符的格式:returnType operator op;这里op是要重载的运算符(+、—等)。如

void operator ++(){++itsVal;}

在生成的对象i进行自加执行简单的命令:++i;

如果要将自加后的对象赋给其他的对象,则只需修改函数返回值的类型为类对象。

几种不同的前置运算符的声明方法,注意返回值的类型:

classType operator ++(){++itsVal;return classType(itsVal);}

const classType& operator ++(){++itsVal;returh *this;}

在效率方面,最后一个声明语句较好。

5.后置运算符的声明。同前置运算符的区别在于加入一个整型参数。如:

void operator ++(int flag){itsVal++;}

其中参数flag并不参加任何运算,只是标明是后置运算符的声明。

6.重载加号运算符。声明如下:

classType operator+ (const classType& rhs){return classType(itsVal+rhs.GetVal());}

这样可以对实例化的两个对象进行加法运算。

7.赋值运算符。声明如下:

classType operator= (classType& rhs);

为避免对象对自身的赋值,必须检查赋值运算符的右边是否是对象的本身。实现如下:

{if(*this==rhs) return;

..........................

return *this;}

8.处理数据类型的转换。比如将一个数据类型变量如int或unsigned int赋给用户自定义的对象。如果没有其他构造函数,编译器会报错。

为完成这种类型转换,需要添加一个用此种数据类型的变量做参数的构造函数。如

CAT(it val);

这个构造函数从整型变量创建一个CAT对象。

要注意,所有的运算符重载均是以同样的方式进行的——用operator声明重载运算符。对于双目运算符(如=或+),右边的变量变成了参数。这是由构造函数完成的。如:

a=b;

变成

a.operator=(b);

《继承》学习笔记

1.向已有的类添加了新功能后的类叫做原来类的派生,原来的类叫做新类的基类。通常基类有多个派生类。

派生类的声明语法:class Dog:public Mammal

基类必须最先声明。

2.成员变量有三种限定符:private(私有型)、public(公有型)、protected(保护型)。

保护型的变量不仅可以被自己的类所访问,也可以被该类所派生出的类的成员函数所访问。而私有成员变量只可以被自己类的成员函数访问。即使是派生类也不能访问他的基类的私有成员和函数。

3.创建派生类的对象时,首先调用基类的构造函数创建对象的基类部分,然后调用派生类的构造函数,创建派生类的部分构成整个派生对象。

删除派生类的对象时,首先调用派生类的析构函数,删除对象的派生部分,然后调用基类的析构函数,删除对象的基类部分。

4.派生类的构造函数调用基类的构造函数来初始化某些成员变量,若有的成员变量希望在派生类中初始化,而基类不做初始化,则可以重载基类的构造函数,即令基类只初始化部分成员变量,然后在派生类的构造函数中显式调用该重载的构造函数。(12.3.1节)

5.派生函数可以覆盖基类函数的实现,即在派生类函数中改变基类函数的实现。

当派生类用与基类成员函数相同的返回值和签名,但却用新的实现方法创建一个函数时,就称为覆盖了该方法。签名包括:函数名、参数表以及可能用到的关键字const。

规则:一旦覆盖了任一个重载方法,那么对这个方法的所有其他原基类函数均被隐藏了,如果不想让他们被隐藏,就必须把它们全部覆盖。

若仍想调用被覆盖的函数,那可以用域限定符(::),即基类::被覆盖的方法。

6.C++扩展了其多态性,允许把派生类的对象赋给指向基类的指针。如:

Mammal *pMammal=new Dog;

Dog是Mammal派生出来的一个类。这样,就可以用该指针调用Mammal类中的任何一个方法。若想调用被Dog覆盖掉的方法,则可以用虚函数来完成。

7.虚函数。在成员函数返回类型前加关键字virtual。表明该类将做为一个基类来派生其他类。

对于虚函数,并不需要在派生类中也定义为虚函数,但继续标记为虚函数是比较好的理解手段。

正确调用函数,对于6中的pMammal指针,调用基类的虚函数,若在派生类中覆盖了该函数,那么就调用覆盖函数,若没有覆盖,就调用基类的虚函数。但该指针不能调用Mammal中没有声明的函数,因为它是Mammal的指针,除非指针是指向Dog对象的指针。

8.一般的规律是:若类中任一个函数是虚函数,则析构函数也应是虚函数。

构造函数不能是虚函数。也没有虚复制构造函数。

若程序需要一个指向基对象的指针,解决办法是在基类中创建一个Clone()方法,并把它设置为虚函数。Clone()方法创建一个当前类的新的对象拷贝并返回这个对象。

virtural Mammal* Clone(){ return new Mammal(*this);}

 

《静态成员变量及函数》学习笔记

1.类的静态成员变量在所有类的实例中是共享的。它是全局数据与成员数据的折中。可以把静态成员变量看成是属于类而不是属于对象的。因此,对类的实例对象中并不会为静态成员变量留出存储空间。

一个常见的错误是忘记定义类的静态成员变量。若HowmanyCats是类Cat的静态成员变量,则其定义句为:

int Cat::HowmanyCats=0;

此外,若该静态成员变量是私有的,那么类必须为该静态成员变量提供一个公有的访问函数。对于没有对象的静态成员,则可以定义一个不属于类的全局函数来访问该成员变量。因此可以在没有对象的情况下访问类的静态成员变量。

2.静态成员函数没有this指针,因此它们不能声明为const,且静态成员函数不能访问任何非静态成员变量。

3.如同数组名,函数名是指向函数的常指针。函数指针的声明如下所示:

long (*FunPtr) (int);

第一对括号必须有,否则就不是函数指针的定义,而是一个返回指针的函数。使用函数指针就如同使用函数名一样。

FunPtr(x);

(*FunPtr)(x);

一样,前一种只是后一种的简化形式。

也可以把函数指针作为函数变量来传递。例如:

void Func (void ( * ) (int &, int &) , int &, int &);

可以用typedef来定义结构 void ( * ) (int &,int &)

typedef  (*VPF) (int &,int &)

则 VPF可以用来代替以上结构体。

4.为创建成员函数指针,可以使用与函数指针创建相同的语法,但要包含类名和作用域运算符(::)。如:

void (Shape::*pFunc)(int ,int);

使用方法一样,只是需要一个类的对象。

 

《高级继承》学习笔记

1.一个类的成员数据有可能包含另一个类的对象。C++程序员称其为外层类包容内层类。

2.私有继承不涉及继承接口,只涉及继承实现。

3.当派生类是一种基类时必须使用公有继承。

当你想把某些功能授权给另一个类而不必访问其保护型成员时一定要使用包容。

当你需要根据一个类而实现另一个类时,并且需要访问基类的保存型成员时使用私有继承。

当需要使用多个基类时,不能使用私有继承,必须使用包容。

当基类的成员不能用于派生类时,不能使用公有继承。

4.如果要把一个类的私有成员数据暴露给另一个类,那么就应把这个类声明为一个友元。

友元关系是不可以传递的,也不可以继承,友元关系也不是互通的。

友元类的声明:

friend class newclass;

友元声明可以放在类声明的任何地方。声明之后,本类的任何成员数据就完全暴露给另一个类。

也可以把另一个类的成员函数声明为该类的友元函数。

 

阅读(249) | 评论(0)


版权声明:编程爱好者网站为此博客服务提供商,如本文牵涉到版权问题,编程爱好者网站不承担相关责任,如有版权问题请直接与本文作者联系解决。谢谢!

评论

暂无评论
您需要登录后才能评论,请 登录 或者 注册