c++学习笔记(一)

来源:互联网 发布:mysql php apache集成 编辑:程序博客网 时间:2024/05/20 09:48

面向过程的语言,如c设计理念是自上向下(top to down),即将一个实体不断解构为越来越小的组件,直至不能再结构。编程实质是一个减法过程

面向对象的语言,如C++设计理念是自下向上(down to top),即不断建构程序所需的组件(类),然后组装成实体(对象及其集合)。编程实质是一个加法过程

类是一种规范,它规定了对象可以使用的数据格式,及对这些数据可进行的操作。类跟对象的关系,可以参照类型与变量的关系。

对象则是根据类规范构造的特定数据结构实体。

函数头:描述函数与调用它的函数之间的接口。包括:

1、函数返回类型:函数名前面的部分,描述从函数返回给调用函数的信息。void返回类型代表不返回任何值。通常约定返回值为0则表明程序运行成功

2、形参列表:函数名后圆括号内的部分,描述从调用函数传递给被调用函数的信息。

如果编译器在main()函数的函数体末尾没有遇到返回语句,则默认为return 0;但这条规则只适用于main()函数,其它函数必须有返回语句。

独立运行的程序都必须有一个main()函数,而动态链接库(DLL)模块等程序则可以没有main()函数,因为它们不是独立运行程序

名称空间:各个预处理头文件放置在空间名称中,要使用空间名称中的函数,可以使用using namespace std;代表使用std名称空间进行编译。

对于声明变量,C++尽可能在首次使用变量前声明它。C++应当为程序中使用的每个函数提供原型。有些语言中,有返回值的函数才被称为函数(function),没有返回值的函数被称为过程(procedure)或子程序(subroutine)

程序访问名称空间std的四种方法:

1、将using namespace std;放在所有函数定义之前,让文件中所有函数都能访问std

2、将using namespace std;放在特定函数定义函数体内,仅让该函数能访问std

3、在特定的函数体内使用using  std::cout;从而使该函数仅能使用std名称空间中的特定函数,如cout

4、不使用编译指令using,而使用std::cout直接调用std名称空间函数

d.dddE+n代表将小数点向右移n位。d.dddE-n代表将小数点向左移n位,浮点数这个名字的由来就是因为小数点可以移动。


自动执行的类型转换:

1、将一种算术类型的值赋给另一种算术类型的变量时

2、表达式中包含不同的类型时

3、将参数传递给函数时

1.1初始化和赋值时进行的转换:将一种类型的值赋给另一种类型的变量时,值将被转换为接受变量的类型。

1.2以{}方式初始化时进行的转换:使用花括号进行的初始化被称为列表初始化。这时,初始化不允许一个值在被赋值时被缩短位数。也即不允许大位数的值赋给小位数的变量,除非大位数的值没有超过小变量位数的存储范围。

2、表达式中的转换:整形提升,即转换为表达式中最高位数的那个类型

3、传递参数时的转换:由函数原型的数值类型决定

强制执行的类型转换(cast):

(typename)valuye         //c格式,注意:这里必须加上圆括号

typename (valuye )       //c++格式

数组的通用声明格式

typename arrayname[arraysize]; //arraysize只能是常量或常量表达式

结构中的位字段

字段的类型应为整型或枚举,接下来是冒号,冒号后面的数字代表该字段占用的位数。如果字段没有名字,则表明提供了一个字段间的间距。unsigned int sn : 4;\\sn使用4个bit

指针变量和&普通变量用来代表地址;而普通变量和*指针变量则用来代表数据值。

要将数字值作为地址来使用,必须通过强制类型转换。如 int *p;    p=(int *) 0XB8000000;注意:p指向一个int值的地址,并不代表p自己是int类型。它是由系统架构(内存)位数决定的

new分配的内存与常规变量分配的内存不同,前者从堆或自由存储区分配内存,后者则从内存区域分配内存,前者需要人为释放内存,而后者在函数返回时自动释放内存

new和delete使用规则:

1、delete只能释放由new分配的内存

2、不能用delete对同一内存释放两次

3、如果用new[ ]为数组分配内存,应使用delete[ ]来释放它

4、如果用new[ ]为实体分配内存,应使用delete来释放它

5、delete可以应用于空指针

指针和数组名都可以来代表地址,唯一的区别是:1、指针是一个变量,而数组名是一个常量2、对数组应用sizeof运算符得到的是数组的长度,而对指针应用sizeof得到的是指针的长度。

数组的地址:

数组名被解释为其第一个元素的首地址。而&数组名则得到整个数组的首地址:

short ar[10];

cout<<ar;       \\显示&ar[0],一个2字节内存块的首地址

cout<<&ar;   \\显示整个数组的首地址,一个2o字节内存块的地址

因此ar+1将地址值加2,而&ar+1将地址值加20

这样声明整个数组的地址指针并赋值:short (* pas)[10]=&ar;\\注意* pas的圆括号不能省略,其次,如果要描述变量的类型,变量名可以省略。(* pas)[0]即是数组中的第一个元素。

指针的地址运算都是所指向数据类型的运算,而不能简单认为是地址的简单运算。


顺序点:在程序执行下一条语句之前,顺序点前的赋值运算符,递增、递减运算符等执行的所有修改都必须完成。分号就是一个顺序点

完整表达式:它不是另一个更大表达式的子表达式。完整表达式的末尾都是一个顺序点


设计循环语句的原则:

1、指定循环终止的条件

2、在第一次测试语句前,对条件初始化

3、在条件被再次测试前,更新条件。


引用变量:

int rats;

int & rr = rats;   \\使rr成为rats变量的别名,即rr和rats拥有相同的内存地址和值。

在定义引用变量的同时,必须对其初始化,而不能像普通变量那样,在定义后再初始化。如

int rats;

int & rr = rats;
只能这样创建一个引用变量rr,而不能这样

int rats;

int & rr;

rr= rats;        \\不允许先声明后初始化,在声明引用变量的同时,必须对其初始化,


对函数参数的引用:

被调用函数通过对调用函数中的变量的别名进行引用,从而允许被调用函数能够访问调用函数中的变量。这称为按引用传递

而C只能按值传递,即被调用函数只能使用调用函数的变量值的拷贝。当然C也可以采用按指针传递的方法对调用函数变量进行访问。

当只想让被调用函数使用按引用传递来的参数的值而不是改变它的时候,应该在函数原型和函数头中使用const,如function(const int & rr );对于形参为const引用的C++函数,如果实参与形参类型不匹配,则采用类似按值传递的方式

应尽量将引用形参声明为const因为:

1、避免无意中修改调用函数数据

2、是被调用函数能够处理const和非const两种类型的实参,

3、使被调用函数能生成临时变量。


左值参数:可被引用的数据对象,如变量,数组元素,结构成员,引用和解引用指针都是左值。常规变量和const常量都可视为左值,因为可以通过地址访问它们

非左值则包括字符常量(字符串常量除外,它们本质是指针类型的地址。),也包括多项表达式。


当函数的返回值是一个引用时,应注意该引用不应是局部变量,因为局部变量会在函数返回时被清除。避免这个问题最简单方法是,返回一个对调用函数参数的引用作为返回引用,这样返回引用将指向调用函数的参数数据。


设置引用参数的两个理由:

1、能够修改调用函数中的数据

2、传递引用而不是数据,提高运行速度,并节省内存


传递引用、指针或按值传递的指导原则:

1、不需要修改调用函数的源数据:

1.1数据对象很小,如默认内置数据类型或小型结构,按值传递

1.2对象是数组,只能使用指针,并将指针声明为const

1.3对象是较大的结构,使用const指针或const引用,这样可以节省时间和空间

1.4对象是类,使用const引用,而且类传递对象参数的标准方式就是按引用传递。

2、需要修改调用函数的源数据:

2.1数据对象是默认内置数据类型或小型结构,使用指针

2.2对象是数组,只能使用指针

2.3对象是结构,使用引用或指针

2.4对象是类,使用引用


函数的默认参数:

在函数原型声明中设置默认值,如char * left(const chat * str , int n=1);默认参数值是初始化值,如果传递参数时省略参数n,那么将默认该参数值为1

注意,只能在原型声明中设置默认值,函数定义与没有默认参数时的格式完全相同

对于多参数函数,必须从右向左设置默认值,否则设置无效。如:

harpo(int n=1,int m=4);          \\设置有效

harpo(int n=1,int m);              \\设置无效
默认参数可以让你使用不同数目的参数来调用同一个函数。函数多态则可以使你能够使用多个同名和函数。


函数重载:

函数重载定义的关键在于函数列表,函数列表也被称为函数签名(function signature),如果两个函数的参数数目、类型及排列顺序都相同,虽然它们的变量名并不完全一样,它们的签名也相同。即函数原型声明相同的两个函数签名相同。


头文件常包含的内容:

1、函数原型、结构、类、声明

2、#define或const定义的常量

3、模板声明

4、内联函数

声明区域:可以在其中进行声明的区域,如全局变量的声明区域为其声明所在的文件,而局部变量的声明区域为其声明所在的函数代码块

潜在作用域:变量的潜在作用域从声明点开始,到其声明区域的结尾结束。潜在作用域比声明区域小,因为变量并不总是在声明区域起点被声明。

作用域:程序能使用变量的区域称为变量的作用域,潜在作用域是一个变量的最大作用域,作用域有时会在潜在作用域中失效。


名称空间,一个名称空间不会与另一个名称空间中的相同名称数据对象发生冲突。即允许程序访问这两个空间中相同名称的数据对象。

名称空间可以是全局的,也可以是另一个名称空间内的局部的。但不能是程序代码块内部局部的。


::作用域解析符。


访问名称空间中名称的方法:namespace_name::member/element of the namespace_name。包含名称空间的名称称限定的名称。如jack::pail

using声明和using编译指令:

这样就不用在每次使用限定名称时都要对它进行限定,using声明使名称空间某个限定名称可被使用,而using编译指令则使整个名称空间可用,

using声明由限定名称和using组成,将特定的名称添加到该名称所属的声明区域中。:如using jack::pail;

using编译指令由关键字useing namespace和名称空间名组成,使名称空间中的所有名称都可用,而不需要使用作用域解析运算符。如:using namespace jack;

在函数内部,using声明声明的限定名称属于局部变量,因此不能在函数内部再声明同样名称的变量;而using编译指令尽管位于函数内部,但其声明的名称空间中的名称仍然是全局的,因此可以在函数内部命名同名变量,并且该局部变量将隐藏名称空间中的同名变量的作用。也就是说,函数内部用useing编译命令声明的,局部名称空间中的变量作用域对该函数来说,也是全局的。但是,函数中函数内部用using编译命令声明的,局部名称空间中的变量对其他函数来说,则使局部的,也即不可用的

在声明区域中,使用using声明将名称空间中的名称导入该声明区域后,不允许再声明相同名称的数据,而使用using编译命令将整个名称空间导入该声明区域后,允许再声明相同名称的数据,但该数据将隐藏名称空间中的同名数据。

注意:使用using声明比使用using编译命令更安全,因为using声明不会改变同名局部变量的数据。

可通过给名称空间创建一个简洁的别名,来简化对嵌套名称空间的使用,如:

namespace MEF=myth::elements::fire;

using MEF::flame;

未命名的名称空间:这种空间只能用于声明该名称空间的文件,而不能用于外部文件。这就使该空间成为内部链接的、静态的空间。

例如:static int counts;等同于下面这个未命名名称空间

namespace

{

int counts;

}

名称空间使用原则:

1、对外部全局变量和静态全局变量,使用有名称的名称空间代替它们

2、如果开发了一个函数库或类库,将它们放在名称空间中。

3、不要在头文件中使用using编译指令,如果一定要使用,应放在#include之后

4、导入名称时,首选域解析符或using声明的方法

5、对于using声明,首选将其作用域设置为局部而不是全局。


OOP特性:

1、抽象

2、封装和数据隐藏

3、多态

4、继承

5、代码的可重用性


类构造函数:

因为程序不能直接访问类中的Private数据成员,而只能通过成员函数来实现对它们的访问,所以设计一类特殊的成员函数,它们与类名的名称相同,用来对Private数据成员进行初始化。

在类声明的public部分声明构造函数原型,通过构造函数的原型声明,可以为类Private数据成员初始化,构造函数没有返回类型的声明。这样,在程序声明对象时,将自动调用构造函数

注意:构造函数的参数名不能与类Private数据成员的名称相同,因为,构造函数的形参表示的是赋给类成员的值,而非类成员。为避免出现这种问题,通常在类Private数据成员的名称使用前缀m_,或者在成员名中使用后缀_。这样构造函数就可以使用同名形参了。

对构造函数的显式和隐式调用:

对stock类对象garment的初始化可以通过对stock类构造函数的显式和隐式调用来完成:

stock garment ("furry",50,2.5);

等同于

stock garment=stock ("furry",50,2.5);


stock *garment=new stock ("furry",50,2.5);       将创建一个无名stock类对象,通过garment指针来管理该对象

通常通过使用对象名.运算符加类成员函数的方法来调用类函数,但是却不能用这种方法调用构造函数,因为,对象在构造函数构造出它以前,是不存在的。


析构函数:在类名前加~就可以创建一个析构函数,析构函数没有参数,也没有返回值类型。是否调用析构函数由编译器决定。通常不用在代码中显式地调用该函数。


为保证被调用函数不会对调用对象的类数据进行修改,应在被调用函数的圆括号后加const关键字


this指针:所有类方法都将this指针指向调用对象的地址。this指向对象的地址,因此*this表示调用对象。


类作用域:不能从外部直接访问类成员,即便是公有成员函数,也需要通过对象实现对它们的访问;同时,在定义成员函数时,必须使用作用域解析运算符。


. 直接成员运算符,即访问的内存中的值

->间接成员运算符,即访问的是内存的地址

::作用域解析运算符,即访问的是作用域空间


类成员运算符重载函数:operator op (argument-list),它跟其他类成员函数一样。

例如:operator + ( )重载+运算符;operator [ ]( )重载[ ]运算符,即数组索引运算符。

假设 x、 y 、z为AA类定义的对象,operator + ( )为AA类的成员函数,则:

x=y+z;             这个表达式,将被编译器解释为:

x=y.operator + ( z);         \\即y对象通过调用operator + ( )函数,将z对象作为参数,来计算并将返回结果赋给x。


因为使用类成员运算符重载函数时,类成员运算符重载函数只能作为表达式的左边操作数,因为在右边的话编译器将不能识别类成员函数。因此引入非成员重载运算符函数,这样就解除了对函数只能是左边操作数的限制,但现在又出现了问题,非成员函数是不允许对类似有类型的数据,因此,又定义了能访问类似有成员的函数,被称为友元函数

友元函数原型声明:

friend time*operator + ( double m, const time &t);            \\在类声明中声明友元函数原型,并加上关键字friend。

1、友元函数虽然在类中声明,但它不是类成员函数,因此不能用成员运算符.来调用

2、友元函数有与类成员函数相同的类访问权限

3、友元函数的定义不需要使用关键字friend,也不需要使用类名加::作用域运算符来限定





0 0