C++ 随笔(仅针对64位window系统)

1.数据类型与表达式

  • 1字节(8位): (unsigned) char

  • 2字节(16位):(unsigned) short int

  • 4字节(32位):(unsigned)(long) int和float

  • 8字节(64位):double

  • 16字节(128位):long double

  • 以U/u、L/l、F/f结尾可以表示无符号型、长型、浮点型常量。

  • 标识符:不能使用字母、数字、下划线之外的字符,首字符不能是数字。

  • 位bit,字节Byte,1KB=1024Byte。

  • 十进制(dec),八进制(oct)第一位是0,如042,十六进制(hex)0x开头。

  • sizeof()返回变量或操作数类型占用内存的大小,单位是字节数。

  • 3/2的结果是1,3.0/2或3/2.0结果是1.5,3/2x2.0=2.0,2.0x3/2=3。

  • 优先级(从高到低):(+,-(正负),*(指针),&,++,- -,!结合方向自右向左)、(x,/,%)、(+,-(加减))、(<,<=,>,>=)、(==,!=)、&&、||,括号最高逗号最低。
    C++ 随笔(仅针对64位window系统)_第1张图片

  • &是按位与,&&是逻辑与,<<是左移,>>是右移。

  • ++i(前缀):i加1再使用i ;i++(后缀):先使用i再加1。
    前缀函数:将值加1,然后返回结果;后缀函数则是:首先复制一个副本,将其加1,
    然后将复制的副本返回。因此,对于类而言,前缀版本的效率比后缀版本高。但对于普通的变量区别不大。

2.循环语句与控制执行顺序语句

  • switch语句表达式的值只能是整型或者字符型,且case后面要加break才会终止执行下面的语句。
  • do…while和while语句的区别就是do…while至少执行一次循环体。
  • break语句可以直接跳出循环体,但在多重循环中,一个break语句只能向外跳出一层,且break语句对if语句不起作用。
  • goto 语句标号;无条件转移语句,使程序跳到标号处并执行冒号后面的语句。
  • exit(表达式);和abort();两个终止程序执行的函数都应包含标准库头文件stdlib.h(里面定义了五种类型、一些宏和通用工具函数)。前者会关闭程序时会关闭打开的文件释放空间等,后者不会做收尾工作,且括号内不能有任何参数。

3.函数

  • 形参和实参;假设被调用函数是void A(int a){};那么这个a就是形参,调用前不分配内存。int b; A(b);这里的b是实参。
  • 函数的类型和return返回值的类型应该一致,如果不一致则以函数的类型为准对返回值进行类型转换。
  • 调用函数遵循“先定义后调用”,被调函数体应该出现在主调函数之前,如果位于主调函数之后,则必须在调用此函数前对其进行声明,声明时可以省略形参变量名只给出形参类型。函数声明的位置可以在主调函数体内也可以在体外,只要将其放在调用语句之前即可。
  • 递归函数:在函数体内调用自身。
  • 内联函数:内联函数声明定义后(inline),编译器将函数体的代码直接插入到函数调用处(节省调用函数的时间开销,但是空间会增加),要加以衡量,适用于短小简单的函数体。注:函数指针会阻止内联
  • 重载函数必须在参数类型或个数方面不同,仅有返回值类型或者函数定义类型不同是不行的。
  • ::是作用域运算符。
  • const是常量类型修饰符,被const修饰的常类型的变量或对象的值不能被更新或者修改。
  • extern型(外部类型)变量,extern型变量一定是全局变量,作用是扩展作用域。例如有a.cpp和b.cpp两个文件,a.cpp中定义全局变量int x;则b.cpp在引用a前要使用extern int x;声明变量x是引用其他文件中的外部变量。编译中遇到extern,系统先在本文件寻找全局变量的定义,找不到则在连接时去其他文件寻找,找到了就扩展作用域到本文件。extern修饰函数则声明此函数是在其他文件中定义的。
  • auto型(自动型)变量,C++编译器默认局部变量为自动型变量,若没有初始化则初值不确定,例如函数调用的形参每次调用都会分配空间最后释放回收,两次调用分配的地址可能不同。
  • static(静态)局部变量,不会回收空间,保留上一次函数调用结束的值。如果用static修饰全局变量,则所定义的变量仅仅局限于本文件内使用,外部文件用extern也无效。用static修饰函数也是同理。
  • register型变量,指示编译器让这类变量不放在内存应尽可能分配到寄存器中,提高存取速度,如果没有声明,现在的编译系统会自动识别使用频繁的变量放到寄存器中,所以一般没有必要声明。
  • 若没有初始化,auto型初值不确定,全局变量和static变量默认值为0。

4.函数数组与函数指针

double ar[10] = {xxx};
int da[3][4] = {xxx};
int n = 10;
const int* p = &n;	//一个指针指向const int
int * const p = &n; //一个const指针指向int
show (double ar, 10) ; //调用show函数
sum(da, 3); //调用sum函数,2和3的格式都可以调用
void show (const double arr[]int n){}//被调用函数1
void sum(int (*a)[4], int size){}//被调用函数2
void sum(int a[][4], int size){}//被调用函数3
  • 这里的arr不是数组名,是一个指针,指向数组ar。这样的话假如数组很大的话就不用复制过去了。const的作用是保护原始数组ar不被修改。
  • 被调函数2和3格式都是正确的,两个a都是指针而不是数组。其中被调函数2的括号是必须的,见第7节解释。
  • 获取函数的地址很简单:只要使用函数名(后面不跟参数)即可,带参数的话就会返回一个值了。
double pm(int x)
double pn(const int &x) //const使x不会被修改,引用&则使参数不用复制给x,而是使用原来的对象
double (*pf) (int x) ;	//pf是一个指向函数的指针
double *pf (int x) ;     //函数名为pf返回一个double型的指针
pf = pm;				//将函数pm的地址给pf,pf指向函数pm
pm(4) == (*pf)(4) ,如果是C++  允许pf(4)

5.编译预处理

源程序.cpp
编译预处理
编译
.obj
连接
.exe
  • 源程序中以“#”开头占一行的命令,为源程序的编译预处理命令,在预编译命令在正式编译程序之前会首先执行。主要包括宏定义(#define)、文件包括(#include)、条件编译(#ifdef 宏名/表达式 #endif)。
  • #include文件包含命令,在编译预处理时相当于将所包含的文件全部复制到该命令行处,然后编译链接这个文件。
#ifdef 
	程序段1
<#else
	程序段2>
#endif
对于条件编译,如果源程序前面定义了宏名则编译程序段1,否则编译程序段2。
所以在项目中添加一个头文件不是库里面的就会加这个条件编译,这样在其他源程序
(.cpp)中包含这个头文件的时候就会自动预编译。

6.数组

  • C++不允许对数组的大小动态定义,所以定义数组的时候大小不能是变量。
  • 数组如果没有初始化则每个元素都是不确定的随机值,除非定义为全局变量或者静态随机变量会默认置为0。假如初始化数组只初始化了前几个值,那么后面的值都是0而不是随机值,所以初始化第一个元素0,就可以全部初始化为0。PS:如果是C++11,一个{}就可以。
  • 对于二维数组,可以省略第一维,但不能省略第二维,如int a[][2];C++中是合法的,但是为什么在Qt里面这种定义没有初始化就不合法?
  • 在C++中,数组名被认为是数组在内存中存放的首地址。在数组名作为函数参数时,实参和形参都应该用数组名。数组名不能直接赋值因为数组名就是一个地址,可以看作一个常量,如*a的值为a[0]。PS:如果是字符数组,输出字符指针指向的字符开始直到’\0’结束。
  • C++对字符使用单引号,对字符串使用双引号,例如"S"表示由’S’和’\0(结束标志)'两个字符组成的字符串,占用两个字节的存储单元。char s[] = “abc\0de”,cout输出为abc,因为\0是结束字符,但是cout<
  • 字符串连接函数:strcat;字符串有效长度:strlen(char *s);大写变小写:strlwr;小写变大写:strupr;复制指定长度数组:strncpy;
  • C-风格字符串与常规char数组之间的一个重要区别是,字符串有内置的结束字符(包含字符但不以空值字符结尾的char 数组只是数组,而不是字符串)。这意味着不必将字符串长度作为参数传递给函数,而函数可以使用循环依次检查字符串中的每个字符,直到遇到结尾的空值字符为止。

7.指针

  • 指针是一种数据类型,存放地址的变量称为指针型变量,指针变量的值也简称为指针,所以指针就是地址。警告:一定要在对指针应用解除引用运算符(*)之前,将指针初始化为一个确定的、适当的地址。
  • 指针变量只能指向同一类型的变量,与指针有关的运算符:&和*。
  • &:取地址运算符,用于取得一个可寻址数据在内存中的存储地址。
  • *:间接引用运算符,用于访问该指针所指向的内存数据。
  • 指针引用一维数组:int a[11],*p;p=&a[0]或者p=a;
  • 函数名是该函数所占内存区域的首地址。
int fun1(int x,int y){cout << (x+y) ;return 0;}
void fun2(int (*p)(int,int)){p(3,4);}
void main(){fun2(fun1);}
函数输出结果为7
  • 对于二维数组,如int a[2][2];有a = *a = a[0] = &a[0][0];因为都是首地址。a[0]+1 = &a[0][1];
int *a[3];表示a是一个具有3个元素的一维数组名,数组元素为整型指针变量。
int (*b)[4];表示b是指向具有4个整型元素的数组的行指针变量,而不是一个数组名。
int **p = a;
如果实参是一个二维数组,例如int c[5][6];那么当数组名c作为实参调用函数时,被调函数的形参可以表示为
int (*x)[6];或者int x[][6];或者int x[5][6];
  • new动态存储分配:系统根据运行时的要求分配内存。
int x;
int *p;			   说明:堆(heap)是一种自由存储空间
p = &x;			   指向栈的内存区域,栈是编译是分配的区域,是静态的
p = new int;       分配一个未初始化的,指向堆的存储区域,运行时分配
p = new int(8);    分配一个初始化为8的整型变量空间
p = new int[n];    分配元素个数为n个的连续空间(数组)
delete p;          释放空间
delete []p;        释放连续空间
  • delete释放:释放p指向的内存,但不会删除指针p本身。例如,可以将p重新指向另一个新分配的内存块。一定要配对地使用new和delete,也不能太频繁使用,否则将发生内存泄漏(memory leak), 也就是说,被分配的内存再也无法使用了。而且同一个指针不能delete两次。
  • 如果使用new运算符在自由存储空间(或堆)上创建变量后,没有调用delete,则即使包含指针的内存(存放指针的内存)由于作用域规则和对象生命周期的原因而被释放,在自由存储空间上动态分配的变量或结构也将继续存在,因此将会无法访问自由存储空间中的结构,因为指向这些内存的指针无效。这将导致内存泄漏,被泄漏的内存将在程序的整个生命周期内都不可使用;相当于内存被分配出去,但无法收回。

8.结构体和共用体

/**结构体**/
struct Student{};声明结构体数据类型,Student是结构体类型名
struct Student{}stu;声明结构体类型Student的同时定义变量stu
typedef struct Student{int a;}Stu;在这里Student、Stu、struct Student都可以定义变量,
							  Stu只是struct Student的别名
typedef struct{int a;}Stu;其实直接这样也可以用Stu定义变量,那为什么不直接struct Stu呢?
反正我就直接struct Stu{};然后用Stu定义变量,加typedef只是为了两个作用相同的名字,我不需要(后面知道了,是因为C++可以省略关键字)
/**共用体**/
#include 
using namespace std;
union data			共用体变量的长度是它最长的成员长度,如main函数中变量d的长度是double型长度
{					变量的首地址都是同一个地址,共享同一块存储空间
    int i;
    double j;
    char s[2];
};
int main()
{
    data d;
    d.i = 0x424344;			数据高位存放在高地址,即0x44,0x43,0x42,0,0,0,0,0,对应ASCII码是DCB
    cout<<&(d)<<'\t'<<&(d.i)<<'\t'<<&(d.j)<<endl;
    cout<<d.s[0]<<'\t'<<d.s<<endl;	虽然s的长度是2,但是输出d.s的话结果是DCB,因为s和i首地址相同
    return 0;
}
输出结果是:
0x61fe1c   0x61fe1c   0x61fe1c
D   DCB
/**枚举**/
enum test{a,b=4,c,d=a-2};   枚举只能是有限个int型常量,如果没有初始化那就从0开始,后续元素累加
cout<< a <<'\t' << b << '\t' << c << '\t' << d;
运行结果为0 4 5 2
  • typedef:声明应该新类型名代替原有的类型名。typedef只可以声明类型不能定义变量,且只能对已有的类型名重新定义一个类型名,并不能创建一个新类型。
  • 一般用typedef声明一个新类型名代替结构体类型名,如上。
  • typedef与#define的主要区别在于后者是编译预处理指令,只做简单的字符替换,typedef是在编译时处理的。

9.类和对象

  • 过程式编程语言:程序=算法+数据;面对对象编程语言:程序=对象+消息。
class 类名
{
	private:成员数据和成员函数					//只能被本类中的成员函数访问
	protected:成员数据和成员函数					//只能被本类或者派生类中的成员函数访问
	public:成员数据和成员函数					//类内类外的函数都可以访问,是类对外部的接口
};
成员函数也可以定义在类体外,要加上作用域限制符“::”,格式:函数类型 类名::函数名(参数){函数体}
  • 相同类的不同对象其成员函数代码所占用的空间是共同的,在公共代码段中,所有类的成员数据和成员函数前都有隐含的this指针,指向当前的对象。
  • 类里面隐含的this指针用来存放该类对象的地址,this指针隐含在类中所有的成员前,可以显示调用不必人为添加,this在非静态成员函数之外的使用无效。
  • 创建对象时系统会自动隐式的调用一个函数:构造函数,该函数有以下特点:
    (1)函数名与类名相同;(2)没有函数类型说明;(3)没有返回值;
  • 如果不显示的定义构造函数,系统自动生成一个不做任何操作的默认构造函数:类名(){}。注意,如果在类体外定义构造函数,哪怕是无参无函数体(和自动生成的一样,类名::类名(){}),也需要在类体内先声明。当且仅当没有定义任何构造函数时,编译器才会提供默认构造函数。
  • 对于重载的构造函数而言,系统根据创建对象时提供的参数确定调用哪个构造函数。
  • 可以不提供参数而调用的构造函数只能有一个。
class A
{
	private:
		static int n;					//静态成员
	public:
		A();							//A行
		A(int x,int y=5,int z=5);		//B行,部分带默认参数
		A(int x,int y,int z);			//C行
}
int A::n;								//类体外初始化静态成员数据,默认初值为0
A a;									//正确,调用A行构造函数
A b(4);									//只能调用B行构造函数,C行的构造函数形参没有默认值
A b(4,5,6);								//B行C行构造函数都能调用,出现歧义,报错
A(1,2,3);								//临时对象
  • 析构函数,对象生存期结束时自动隐式调用,定义格式为:~类名(){函数体},先定义的对象后析构。
  • 如果将类的某个成员数据存储类型指定为静态类型,这个成员数据就被类的各个对象所共有,在内存中只占一份空间,静态数据成员必须在类体外初始化,默认的初值为0。静态成员即使还没有创建对象,也可以直接用类名引用。
  • 静态成员函数可以直接访问静态成员变量,不能直接访问普通成员变量,但可以通过参数传递的方式访问。普通成员函数可以访问普通成员变量,也可以访问静态成员变量。静态成员函数没有this指针。非静态数据成员为对象单独维护,但静态成员函数为共享函数,无法区分是哪个对象,因此不能直接访问普通变量成员,也没有this指针。
  • 如果要在外部函数中引用类的保护或私有成员,则需要用friend关键词将该外部函数声明为友元函数,假如在类体A中定义:friend class B,B是A的友元类,B可以自由引用A中所有成员。
  • 继承:在已经存在的类的基础上建立一个新的类,已经存在的类称为基类或者父类,新建立的类称为派生类或者子类。
  • 派生类:不仅有自己特有的成员,而且有父类的全部成员数据和成员函数。
单继承派生类定义格式如下:
class<派生类名>:[继承方式]<基类名>
{
	<派生类新增成员数据和函数的定义>
};
多继承派生类定义格式如下:
class<派生类名>:[继承方式1]<基类名1>,...,[继承方式n]<基类名n>
{
	<派生类新增成员数据和函数的定义>
};
单继承派生类构造函数定义格式如下:
派生类名(总参数列表):基类名(参数列表)
{派生类函数体}
多继承派生类构造函数定义格式如下:
派生类名(总参数列表):基类名1(参数列表1),...,基类名n(参数列表n)
{派生类函数体}
  • 派生类的三种继承方式:公有继承(仅在类外不可访问基类的私有成员)、保护继承(能调用基类的公有和保护,但仅能在在派生类中调用,类外不行)、私有继承(仅能调用基类的公有函数,且仅能在在派生类中调用,类外不行)。
  • 如果基类和派生类中出现了同名成员,那么在派生类中所定义的成员名具有支配地位,同名覆盖
  • 假设类B和类C是类A的派生,而类D又是类B和类C的派生,那么类D将会有两份完全相同的类A的副本(B,C里面的A的成员),若要使公共的基类在派生类中只有一份副本,则可以将这种基类声明为虚基类,即class B:public virtual A{};class C:public virtual A{};classD:public B,public C{};这样就只有一份副本。
  • virtual也可以用来定义虚函数。派生类对象的地址可以赋值给基类的指针,此时基类的指针只能引用派生类对象中从基类继承过来的那一部分成员,这时基类指针不管指向基类对象还是派生类对象,引用的都是从基类继承的成员。如果要让这个基类指针调用派生类的成员函数,那么就要将这个成员函数定义为虚函数。虚函数是指一个类中你希望重载的成员函数 ,当你用一个基类指针或引用指向一个继承类对象的时候,调用一个虚函数时, 实际调用的是继承类的版本。
class A{public:virtual double fun(){return 1;}};
class B:public A{public: int fun(){return 0;}};
B b;
A *p=&b;	//基类指针指向派生类对象
p->fun();	//调用的是类B的fun函数,如果基类A中不加virtual,调用的就是基类的fun函数
  • 在基类中没有实现部分的虚函数称为纯虚函数:virtual 函数类型 函数名(参数列表)=0;
  • 拥有纯虚函数的基类不能定义对象但可以定义指针或者引用,如果一个类中至少有一个纯虚函数,那么这个类被称为抽象类。抽象类必须用作派生其他类的基类而不能直接创建实例,但可以定义或者指针实现运行时的多态性。
  • C++的多态性。多态是指同样的消息被不同类型的对象接受时导致不同的行为。所谓消息是指对类的成员函数的调用,不同的行为是指不同的实现,也就调用不同的函数。换言之,多态指的就是用同样的接口访问功能不同的函数,从而实现“一个接口,多种方法”。
  • 面向对象的多态性可以分为4类:重载多态(例如函数重载),强制多态(强制类型转换),包含多态(类族中定义于不同类中的同名函数的多态的行为,主要是通过虚函数来实现),参数多态(函数模板,创建一个通用的函数支持多种不同形参,避免重载函数的函数体重复设计)。前面两种统称专用多态,后面两种统称通用多态。

10.C++与C的区别

C语言是面对过程编程,试图使问题满足语言的过程性,根据执行的操作来构思一个程序。C++则是面对对象编程(OOP),试图让语言满足问题的要求,可以轻松使用已有的类,这也意味着可以方便的重用和修改现有的代码。

  • C++使用了标准模板库STL,C没有。
  • C++中new和delete是对内存分配的运算符,取代了C中的库函数(需要头文件)malloc()和free()。
    变量是在编译时分配的有名称的内存,而指针只是为可以通过名称直接访问的内存提供了一个别名。指针真正的用武之地在于,在运行阶段分配未命名的内存以存储值。在这种情况下,只能通malloc()或者new。
  • 标准C++中的字符串类string取代了标准C函数库头文件中的字符数组处理函数( C中没有字符串类型),string对象可以赋值。
  • C++中用来做控制态输入输出的iostream类库替代了标准C中的stdio函数库。
  • C++中的try/catch/throw异常处理机制取代了标准C中的setjmp()和longjmp()函数。
  • 在C++中,允许有相同的函数名,不过它们的参数类型不能完全相同,这样这些函数就可以相互区别开来。而这在C语言中是不允许的。也就是C++可以重载, C语言不允许。
  • 在C语言中,输入输出是使用语句scanf()和printf()来实现的,而C++中是使用类来实现的。
  • 在C++中,除了值和指针之外,新增了引用&。C的&只是求地址运算符。引用型变量是其他变量的一个别名,名字不相同,其他都是相同的。
  • C++相对与C增加了一些关键字 ,如: bool、using、 dynamic_ cast、 namespace、wchar_t等等
  • 强制类型转换。C:(int) 变量名;存粹的C++则是:int (变量名),类似于调用函数。
  • 假如定义了一个结构体A,C++允许在声明结构变量时省略关键字struct,直接A a。而C不行,C得struct A a,如果C想要就得用typedef。
  • 对于指针的定义,传统上,C用int(空格)※p;表示※p为整型,C++用int※(空格)p;表示p为指针int的变量。int※ p1,p2;p1为指针,p2为整型变量。(用※是因为两个*的话CSDN编辑器不显示)。
  • 用m_开头表示类的成员变量,member的意思,如果是全局变量,则由g_开头,还有常量c_开头,静态变量s_开头

11.C++11对C++(98)做的改进

C++98是1998年制定的第一个标准,目的是为了适应可移植性,标准模板库STL随之诞生。
2011年产生了新的标准:C++11。增加的内容有:自动类型推导auto、列表初始化、变量类型推导、范围for循环、返回类型后置语法、final和override、=default和=delete、lambda表达式、std::move、std::array、std::forward_list、 std::unordered_map和std::unordered_set、 智能指针、右值引用&线程库。具体内容可参考:C++与C98对比.

  • C++11新增了类型long long和 unsigned long long,以支持64位(或更宽)的整型;新增了类型char16_t和char32_t,以支持16位和32位的字符表示;还新增了“原始”字符串。
  • C++11扩大了用大括号{}括起的列表(初始化列表)的适用范围,使其可用于所有内置类型和用户定义的类型(即类对象)。使用初始化列表时,可添加等号(=),也可不添加;创建对象时,也可使用大括号(而不是圆括号)括起的列表来调用构造函数;C++11提供了模板类initializer_list,这个类包含成员函数begin()和end()可将其用作构造函数的参数;
vector<double> payments {45.99,39.2319.95,89.01};

这将创建一个包含4个元素的容器,并使用列表中的4个值来初始化这些元素。这之所以可行,是因为容器类现在包含将initializer list作为参数的构造函数。

  • C++11提供了多种简化声明的功能,尤其在使用模板时。
  • 新增了关键词nullptr用于取代NULL,但是NULL也是合法的,因为C++兼容C。
  • 分配内存的方法。C++有3种,自动存储,静态存储(static),动态存储(new),C++11新增了线程存储。
  • 智能指针:如果在程序中使用new从堆(自由存储区)分配内存,等到不再需要时,应使用delete将其释放。C++引入了智能指针auto_ptr,以帮助自动完成这个过程,当智能指针过期时,其析构函数将使用delete来释放内存。随后的编程体验(尤其是使用STL时)表明,需要有更精致的机制。基于程序员的编程体验和BOOST库提供的解决方案,C++11摒弃了auto_ptr,并新增了三种智能指针: unique_ptr、shared_ptr 和 weak_ptr,所有新增的智能指针都能与STL容器和移动语义协同工作。要创建智能指针对象,必须包含头文件memory
double x = 0.5;
auto_ptr<double> p(new double(1.2)); p是一个double型指针,1.2是对象
auto_ptr<double> q;
q = p;		在这里p拥有对象1.2的所有权,只有一个指针可以拥有。编译器不警告,可能导致后面程序崩溃
			这个赋值将p的所有权转移给了q,析构了q后,p将指向一个空指针
q = (new double(1.2));  合法
若使用unique_ptr,编译器认为语句非法,避免了p不再指向有效数据的问题。故unique_ptr比auto_ptr更安全
shared_ptr <double> x(new double(1.2)); 
shared_ptr <double> y;  
y = x;  换成shared_ptr 就是合法的,因为shared_ptr 采用了引用计数
		x,y指向同一个对象,引用计数为2,引用计数为0 时才释放
auto_ptr<double> p(&x);     非法,因为引用没有用动态空间(x在栈里面),不能delete
  • 作用域内枚举:传统的C++枚举提供了一种创建名称常量的方式,但其类型检查相当低级。另外,枚举名的作用域为枚举定义所属的作用域,这意味着如果在同一个作用域内定义两个枚举,它们的枚举成员不能同名。最后,枚举可能不是可完全移植的,因为不同的实现可能选择不同的底层类型。为解决这些问题,C++11新增了一种枚举。这种枚举使用class或struct定义。
enum egg { Smal1,Medium,Large,Jumbo } ;
enum t_shirt { small,Medium,Large,xlarge } ;
无法通过编译,因为使用了同一个作用域,因此需要用classstruct限定作用域,如下
enum class egg { small,Medium,Large,Jumbo} ;
enum class t_shirt { small,Medium,Large,xlarge};
  • 对类的修改:拓展了explicit的用法;类内成员可以初始化。
  • 模板和STL的修改:基于范围的for循环;新的模板array;新的STL容器 forward_list、unordered_map、unordered_multimap、unordered _set和 unordered_multiset;>>不用空格分开。
  • 右值引用:传统的C++引用(现在称为左值引用)使得标识符关联到左值,C++11新增了右值引用(这在第8章讨论过),这是使用&&表示的。右值引用可关联到右值,即可出现在赋值表达式右边,但不能对其应用地址运算符的值。右值包括字面常量(C-风格字符串除外,它表示地址)、诸如x+y等表达式以及返回值的函数(条件是该函数返回的不是引用)。
int n = 10;
int & rn = n; 	左值引用,n为标识符,rn为左值
int x = 1, y = 2;
int && r2 = x + y;  右值引用,r2关联到了3(1+2),后面即使xy变了r2也不会变。
  • 移动语义。在一些对象的构造时可以获取到已有的资源(如内存)而不需要通过拷贝,申请新的内存,这样移动而非拷贝将会大幅度提升性能。例如有些右值即将消亡析构,这个时候我们用移动构造函数可以接管他们的资源。
  • Lambda函数:一种定义和应用函数的数学系统。这个系统让您能够使用匿名函数—即无需给函数命名。在C++11中,对于接受函数指针(函数名)或函数符(函数名带参数)的函数,可使用匿名函数定义(lambda)作为其参数。
[capture list] (params list) mutable exception-> return type { function body}
capture list:捕获外部(动态)变量列表,函数体可以使用中括号内的变量
params list:形参列表
mutable指示符:用来说用是否可以修改捕获的变量
exception:异常设定
return type:返回类型,仅当lambad表达式完全由一条返回语句组成时,自动类型推断才管用
function body:函数体
  • 省略其中的某些成分来声明“不完整”的Lambda表达式,常见的有:
[capture list] (params list) {function body}
例如:[ &count] (int x) {count += (x % 13 == 0); }
[capture list] {function body}
#include 
using namespace std;

int main()
{
    int a = 123;
    auto f = [&a] { cout << a << endl; }; 
    f(); // 输出:123
    a = 321;
    f();// 输出321,如果没有引用,就输出123
}

12.STL(标准模板库)

STL提供了一组表示容器、迭代器、函数对象和算法的模板。容器是一个与数组类似的单元,可以存储若干个值,如vector。STL容器是同质的,即存储的值的类型相同;算法是完成特定任务(如对数组进行排序或在链表中查找特定值)的处方,如find(),sort();迭代器能够用来遍历容器的对象,与能够遍历数组的指针类似,是广义指针;函数对象是类似于函数的对象,可以是类对象或函数指针(包括函数名,因为函数名被用作指针)。STL使得能够构造各种容器(包括数组、队列和链表)和执行各种操作(包括搜索、排序和随机排列)。
在C++标准中,STL被组织为下面的13个头文件:algorithm、deque、functional、iterator、vector、list、map、memory、numeric、queue、set、stack和utility,C++11还添加了其他东西(见上),这些类都包含着名称空间std中(为了避免命名空间污染,例如两个库都定义了list,但是不兼容),所以要using或者std::。标准模板库里面没有string,但是他在标准库里面,用来输入一个字符串类型。

  • vector,动态分配内存,存储在堆中。
  • 与数组一-样,array对象的长度也是固定的,也使用栈(静态内存分配),而不是自由存储区,但是array对象可以赋值,数组则需要一个一个复制。要创建array对象,需要包含头文件array。
  • vector和array都有成员函数。
  • 要访问顺序容器和关联容器中的元素,需要通过“迭代器(iterator)”进行。迭代器是一个变量,相当于容器和操纵容器的算法之间的中介。迭代器可以指向容器中的某个元素,通过迭代器就可以读写它指向的元素。从这一点上看,迭代器和指针类似。迭代器定义:容器类名::iterator 迭代器名;

你可能感兴趣的:(c++,c语言)