wordpress主题不支持php7,无忧网站优化,网络热词英语,哈尔滨seo排名优化公司价格一.类的定义
1.1类定义的格式 图中class为关键字#xff0c;Stack为类的名字#xff0c;用{}框住类的主体#xff0c;类定义完后#xff1b;不能省略。
为了区分成员变量#xff0c;一般习惯在成员变量前面或后面加一个特殊标识#xff0c;_或者m_ 1.2访问限定符
c采用…一.类的定义
1.1类定义的格式 图中class为关键字Stack为类的名字用{}框住类的主体类定义完后不能省略。
为了区分成员变量一般习惯在成员变量前面或后面加一个特殊标识_或者m_ 1.2访问限定符
c采用一种封装的方式用类和对象的属性与方法结合在一起让对象更加完善通过访问权限选择性的将其接口提供给外部使用。
public修饰的成员在类外可以直接访问使用private和protected修饰的成员在类外不能直接使用。 1.3类域
类定义了一个新的作用域类的所有成员都在作用域中在类体外定义成员时要用操作符来指明成员。
二.实例化
类就像一个模型图纸一样实例化之后可以实现许多的功能但是在实例化之前他是没有任何功能的并且也不会占用实际的空间只有当实例化出对象后才能进行物理存储。 2.1对象大小
类实例化出的没一个成员都有独立的数据空间那么类中的函数是否能进行储存呢首先函数在编译之后是一段指令对象无法进行存储。所以函数会以指针地址的方式通过汇编指令call地址找到函数的地址进行使用。
打个比方类里面的成员变量每实例化一份就会开辟一份的空间而类成员函数则会以一份公共的地址放在一个代码公共区内。 三.this指针
在一个类中若有多份成员函数c是如何找到访问对象的呢
这里存在一个this指针this指针在编译器编译后会默认出现在第一个位置增加一个当前类型的指针类的成员访问成员变量本质都是通过this指针来使用的。c规定不能在形参和实参位置写this指针编译器编译时会处理但可以在函数体内使用this指针。
我们来看一串代码 它的编译结果是什么
答案是正确通过
p为空指针去访问类中的print函数虽然是空指针但是我们并没有拿去访问实体所以不会出错。 若是这样去访问实体a所以就会运行崩溃
3.2this指针存在哪个区域内 常量区是用来存放一些字符类字符串类的全局变量。堆区是用来存放malloc手动开辟的空间变量的存储区域。静态区内存放的是static修饰的变量以及全局变量。栈区是存放函数的形参实参的空间以及函数内的变量。
很明显这里的this指针应当存放在栈区。
四.类的默认成员函数
4.1概念
默认成员函数就是用户没有显示实现编译器会自动生成的成员函数称为默认成员函数。一个类我们不写的情况下会默认生成6个成员函数。分别为构造函数负责完成初始化工作析构函数负责完成清理工作拷贝构造是使用同类对象初始化创建对象赋值重载只要是把一个对象赋值给另一个对象取地址重载主要是将普通对象和const对象取地址。
4.2构造函数
构造函数是特殊的成员函数虽然他的名字叫构造但并不是以开空间创建对象为目的而是对象实例化时初始化对象。构造函数的本质是为了替代以前stack类中写Init的功能构造函数自动调用完美解决了此类问题。
4.2.2构造函数的特点以及实现
函数名与类名相同在类中单独初始化且名字为类名。
无返回值不需要给任何的类型c规定如此。
对象实例化时会自动调用函数。如下图d1会自动初始化三个值为1。 构造函数可以重载我们可以根据自身需要设置变量的值以及是否需要缺省参数。
若我们不写编译器会默认初始化生成默认构造函数若成员变量为基本类型char int double 指针等那么编译器就会报错。若成员变量为自定义类型先前已经在类中定义过的类型就可以正常进行初始化。
无参构造函数全缺省构造函数默认生成的构造函数都叫做默认构造函数不传实参就可以调用这三个函数有且只能存在一个不能同时存在。
4.2.3构造函数实现法2
除了用函数体内赋值的方法构造函数初始化还有一种方法就是初始化列表初始化列表从一个冒号开始用逗号分隔数据成员列表每个成员变量后面跟一个放在括号中的初始值或表达式。在和中只能初始化一次 当面对引用成员const成员变量没有默认构造的类类型变量必须在下进行定义。因为这些变量已经在主函数中定义过了无法在构造函数中重新定义所以只需要在下定义即可使用。
4.2.4成员变量初始化逻辑
显示在初始化列表的成员变量就按这个值进行初始化。未显示在初始化列表的成员如果有缺省值或者就按缺省值初始化若没有缺省值内置类型成员可能会初始化为随机值自定义类型成员若有对应的构造函数那么就可以调用若没有则会报错。引用const没有默认构造函数的成员必须在初始化列表进行初始化。成员变量会根据声明的顺序进行初始化所以尽量按照声明顺序进行定义。
4.3析构函数
析构函数与构造函数相反析构函数是完成对对象本身的销毁例如我们需要在函数内申请空间当栈帧销毁时要先做清理资源使得资源释放。
4.3.2析构函数的特点
要在析构函数前加上~。析构函数要与类同名 参数无返回值且一个类只能有一个析构函数若未显示定义系统会自动生成。
对象生命周期结束时系统会自动调用析构函数。
跟构造函数类似若我们不写编译器自动生成的析构函数对内置类型不做处理自定类型成员则调用他们的析构函数。
若类中没有资源申请析构函数可以不写直接使用编译器生成的默认析构函数。但是当有资源申请时一定要自己写析构函数否则就会造成资源泄露。
4.3.3构造函数和析构函数的顺序
构造函数遵循先全局变量再局部变量然后是局部静态变量。全局变量指在main主函数外的的变量静态变量指static修饰的变量而局部变量就指主函数内的变量。遵循从上到下的顺序进行构造。
析构时遵循先局部再全局的思路先析构局部的简单变量再析构静态变量最后是全局变量
4.4拷贝构造函数
类似于键盘的复制粘贴用于复制类类型的函数。拷贝构造需要接受拷贝的必须是未存在的对象而拷贝的必须是存在的对象。
4.4.1拷贝构造函数特点
1.拷贝构造函数是构造函数的一个重载。将函数内部的值copy到拷贝构造函数中。 2.拷贝构造函数的参数只有一个且必须是类类型对象的引用可以有其他参数但必须是缺省参数使用传值方式编译器会报错因为会在语言逻辑上引发无穷递归调用。
所以在拷贝时必须使用引用直接指向原先的类。 通过加上Const来防止拷贝的值被轻易修改。
若没有使用符号每当我们需要拷贝时就要重新开辟一块空间调用一次构造函数此时通过拷贝函数将其copy进来但是当我们调用拷贝函数时又会重新开辟一块新的空间反复上述步骤。
3.若未显示定义拷贝构造那么系统会调用默认的拷贝构造。这里的拷贝构造是浅拷贝若变量为intchar这些类型的变量拷贝时会单独开辟一块空间储存他们的值。若变量为malloc出的一块空间或者指针文件类的指向型浅拷贝后还会指向原先的地址。若是这样一旦函数析构或者变量改变就会影响到拷贝的值。所以我们需要在拷贝函数上重新单独为其开辟一块新的空间储存。 五.赋值运算符重载
5.1运算符重载
当运算符被用于类类型的对象时编译器无法直接识别运算符定义所以我们需要重新进行运算符定义。我们用operator和后面要定义的运算符共同构成他相当于一个函数拥有返回类型和参数列表以及函数体。重载的运算符函数的参数个数要与运算对象个数一样多并且按照顺序进行排列。若重载运算函数是成员函数则它的第一个运算对象默认传给隐形的this指针所以运算符重载作为成员函数后参数少一个。运算符重载后其优先级和结合性与对应的内置类型运算符保持一致。当然也并不是所有的运算符都能重载像.* sizeof .不能进行重载 如图其中的.*是调用成员函数指针的符号。
一个类需要重载哪些运算符是看哪些运算符重载之后有意义比如日期减日期可以得到天数但日期加日期就无意义。
重载运算前置和后置时重载名都是无法区分所以c规定后置重载时增加一个int形参跟前置构成函数重载用于区分。 5.2赋值运算符重载
赋值运算符重载是一个默认成员函数用于完成两个已经存在的对象直接拷贝赋值这里需要跟拷贝构造进行区分拷贝构造是一个对象拷贝初始化给另一个要创建的对象。
5.2.1特点
1.赋值运算符应当写成const当前类类型的引用否则传参会有拷贝降低效率。
2.没有显示实现时编译器会自动生成一个默认赋值运算符重载默认赋值运算符重载为浅拷贝对于动态开辟空间的变量会同时使用同一块空间会导致错误发生。也就是说当成员内有多个对象享用同一块空间时需要自行手动实现。
3.手动实现时需要先释放当前对象已有的资源以避免内存泄露对于intdouble这类型的直接进行赋值对于指针这类的需要重新开辟空间进行深拷贝。最后返回当前对象的引用。
4.注意当出现aa的情况直接释放资源会导致错误所以开头先判断两者地址是否重叠再进行拷贝。
代码参考 5.3日期实现
5.3.1 Date.h 其中对于自身有改变的函数需要传递引用值对于this指针不存在改变的要在函数后添加const修饰。
5.3.2 Date.cpp 对于代码相似的函数实现尽可能的使用函数调用这样可以减少代码量并且使得程序更加可观。
ostream和istream是c库中定义的一个流输入输出重载的函数名。我们在类中定义友元函数因为函数调用时是按照严格的左右位置调用的。
六.类型转换
当我们使用来赋值类类型时会存在一个隐式的类型转换开辟一块空间将其进行拷贝函数赋值。 若是遇到引用符号时因为临时对象具有常性所以在传值时左值必须使用const将变量转变为常性。若等号传参时有多个变量则用来包含值若是有多组则以这样的形式传值。若是自定义类型转换则需要相应的构造来支持。
七.static成员
7.1static成员特点
用static修饰的成员变量称之为静态成员变量静态成员变量一定要在类的外部进行初始化。静态成员变量为所有类对象所共享不属于某个具体的对象不存在对象中存放在静态区。用静态成员修饰的函数没有this指针意味着函数内部不能访问非静态的成员只能访问静态成员。而非静态的成员函数可以访问任意的静态成员变量和静态成员函数只需要进行突破类域。突破类域有两种方法类名静态成员或者对象.静态成员。静态成员不走初始化列表因为他不属于某个对象也不能进行声明位置的初始化但需要在声明处进行声明。
八.内部类
内部类就是在一个类里面再封装一个类当这两个类之间有着紧密联系或者a类是为b类专门实现的时候可以使用内部类默认是外部类的友元类。内部类是一个独立的类与定义在全局相比它只受外部类类域限制和访问限定符限制所以外部类的对象中不包含内部类。
九.匿名对象
用类型实参定义出来的对象叫做匿名对象匿名对象的生命周期只在当前一行。 若我们想延长匿名对象的生命周期可以使用const a b A的方法来延长生命。
匿名对象的用法主要服务于当函数调用时给类类型给缺省值无法直接给出时。 十.对象拷贝时的编译器优化
编译器为了提高程序的运行效率在不影响正确性的情况下会尽可能减少一些传参过程中可以省略的拷贝。 在不优化的版本下f2中的aa传值过程中会预先开辟一块空间接收return aa的值再将值进行一次拷贝构造给回aa2中。在vs2019的debug版本下则会省略掉中间值的拷贝构造而是直接将return的返回值拷贝构造给aa2。而在release版本下则会更加的激进return返回的aa直接就是aa2的引用返回值直接省略了空间的开辟。