typeid(a).name();//获取a的数据类型
auto——推导,即可以自动退导出来变量的类型和大小
int a=0;
auto b=a;//b的数据类型为int
auto c=&a;//c的数据类型为int*
auto*d=&a;//d的数据类型为int*,但是在这之前d就已经被限定为必须为指针
auto的实际价值为简化代码,因为有些变量的数据类型非常长(因为要声明定义域)
auto缺点:1、不能做形参(因为无法推导参数数据类型);2、不能声明数组。
范围for——语法糖,运算过程为:自动取数组中的数据,分别赋值给e
arr[]={1,2,3,4,5,6,7,8,9}
for(auto e:arr)
{
cout << e <<" ";
}
cout<< endl;
//此代码回遍历arr数组并输出
在上述代码里,e因为用auto声明几乎可以接受任何类型的arr数据
声明函数为了效率不能传递整个数组,所以不能用范围for.
在c++中,NULL被识别为int数据类型,并被定义为0,所以c++中用nullptr来代替NULL。
类和对象
类可以理解成是一种用户自己构造的数据类型,用这种数据类型声明的变量被称为 “对象”
可以用struct来定义类,但是c++更喜欢用class
访问限定符有三种:1、public——公有;2、Proteced——保护;3、private——私有;一般认为保护和私有的限定范围一致。
类在存储时,为什么成员函数不在对象中,成员变量却在?
每个对象的成员变量不同,需要单独存储;成员函数一样,可以存放到共有分享区域(代码段),需要时再去调用。
为什么需要封装?
C语言中:1、数据和方法分离;2、数据访问控制室自由的、不受限制的;c++中:1、数据和方法封装到类里;2、可以控制访问方式(愿意被访问的公有,不愿意的私有)。
C语言的数据访问自由,需要用户自己维护,同时注意不能越界;c++因为限制了访问方式,相对更加安全。
构造函数——初始化函数,是一个特殊的函数(不能用对象去调用)
1、函数名与类名相同;
2、无返回值;
3、对象实例化是编译器自动调用;
4、构造函数可以重载;
5、如果类中没有自定义构造函数,则C++编译器会自动生成一个无参的默认构造函数;一旦有显示定义,编译器将不再生成默认构造函数。
默认构造函数的特点:1、对于内置类型(比如int/char/指针等),不做处理;2、对于自定义数据类型,会去调用他的默认构造函数(按照他的默认构造函数初始化方式来进行数据处理)(ps.c++11中针对内置类型成员不初始化的缺陷打了补丁,即:内置类型成员变量在类声明中可以给默认值——类似缺省)。
默认构造函数:因为无参,所以默认构造函数只有一个,没有函数重载;自定义的无参构造函数、全缺省构造函数、编译器默认生成的构造函数,都可以认为是默认构造函数。
析构函数——与构造函数功能相反,完成对对象的清理工作
1、函数名是在类名前加上符号 ~
2、无参数,无返回值——所以析构函数不能重载。
3、一个类只能有一个析构函数(没有自己定义的析构函数,系统会自动生成),
4、在对象生命周期结束时,系统自动调用析构函数进行数据清理
拷贝函数——只有单个形参,该形参一般是对本类类型对象的引用(且为了不拷贝错误,一般用const修饰)
1、拷贝构造是构造函数的一个重载形式
2、参数只有一个且必须是类类型函数的传引用调用,使用传值调用编译器直接报错,会无穷递归
3、若是没有显示定义,则编译器会自动生成拷贝函数。默认的拷贝函数拷贝时按内存存储字节顺序完成拷贝,这种叫做浅拷贝。
对于自定义类型,就需要自定义拷贝构造,然后调用,这种叫做深拷贝
拷贝函数的典型调用场景
1、使用已存在的对象创造新对象
2、函数参数类型为类类型对象
3、函数返回值为类类型对象
运算符重载——函数名为opreator后加上重载的运算符,比如++:operator++()
1、为了增强程序的可读性
2、为了让自定义类型可以运用运算符
注意:
1、不能通过连接其他符号来创造出一个新的操作符
2、重载操作符必须至少有一个类类型参数
3、不能对内置类型重载,比如+
4、作为类成员函数重载时,形参看起来比操作数目少1,是因为成员函数的第一个参数为隐藏的this
5、.* ::(域作用限定符) sizeof ?:(三路选择符) .(成员访问符) 这五个运算符不能重载
赋值运算符重载
1、参数类型:const T&,传递引用可以提高传参效率
2、返回值类型:T&,返回引用可以提高返回的效率(有返回值的目的是为了支持连续赋值,比如Date d1,d2,d3;然后可以直接复制d1=d2=d3)
3、检测是否自己给自己赋值
4、返回*this:要符合连续赋值的含义
赋值运算符只能重载成 类的成员函数,不能重载成 全局函数(原因:在类里,赋值运算符如果不显式定义,则编译器会自动生成默认的。此时如果全局还有一个赋值运算符重载,就会和类里的冲冲突,因此赋值运算符重载只能是类的成员函数)
默认生成的赋值运算符重载,是以值的方式逐字节拷贝。(内置类型成员变量是直接赋值的,自定义类型变量则调用对应类的赋值运算符重载进行赋值)
const成员
将const修饰的成员函数称之为const成员函数,const修饰的成员函数,实际上修饰的是成员函数里的this指针,表明在该成员函数中不能对任何类的成员进行修改
初始化列表——以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个成员变量后面跟一个放在小括号中的初始值或者表达式。
构造体函数中的语句只能将之称为赋初值,不能称为初始化,因为初始化只能初始化一次,而赋初值可以多次赋值。
1、每个成员变量只能在初始化列表里出现一次,(只能初始化一次)
2、类中包含以下成员,必须放在初始化列表位置初始化:1)引用成员变量;2)const成员变量; 3)没有默认构造函数的自定义类型成员
3、尽量使用初始化列表初始化,因为对于自定义类型成员变量,无论你是否使用初始化列表,一定会先使用初始化列表初始化。
4、成员变量在类中的声明顺序就是初始化顺序,和初始化列表中的顺序无关
explicit关键字——不允许其他类型的变量来通过隐式类型转换对自定义类型进行赋值
例如:double a=1;这条语句是把整型数1赋值给一个double类型的变量,需要先把1进行隐式类型转换变成一个无名的double类型数据,然后再拷贝给a,如果在自定义类型时前面加上了explicit,则会拒绝这种隐式类型转换,例如:Date A;则A=1这种方式就会编译失败。
static成员——声明为static的类成员称为类的静态成员,static修饰的成员变量称之为静态成员变量;static修饰的成员函数称之为静态成员函数。
1、静态成员变量为所有类对象共享,不属于某个单独的对象,放在静态区
2、静态成员变量必须在类外定义,定义时不添加static关键字,在类里只是声明(在类里,静态成员变量属于所有类对象,所以像单个对象里的构造函数里的缺省值的赋值方式不能采用,即不能在类里直接赋值(直接在建立时赋值也属于缺省值,例如:static count=0;))
3、类静态成员即可用 类名::静态成员 或者 对象.静态成员 来访问(因为需要确认是哪个类里的静态成员)
4、静态成员函数没有隐藏的this指针,不能访问任何非静态成员(同时访问静态成员函数也不需要含有this指针,只需指明是在哪个类即可)
5、静态成员也是类的成员,受public/protected/pribate访问限定符限制
友元
友元函数——在类外的函数需要访问类里的私有成员变量,需要在函数声明时在前面加friend关键字(在类里声明)
//例如
friend ostream& operator<<(ostream& _cout, const Date& d)
1、友元函数可以访问类的私有成员和保护成员,但不是类的函数
2、友元函数不能用const修饰(const修饰的是this指针,友元没有this指针)
3、友元函数可以在任何类定义的地方声明,不受类访问限定符的限制
4、一个函数可以是多个类的友元函数
5、友元函数的调用和普通函数相同
友元类——友元类的所有成员函数都是另一个类的友元函数
1、友元关系是单向的,不具有交换性
2、友元关系不能传递
3、友元关系不能继承
内部类——一个类在另一个类内部定义,则这个类被称为内部类
1、内部类是外部类的友元类,但外部类不是内部类的友元类(所以内部类可以访问外部类的public/protected/private数据,但是外部类不能访问内部类的protected/private数据)
2、内部类可以直接访问外部类的static成员,不需要外部类的对象/别名
3、外部类的大小和内部类没有关系,即sizeof(外部类)=外部类