欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C++学习笔记(1)

程序员文章站 2022-04-12 20:36:50
1、头文件:标准的头文件不带 .h 。如果想用C语言的函数,可以用C语言的头文件,推荐的写法是去掉后面 ". h ",前面加上C,比如: stdio.h ==...

1、头文件:标准的头文件不带 .h 。如果想用C语言的函数,可以用C语言的头文件,推荐的写法是去掉后面 ". h ",前面加上C,比如:

stdio.h ==> cstdio , math ==> cmath。

2、名字空间:标准库中的所有名字都放在一个名叫 std 的名字空间中,使用时要加上 std:: 前缀。偷懒的办法是用一句

usigned namespace std来表示自动在标准库的名字前加 std 。

3、输入输出:cin>> 变量 /cout<< 数据,可以连成长串。

4、扩展名: .cpp , .C , .cc , .cxx

编译连接: g++, 用法跟gcc一样 ,或者用gcc -lstdc++ 。

注释: /* */ , // 。

5、名字空间用法:解决名字冲突问题

定义:

namespace 名字{

变量声明、变量定义、函数声明、函数定义 . . .

}

多个名字的名字空间会合并在一起。如果需要也可以在名字空间里嵌套名字空间

namespace hanbo {

namespace a{

char x;

}

double z;

}

使用: 名字空间: :内部的名字 例如:hanbo: :a: :x

统一指定名字空间

using namespace hanbo ;

c : : x

z

using namespace hanbo;

using namespace uc;

6、匿名名字空间(全局名字空间)

全局定义的 int var ; void func( );

可以直接使用名字,也可以加 : :

var = 123;

: : var = 123;

7、输入输出:在写程序时不用再为类型费心了。

注意:写程序时不用费心。运行时还是要正确输入,如果输入无效不会被取走而且读取失败。

8、在C中 结构、联合、枚举

定义完毕后得到的是自定义类型。

struct Student { . . . };

struct Student furong ;

在C++中使用结构、联合、枚举类型来定义变量时,不用再重复 struct 、 union 、enum 关键字。

Student furong;

结构体定义时,成员不仅可以是数据,还可以是函数,甚至可以是类型。

支持匿名联合。

union {

int x;

char y[ 4 ];

};

枚举不在等同于整数类型,如果需要转换可以用强制类型转换。

9、布尔类型:bool 在C99支持

表示是否,只有true/false两个值,可以自动转成整数1/0.其他类型数据当成布尔类型时非零即真零则假。

10、字符字面量:

' * '

在C语言中没有真正的字符字面量,用的实际都是int。sizeof( ' X ' ) == 4;

在C++里sizeof ( ' X ' ) == 1 。

11、void * 类型:

严格限制不能赋值给其他地址类型,

C++是一种强类型语言。

12、类型转换:

强制类型转换在C的基础上增加了一种形式:类型(数据);

C++中不提倡用强制类型转换,如果确实需要类型转换,C++提供了另外4种方式:

satic_cast <类型> (数据)用于数字类型之间以及void * 和别的 * 类型之间

reinterpre_cast<类型> (数据)用于数值类型与地址类型之间或者地址类型相互之间。

const_cast (T 常量的地址)去掉对地址所指向的目标的const限制。

dynamic_cast 以后再说:

例子:

int a = static_cast(123.45);

int b = 100;

void *p = &b;

int *q = static_cast( p );

char *r = reinterpret_cast( q );

const int c = b;

*const_cast( &c ) = 200;

13、函数:

潜规则:1、默认返回int类型,作废。2、空参数表表示参数的个数随意,C++中则表示无参。因此,函数调用前必须要声明或者定义。

重载:允许多个函数同名,但要求不同的参数表(不同类型、或者个数、或者顺序,必须足以让编译器分清该调用哪一个函数)

编译器无法区分该调用哪一个时则产生编译错误。自动类型提升依然适用。与返回类型无关。

形式参数默认值:如果函数某个形式参数在调用时绝大多数情况下都传递的是某个特定实际参数,那么就可以把这个实参值指定为

这个形参的默认值,调用时可以不再传递。有默认值的形参必须靠右。声明和定义分开时,形参默认值放在声明中。

哑元:多余的形参,不需要指定名字,一般是为了跟以前的版本兼容,偶尔也用于其他用途。

宏函数:基本不用,一是不符合强类要求,二是副作用。取而代之是内联函数,用inline表示。内联函数会在调用的地方展开函数代码

而不是产生调用,跟宏函数一样高效,但却有明确的参数类型而且没有副作用。是否真正执行inline的要求,完全由编译器

自己决定。

动态内存管理:C语言用malloc、calloc、realloc、free,返回void * 。

C++用: new 类型

new 类型 (初始值)

new 类型 [ 元素个数 ]

delete 地址

delete[ ] 地址

new (指定地址)类型:在指定的地方分配内存(少用)

指定初始值直接用一对空圆括号的称为零初始化。

char *p = new char ( ); *p == '\0';

14、new失败会throw抛出异常导致程序终止,可以用nothrow来在申请内存失败时像C语言的内存管理函数一样返回NULL,头文件。

15、古怪的关键字,用来表示运算符:and(&&)、 or(||)、 not(!)、 and_eq(&=)、 or_eq(|=)、 not_eq(!=)、 bitand(&)、

bitor(|)、 xor(^)、 compl(~)、xor_eq(^=)。

古怪的指针:成员指针

struct Data{

int year;

int month;

int day;

};

int (Data: : *) p = &Data: :year ;

p = &Data: : month ;

p = &Data: : day ;

16、命令提示符修改:

打开主目录下的.bashrc文件vi ~/.bashrc

在末尾增加两行:

PS1='\W$ '

PATH=$PATH : .

17、引用:本质上是指针(地址),编译器自动取地址、加星号。

定义:类型 & 名字 = 初始值 ;引用必须初始化,用谁初始化它,它就跟谁是同一体,这种关系从开始到结束永不变。

特别说明:形参是在调用时由实参初始化。返回值是在返回时return后面的数据初始化。

引用使用变量本身(同一体)而不是复制数据,改变引用变量的值也就改变了它的同一体的值。

特别说明:引用不是新的数据类型而是表示传递方式。

18、字符串:C风格字符串数组长问题,忘记末尾的 ' \0 ' 出乱码。

C++字符串string类型,头文件,同样支持像字符数组那样按下标访问元素,支持 +、=、各种比较运算,不用考虑

空间问题,成员函数size( )、length( )都可以取得字符串长度,成员函数c_str()可以返回对应的C风格字符串(只读)。

19、数组:C风格的数组长度一旦确定就不能变了,作为参数传递就蜕变成地址无法知道长度只能再追加一个数来表示长度。

C++风格数组用vector<类型> 数组名(长度,元素初始值),用法跟数组一样,随时可以用成员函数resize(长度)来调整大小,

随时可以用成员函数size( )来取得元素个数。头文件

20、头文件是C语言字符串处理函数的头文件,在C++可以照样使用,也可以换成,效果相同,唯一的区别是后者把前者里面的内容都放到了namespace std里。头文件是C++语言自己的字符串类型头文件,里面是对string类型的定义。

21、C语言指针:指针是用来保存某种类型的变量的地址的变量,取地址&:&变量,取得某个地址的变量:* 地址,(*地址). 成员,地址 -> 成员, 向函数传递参数的时候,如果希望函数能操作某个变量里的数据,就把变量的地址传递给函数,函数里就可以根据地址找到内存中的那个变量,从而取得或者改变变量里的值。地址运算:p+n,p-n,p[ i ]==>*(p+i),*p,p-q,结果是多少个单位(元素),单位为sizeof(地址类型),比较,指针定义时建议带初始化从而避免野指针,不要通过空指针或者目标已经释放掉的指针去找变量,不要返回非静态局部变量的地址 作为函数的返回值,作为函数形参的指针如果不用于改变目标变量的值就尽量加const修饰保护目标不被误伤。

22、输出地址:输出字符类型的地址,为了保持与C语言的兼容,系统处理成输出字符串。如果需要输出地址应该转换成其它地址类型。

引用:引用一个常量,应该加const。用临时结果(运算结果或者函数的普通返回值)去初始化一个引用时,也应该对引用加const。

const int x = 20;

const int &rx = x;

23、面向对象:Obiected-Oriented Programming

C语言是一系列函数描述程序的工作过程,称为面向过程。C++把程序里面的数据和对数据的操作合起来看做一个实体(称为对象),编程工作就是操作这些对象相互作用来完成程序的功能。这样的编程方式称为面向对象编程。

抽象:每个实体都有很多的数据和行为,只提取咱们关心的数据和行为。

封装:用抽象的结果来描述实体的类型,称为封装。在C++中,可以用结构来实现封装,但出于跟C语言兼容,一般在C++中结构也只封装数据,用class关键字来实现真正的封装,称为类。封装好的类和结构都只是一个类型。

定义类:

class xxx{

成员变量或者成员函数的定义

};

结构与类的区别:结构默认就是简单打包,内容对外是透明的,可以通过结构变量访问它的任何一个成员。类默认是保密封装,对外是不透明的,不允许通过变量直接访问它的成员,除非是故意公开的(public)。一般公开的都是函数。

这种类或者结构类型的变量(实例)就称之为对象。

成员函数如果在外面去实现(函数体不写在类内部),那么函数名应该写成 “ 类名::函数名 ”。结构也是如此。

文件结构:一个类一般写成一个.h文件和一个.cpp文件,在.h文件中,成员函数只有声明,没有函数体,在.cpp文件中包含.h文件并且给那些成员函数的定义(包括函数体)。

24、创建一个对象时会自动调用一个成员函数,称为构造函数。函数名就使用类名,无返回类型。构造函数可以重载,一般访问限制为公开。创建对象如果不传参数,不要带空括号,否则被误认为是一个函数声明。

如果一个类没有构造函数,编译器会自动为它产生一个不干事的空函数。只要有构造函数了,编译器就不在为这个类产生这个无参构造函数(默认构造函数、缺省构造函数)。

25、一个对象创建时如果需要做额外的事情,就可以放在构造函数里面。创建对象的实际操作步骤:分配内存,执行构造函数。如果成员是对象,成员的构造函数会在整体的构造函数之前执行。

26、this:调用成员函数时,编译器总是自动悄悄把点(或指向)它的那个对象的地址作为一个隐含参数传递;在成员函数内部,用关键字this来接收这隐含参数,类型为 X * const ,表示当前对象的地址。this:固定指向当前对象,只存在与成员函数中,对哪个对象调用这个成员函数的时候,this就指向哪个对象。 *this 就代表当前对象。

27、静态成员:属于整个类的数据或者行为

静态成员变量:所有这个类的对象公用一份,在类外面初始化,初始化时要在变量名前面用类名双冒号修饰。

静态成员函数:所有这个类的对象共同的行为,不依赖于任何一个对象,因此静态成员函数中不存在this,只能访问静态成员,访问非静态成员必须要指明对象。

28、初始化列表:

在构造函数定义里紧跟参数表之后可以有一个初始化列表,用冒号开头,后面有若干对“成员变量(初始值)”,多对之间用逗号分隔。初始化列表是初始化常量成员和引用成员的不二法门。

29、动态创建和释放对象

new 类名 delete 地址

new 类名 (参数) delete 地址

new 类名 [ 个数 ] delete [ ] 地址

返回的都是地址。delete p ; p = NULL ;

一个变量的创建和释放:

全局变量:main执行之前创建,main返回后释放

静态局部变量:第一次执行到时创建,main返回后释放

普通局部变量(含形参):每次执行到时创建,超出作用范围时释放

临时结果:临时创建,当场使用,立即释放

动态变量:执行new时创建,执行delete时释放

-----------------------------------------------------复习-----------------------------------------------------

1、面向对象:封装(写类),前提是抽象。从对象、行为/交互角度去考虑程序如何实现。

类定义:数据、函数、访问限制(public允许本类之外的函数访问,private只允许本类的成员函数访问)。多文件,类实现放在 .cpp 文件,类定义里面函数只声明(放在 .h 文件中#ifndef)。

构造函数:创建每一个对象时总会自动调用构造函数,可以重载。如果没有人为定义构造函数,编译器会自动产生一个无参的。

this :固定指向当前对象,只存在于成员函数中,对哪个对象 调这个成员函数的时候,this 就指向哪个对象。 *this 就代表当前对象。

2、静态成员:属于整个类的数据或者行为

静态成员变量:所有这个类的对象公用一份,在类外面初始化。初始化时要在变量名前面用类名双冒号修饰。

静态成员函数:所有这个类的对象共同的行为,不依赖于任何一个对象,因此静态成员函数中不存在this,只能访问静态成员访问非静态成员必须要指明对象。

3、析构函数:对象释放时会自动调用的函数,函数名为 ~类名,总是无参,无法重载,一般也是公开的,无返回类型。

默认析构函数:如果一个类没有定义析构函数,编译器会自动产生一个什么也不做的析构函数。

如果想在main之前或者main之后做一些工作,可以用全局对象的构造函数和析构函数来实现。

对象本身占用的内存空间会在超出作用范围时或者delete时自动释放了,如果对象还额外分配了资源,可以在析构函数中释放那些额外的资源。

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

30、匿名对象:直接定义一个对象但不给提供名字,这个对象会在这个语句之后就立即释放而且编译器能对匿名对象的使用进行最大限度的优化性能。提倡使用匿名对象,前提是它是一次性对象。

类名 变量名(参数);

类名 (参数);(匿名对象声明,没有变量名,对象只能当场使用。)

只有一个参数时,匿名对象可以看成类型转换。

如果要禁止自动用匿名对象实现类型转换,可以用explicit修饰构造函数。

31、拷贝构造:

零初始化:类型名(),对于基本类型而言是数值0,对于类或者结构类型而言是匿名对象。

当用一个同类对象初始化一个同类的新对象时,调用的自然是拷贝构造函数。一个类如果没有定义拷贝构造函,编译器会自动产生一个拷贝构造函数,内容是逐个复制对应的成员。这一般能满足程序的需求。但如果其中有指针成员指向动态内存时,两个对象的指针成员都会指向相同的地方,两个对象释放时都会delete这块内存从而导致错误。要避免这个问题,需要自己写拷贝构造函数,让每个对象指针成员各自指向一片动态内存,把旧动态中的数据复制过去。让两片动态内存中的数据相同。

32、复习

小点心:各种增强(强类型,重载,引用/复制)

面向对象:封装、抽象、类(定义,实现)类型,对象,构造函数、析构函数、默认什么都不干,成员函数。私有数据,公开函数。成员函数中有this指针指向用来调用这个成员函数的对象。

静态成员函数/变量属于整个类,访问时不需要通过对象,直接用“类名::成员”,所以没有this。静态成员在类外面初始化(类似全局变量但要用“类名::成员”格式。)

初始化列表:在构造函数的定义的参数表之后函数体之前,冒号开头,若干对“成员(初始值)”,逗号隔开。成员如果是常量或者引用只能用初始化列表。

匿名对象:不起名的对象,同样调用构造函数和析构函数。

一个参数的匿名对象可以看成类型转换,编译器可以自动完成这样的类型转换。用explicit修饰构造函数可以禁止编译器通过它来自动进行类型转换。

new/delete 在堆空间创建对象,不会自动释放,必须要认为释放。

注意配对:new与delete,new[ ]与delete[ ]配对,特别注意new/delete不要跟malloc/free交叉配对。

拷贝构造函数:创建新对象用另一个同类对象初始化它时,调用拷贝构造函数(形参是本类类型的const引用)。一般默认的就不可以了。如果有指针成员指向动态内存,必须要自己来定义拷贝构造函数实现需要的功能,特别是指针成员不要直接复制,而让他们各自指向一块不同的动态内存,然后复制动态内存中的数据。

33、面向对象的扩展功能

备注:编译器在编译一个类时,会先扫描类定义(不含函数体),之后才扫描类实现(各成员函数的函数体,静态成员变量的初始化),所以在类的成员函数里可以访问在后面才出现的成员。

const对象调用的成员函数也要求不会修改成员变量的数据。成员函数可以再参数表后用const来宣称自己不会修改当前对象的数据,称const成员函数。因此,用const对象只能调用const成员函数。

const对象内部如果确实有需要修改的数据成员,用mutable来修饰即可。

34、 运算符重载:在C++中,运算符都被当成函数。C++允许程序员自己来规定运算符如何工作,方法就是自己定义相应的运算符函数,比如 operator &,operator +,operator <<,称为运算符重载。运算符函数代替运算符,其中函数参数对应运算符的操作数,函数返回值对应运算符的运算结果。

35、 运算符函数的形参和返回类型尽量用引用,尽量加const。运算符函数的定义格式之一:全局函数

返回类型operator运算符(参数表)

双目:运算结果的类型operator运算符(操作数1,操作数2);

单目:运算符结果的类型operator运算符(操作数)。

36、在全局函数中如果需要访问对象的非公开成员,需要在类中对这个全局函数进行授权,方式是在类里用friend声明这个函数为友元。一般在类里面声明在外面定义,也可以在类里面friend声明的地方直接定义,但它依旧是友元,不是成员。

如果需要的话,一个类A也可以把另外一个类B声明成友元,这等同于把B类的所有成员函数都声明为A的友元了。一般不这样做,而且友元声明是单向且不传递的。

37、运算符函数的定义格式之二:成员函数

返回类型operator运算符(除第一个操作数之外的参数表)

双目:运算结果类型operator运算符(第二个操作数)

单目:运算结果类型operator运算符()

以成员函数形式定义时,第一个操作数作为了当前对象,不需要作为实参传递,只需要传递剩余的操作数就可以了。在运算符函数里可以通过*this或者this->来访问第一个操作数。

规定只能用成员函数的[ ]、( )、=、->、强制类型转换。

如果有指针成员指向动态内存,还应该自己写赋值运算符函数来实现跟拷贝构造函数相似的功能。三大:拷贝构造、赋值、析构。

38、类型转换运算符函数格式为

operator 类型名()

不写返回类型,返回类型跟“类型名”一致,只能是成员函数。

圆括号运算符时,参数个数不定。函数定义格式:

返回类型operator()(参数表)

支持圆括号运算符的对象也称为函数对象,因为使用的形式特别像函数。

39、复习

运算符重载:

编译器把运算符当作函数

全局函数

函数名:operator 运算符

参数表:所有的操作数

返回类型:合乎情理即可

成员函数

函数名:operator 运算符

参数表:除第一个之外的操作数,当前对象作为第一个操作数

返回类型:合乎情理即可

编译器会尝试两种形式([ ]( )=->type只当成员函数),如果两种都有或者都没有,编译失败。每个运算符函数只允许有其中一种形式。

程序员可以通过运算符函数自己设计运算过程(函数体)以及拿什么作为运算结果(return的数据)。

. . . operator + (const A& x, int n);

A obj ; obj + ' * '

operator = 函数不写也可以使用,默认逐个成员赋值,如果有指针成员指向动态内存是需要自己来写。

operator type ( ) 不写返回类型,却胜写返回类型,因为函数名里面已经包含了返回类型。

ostream operator << (ostream &o, const A& x)

istream operator >> (istream &i, const A& x)

这两个只能写成非成员函数,如果需要访问对象的私有成员,就要在类里面把他们声明为友元friend 。

const 对象只能调用const成员函数(const在参数表后)。

const成员函数里的this是const A* const ,而普通成员函数里的this是 A * const。const对象中的mutable修饰的数据成员允许修改。

const int *p;//p指向const int

const const *;//同上

int * const p;//p本身是const

const int * const p;//p本身是const而且指向const int

40、运算符重载

-> 返回内部某个结果变量的地址,编译器通过这个地址执行真正的 -> 操作访问里面的成员。

new/delete 重载负责内存的分配和释放,编译器会在内存分配之后调用构造函数,内存释放之前调用析构函数。有[ ]和没有[ ]的是不同的运算符。

++/--

前++前--,正常单目运算符用法,因为计算结果就是对象的最新值,因此可以直接拿这个对象作为计算结果

成员:A& operator ++ ( ) { . . . return *this; }

友元:A& operator ++(A& x){. . . return x;}

后++后--,非正常用法,计算结果是对象的旧值,必须用临时空间来保存,不能拿对象本身作为计算结果,定义运算符函数时需要一个多余的int哑元。

成员:A operator ++ ( ) { . . . return old; }

友元:A operator ++(A& x){. . . return old;}

运算符重载一定要合乎情理。运算符重载是自己规定运算符对于特定类型的操作数应该如何工作,对于基本类型的运算符不应该操作,不应该创造新的运算符,不应该改变操作数的个数。

有些运算符不允许重载: [ . ] [ .* ] [ ? : ] [ : : ] [ typeid ] [ sizeof ]