C++
来源:互联网 发布:重庆江北离家纺城源码 编辑:程序博客网 时间:2024/06/07 08:55
C++重点知识
C++多了布尔值的算术类型.(整型)
//ftp://192.168.100.100
开源的库和OC基础.
内存管理,属性,代理.
String 抽象数据类型标准库
C++的引用 类的声明,多态,继承.(文件操作融合)
面向对象是思想不是语言.(计算机是面向过程的)
C->C++(比C多的)->OC(比C++多的)
C++关键在于代码复用性(关键)和安全性.
游戏的类库是C++写的,不是OC.
C++文件构成
头文件: .h
源文件: .cpp
编译:
G++ 用法和gcc一样.
Cout是对象(一般是屏幕)
Cout会自动检测格式.
引用:
名字不同但指向同一块地址.
<iostream> 文件流 不需要.h 和C区分(系统的库不用,自己写的要带)
Using namespace std;
Std是名字的空间,起名字是为了防止空间冲突.
Cout<<“hello”<<endl;
C++效率慢(g++)
Cout是智能输出的.(cout << I << 8)( int i=8);输出就是8,不是I;
命名空间是防止名字重复(像C语言中的重复包含,工程名_小组名_模块名)
Iostream相当于清华大学,std相当于物理系,cout就是要找的.
:: 域解析符
Std::cout<<...<<std::endl;
不加
就得每一个加std::
#if 0/1
...
#endif
条件编译
操作系统最好的还是用面向对象来写(目前主流的都是C)
虚拟存储+多任务=操作系统
声明字符串的时候要加 const.(不然会警告)
Const Int *p=”Hello”;
Cout会自动取值(表达式,函数);
Endl是回车;
引用变量是为了类似C的地址参数传递.(交换2个数)
再次注意函数值传递是副本传递.
引用是对现有变量取别名.
Int max=10;
Int &ref=max;
Ref是mac的引用.
Ref没有新的空间.
Ref和max共用同一块空间.
Ref没空间,这几个字母是给编译器的,编译器会维护符号表.也就是说ref是给人看的,实际上变成机器码就还是同一块空间.
C++里面函数传参的机制:
1:值传递
2:引用传递(C没有)
Pox和py是调用的时候动态绑定,不调用不绑定,绑定结束是函数调用结束.(引用传递,并没有重新开辟空间)
引用的缺点:
Swap并没有传递东西,仅仅是符号绑定,(其实也是传递,只是没有数据拷贝,类似空间跳跃)
Swap(m+4,n)这样就不行了,要变量,m+4最后是常量了.
C++的是四不像,既有面向对象也存在多继承,破坏封装性,语法很多,但大多用不到.
OC中的代理,属性,内存管理.
&是引用的意思.参数换成&x,&y也是一样的.引用只是声明才加上&符号.而且引用必须要初始化.
这就是错的,未初始化.
引用只能绑定一次.而且引用&只能是变量,常量不行.
类型也要匹配,double和int
&+变量.
指针是间接的,引用是直接的.
返回局部的引用和返回局部的变量地址一样,没用.
相当于a=9;(a不是局部变量)
类和对象:
类(高级结构体)
类是一种抽象个体,能形容,代表个体的特点.
具体的个体就是类定义的对象.
每个类的对象会有一些自己的特性(比如吃饭的方式不同,但都是人)
Class 类(struct结构体类似)
属性(数据类型)和行为方法(函数)抽象一个类.
在C语言中结构体不能声明函数,但C++可以.
调用一个方法是对对象发送消息.(就是函数调用,但不能这么说,面向对象的思想)
对象都是活的,2个对象可以发生关联.
结构体没有继承等.
Publie:公有方法.(确保公开的都是方法,不对对象产生损害)
Private:私有方法.
C++默认是私有的属性;
可以直接定义.不用加class.
上面是类(没空间),下面的a是对象(有空间); 类也要考虑对齐.
方法不占空间.(函数)
a和b是一样的大小,方法是公用的.(空间存储的都是数据,函数没空间)
a和b不同的是值,也就是属性.
这样写和类就没关系了,是C的风格.绝对不能这样写!
怎么写才能体现这个函数是这个类的呢?(域名的概念)
Std::(域名)
这样就是这个类的成员方法,不是普通函数了.(类的名称+2个冒号)
类的成员方法一般都是通过类的成员去引用.
其中隐含了this指针
Sut类型的指针,指针变量,指向谁不知道.this谁调用就指向谁!
1:类域
2:this默认就是,不用手动添加.
一般属性都是私有的,方法都是公有的.
封装,继承,多态.
属性设成公有的话,就会破坏封装性.
封装对外隐藏关键属性,细节.
面向过程:造汽车全车都得自己造,标准是自己定的.
但是更换轮胎什么的给不了别人,更换麻烦.就是说不能轻易改动.(蛋炒饭)
面向对象:可以捐眼角膜什么的.轮胎,发动机都不用自己造.
一天可以换一样.(鸡蛋盖饭)发动机坏了,只关注发动机就行了.
也就是说复用性.
类外(公有的,publie:)可以通过对象来引用的(有片面性).
非成员方法来访问的.上面的是成员方法,合法的.
(类内可以随意访问,不然没意义了)
类内(成员方法):成员方法要访问成员变量.
成员方法来访问的.(有类域)
这个就不合法了.类外不能访问受保护的成员.
总结:在成员方法内可以访问(带类域的)
类的声明,方法里可以直接写函数定义.
Show可以直接实现.
不写类域的函数就是变成普通函数了,不然this无法定位.
给a发送init消息.
This a调用就指向a
也就是说以后不用考虑是谁的了,直接赋值.
面向对象是围绕对象在操作(初始化,发送消息)
缺省参数(C++的,C没有)
Int b=10是缺省参数.
B没有接收到参数,就调用默认的缺省参数.
Add(,20)这种用法就是错的!
使用缺省参数必须要保证缺省参数在最右边,它的右边开始的参数都必须都是缺省值.(从右到左)
全缺省也可以.
Int add(int a,int b=10,int c=10)
缺省值不一定要用.
函数的重载(overload)(也是C++特有的)
函数名可以一样,但形参(个数,类型)等必须不同.
一般不用返回类型来区分函数的不同,用形参.
调用的就是第一个,形参是空的(void).
这是重定义了,就错了!!
Func()遇到缺省参数和void一起时编译器就纠结了.
上图,二义性了
函数重载大部分都是通过形参个数来判断的.(推荐)
函数的重载体现了多态的一种.
多态:调用的函数名一样,但产生的行为不一样.(通俗的讲)
封装:
结构体(C中体现的,但只是数据封装)
类:自带函数的超级结构体,处理数据的时候不知道用什么函数,类可以避免这种情况.在类中可以查看所有所需要的函数.
Aver求平均成绩的成员函数,成员内部直接拿来用,不用传参.没隔阂.
要访问成员必须要设一个变量(对象),不然没成员.
调用的形式,必须要通过结构体变量去访问成员.
封装性=成员方法+成员变量.
定义类就用class 用struct也可以但是会和C的结构体弄混.
类:Student (模具)
对象:s
实例化了对象.
S是Student的实例.
不创建对象的话,就没有空间.
类的作用就是创建对象.
S开辟的空间只有math和english的空间,没有方法的空间,方法空间是共用的,存在应用程序的某个空间.
Class和struct的区别在于权限.
Class要求设置权限,默认是private的.
Private(私有的),publie(公开的),protected(继承保护);
永远不要让类里面的数据成员对外暴露.
S在栈上,栈上的数据不可控,都是0或者随机数.
(所以对象要用构造函数初始化)
Stdlib.h
Arc4random() 真正的非算法随机数.
‘\0’不算字符串长度.
初始化就是初始化对象里面的数据成员.
一个对象永远只调用一次.
For里面定义的int i=0;作用域无法出for循环.
写.h的目的是为了给别人分享的时候,只写类的定义和方法的声明.不然代码泄露.
一个类有一个.h和一个.cpp
.h一般放函数的声明,类的声明,宏定义,类型的定义.
定义是要开空间的,声明不用.
全局变量在源文件中.
分开写的话.cpp中要加域解析符.
在函数名前面加
头文件不参与编译(.cpp->.o),只是预处理.
链接的时候编译器会找到函数真正的地址填充(这就是为什么汇编会有很多MOV,都是地址)
链接阶段会出现的错误只有找不到地址.
1:创建一个对象(开空间)
2:初始化(必须的,否则很危险)
3:使用
4:所占用的资源要清理(外部的资源).
5:终止.
(适用所有的语言)
构造函数与析构函数:
构造函数
在创建对象的时候必须要经过初始化的函数.(强制)
构造函数是成员函数,但有区别:
1:没有返回类型(void也不能有!),形参可以随意.
2:函数名要和类名一样.
作用:
用来初始对象.
必须要没viod,无任何返回值
所有的构造函数,程序员本身不会通过对象去调用,编译器自己调用的.
创建对象的时候要传给构造函数的参数,用来初始化.
所有的对象有且必须要调用一次构造函数.
再次强调:构造函数是成员函数,没有返回值,和类名一致,不主动用对象调用构造函数,构造函数是给编译器来用的.构造函数我们一般都会overload(重载).
编译器会调用构造函数,去所有构造函数里找,看哪一个参数类型匹配,找不到会报错.
因为我提供的构造函数的确是有一个参数,但这个参数类型转换是无效的,所以编译无效.
要是自己写了一个构造函数的话,系统就会不使用默认的构造函数.
创建任何对象的时候,要是程序员没写构造函数的话,编译器会使用默认的构造函数,这个构造函数没有参数,什么也不做.
2个构造函数,一个带参,一个不带.
这样就是构造函数重载了.
误区:
构造函数不是用来创建对象的,而是用来初始化的.目前所有的对象都在栈上,(main申请的,new出来的就是堆(动态开辟的)上).
编译器也是通过对象去调用的.(也是用 . 调用的,编译器自动识别)
Free()只能标记为malloc得到的空间地址,不是清除,只是标记可用.
Realloc()有2个参数,第一个为malloc得到的空间地址,第二个为增扩的空间(SIZE+INCREMENTSIZE)*sizeof(类型)
如果原来的空间后面不够大的话,指针要重新接受新的空间,它会自动复制原先的空间里的内容,也就是说会重新开辟新的空间(在原来空间后的空间不够大的话).
以后用类都得用成员函数(思路).
每一次malloc都得free.
Free()之后指针的值并没有变.
都是函数,会有失败的几率,要检测.
New 是关键字,
Delete也是关键字,不是函数,不会失败,不需要检测.
Ptr2指针在栈上,ptr指向的是堆.也就是说,ptr2这个指针还在,但是是野指针了.(指针可以重复使用,释放的只是空间,没有改变指针这个量什么)
开辟5个int;
Delete[] ptr2; []中不用加数字,new会自动记录个数.
Delete后加不加[]看new的时候加不加,统一的.
只要没有被左值,指针就存在,生命周期是在main中.
栈上的对象消失了但是指向的堆上的空间还在,会内存泄露.
对象消失了,但是占用的资源还在,需要手动清理.
析构参数必须为void;
重载构造函数的目的是多元化.
析构函数,也是强制的.
析构就不需要重载.
析构函数的参数必须为void.
析构函数不可以重载.
不主动通过对象去调用析构函数.(系统调用)
系统在对象生命周期即将结束之前调用.也就是说对象最后调用的函数一定是析构.
析构函数不是自杀用的.(是用来清理外部资源的)
析构函数清理的一定是对象外部的资源.对象生命周期结束时内部资源也就随之消失了.
清理的是某个成员占用的外部的某些资源(文件,网络,数据库等).
编译器默认也提供一个析构函数,也是啥也不干.
析构是对象最后一个调用的函数了.
创建一个堆上的对象.
需要一个指针指向堆上的对象.
Ptr在栈上,堆上的对象.(要用指针来指向使用)
传进来的不是字符串,而是地址.也就是说一共有2个Hello,Apple.ptr开辟了空间后,就拷贝了一份.
再次强调对象本身的资源不需要清理.
Main栈里的ptr指向堆上MyString对象,对象指向堆上的字符串.
Delete ptr;
要清理ptr指向的对象,该对象快结束时会触发析构函数,析构函数里的代码是清理_string指向字符串,所以正好全部清理.
OC里的对象全在堆上.
构造和析构都是一种触发机制,不主动调用.
堆上的对象.
栈上的对象.
Bool类型:true-false.
拷贝构造函数
拷贝构造函数,参数是引用的对象.
拷贝完后,2个对象内容一样.
如果没有定义拷贝构造函数,编译器会自动生成一个隐含的拷贝构造函数,也会自动完成数据拷贝.(两块内存拷贝,和权限没关系)
如果用户定义了构造函数但没定义拷贝构造函数,编译器还是会自动生成拷贝构造函数.
也就是说编译器在生成构造函数的时候,有2种一起生成的,构造函数和拷贝构造函数.
编译器自动的拷贝构造函数的缺点:
_string1 和_string2的指向都一样了,都指向hello,world了,
如果其中有一个生命周期结束,另一个就悲催了.
所以要复写.
类的内部对象可以调用任意数据,不管私有的还是公有的.(不过不建议在公有成员方法里调用私有成员)(最多最多在拷贝构造函数里用,但也不建议,破换了封装性)
C++里面不允许成员函数名称和数据成员名称一样,但OC可以.
继承才是面向对象的核心.
_t的变量都是基本typedef出来的.
链表排序:
构造:
数据是ID,头结点不放数据,不管ID,只管next指针指向NULL;
析构:
用另一个指针指向删除节点的后面,然后遍历删除
插入:
头和尾插法;
排序:
(扑克牌)
不用开辟另一个链表,在本身上排序.
重点回顾:
1:引用:没有开辟空间,传参,拷贝构造函数重点使用
2:缺省参数:从右往左缺省.重载
3:函数重载:搭配构造函数.函数名相投,参数有区别.不能有二义性.
4:封装性:数据+方法.对应的类找对应的方法.
5:构造,析构函数:类的成员函数,每个类都有,不能主动去调.但编译器还是通过对象去调的.析构参数不能重载,参数为void.
继承性:
已经存在的类表明已经基本没有大问题,不要在原先基础上修改,破坏了封装性.
用继承来扩充.
继承是有权限的.
(OC中的成员函数都是publie的,只有数据成员才有权限控制)
父类-子类
基类-派生类
继承下来的都是模具,不是具体的对象.
变化的都只是模具.
继承可以在没有源代码的基础上(只有机器码)继承.
子类化(继承)
子类增加新的功能:(隐私问题)
父类的私有成员子类里不能直接调用.
继承的原则是:不能操作父类的成员.(破坏封装性),要调用父类的函数.
添加的代码只要管理好自己的成员就行了,如果要操作父类的成员就交给父类的函数.
复写的现象:
父类和子类有同名的函数情况:(两个show()函数)
这种情况叫做函数的复写
父类的show()被复写了,调用的是子类自己的show();
函数想要调用父类的show()要加域解析符(父类名::show());
对象调用的话也是一样(对象名.父类名::show());
复写的目的:
父类提供的函数对子类来说过时了,功能不适用子类.
在没有源代码的情况下复写.
(显式)
什么时候用继承什么时候用指针:(包含和继承)
Has a
Is a
两种逻辑关系.
两个类是否满足is a的关系就是继承.(黄种人是人,所以是继承)
Has a(电脑包含CPU).
引用的一种方法就是创建一个对象引用另一个对象,在调用那个对象的链表头结点.
Set(写)方法和get(读)方法(OC中get省略,直接是函数名).
在继承里的构造和析构函数:
构造:
子类继承了父类的成员.创建对象的时候要把父类的成员变量也初始化了.(构造有N个参数)子类自己的变量可以自己初始化,父类的要调用父类的成员方法.
代码如下:(Child 是子类,Parent是父类)
Child(int age,int id):Parent(age)//函数原型:函数调用
{
...
}
Child虽然调用2个参数,但只用了id,age是给父类用的;
(孙子类的构造层层调用,类似递归)
体现了封装性.
C++不存在不调用构造函数的对象.
初始化子类的对象时候先初始化父类.
析构:
子类继承了父类,子类只清理自己的外部资源.父类的交给父类自己的析构函数.
gitHub网站(代码)
Stackoverflow(全球问答专业网站)
初始化是会失败的.(打开文件或数据库)
父类初始化成功在是子类
但是析构不能重载,基本不会失败.析构是和构造反着来.是先清理子类的,再是父类的.
析构函数和构造函数是反得.
问题:
如果不手动写后面的传参,父类的age就是是默认的void.
注意字符串要+1;(\0)
构造先有父类才有子类;
析构要清除子类才是父类.
This指针:
This只能在成员内部使用,它指向对象,但不是固定不变的.
人看到的代码和编译器看到的代码不一样.
Return _a+_b+_c;
// return this->_a+this->_b+this->_c;
编译器会把地址传给this,谁调用就指向谁,不能修改this.
This是可以直接访问成员,很特殊的指针.
编译器自动绑定的,只能在成员方法中使用,不能在外面使用.
一般不主动调用,当形参把成员变量的作用域屏蔽的使用,要手动调用.
谁触发了函数,this就绑定谁.
This是一种动态的概念.
每一个成员方法里都有一个隐藏的this指针.
多态:
函数的重载(OC中没有)
父类:Parent p(50);//年纪
子类:Child c(30,2)//年纪和ID
C对象不能赋值给p对象,(c被截断,赋值可以赋值但是只有父类的原来东西被还回去了.注意的是,p反而不能给c赋值,因为c空间太大,p装不满(没有填充规则(补0是没意义的),普通数据没有填充规则),剩下的部分谁都给不了.)
也就是说:
子类可以赋给父类(有意义),父类不能赋值给子类.
这和普通数据转换不同,多了我可以就取需要的,少了就没法扩充.
父类的指针除了指向自己外,也可以指向子类(子类的起始地址叫给父类指针,有一部分截断,但不用就行)
Parent *ptr;//父类指针
Ptr=&c;//c是子类
Ptr->showChild();//报错!因为ptr实际被编译器识别成Parent *类,showChild()函数是子类的,指针找不到.
Ptr->show();//注意:这里调用的是父类的show(),因为ptr指向的是父类的类型空间.
也就是说,对象赋值是空间拷贝.
((Child *)ptr)->show();//这样就是调用子类的了.
(强转只具有临时性)
首先判断是否是普通还是虚函数(virtual修饰).(编译器决定)
看函数之前的指针,这个指针指向的是什么类型的空间.
总之,注意指针指向的空间类型,来判断是父类还是子类的指针.
指针是什么类型就是什么类型的对象去调.
以上都是编译器确定,通过对象.
多态:
运行时确定的.
虚函数
运行时的多态性
Virtual修饰后的函数就是虚函数,不是普通函数了.
如果ptr->show();是虚函数的话,编译器做不了主了,只检测语法,运行时才能判断.
指针调用函数要先确定函数是普通的还是虚函数.普通就是编译器决定的类型.虚函数是运行时决定.
虚函数要有子类的复写才能实现,复写后调用的就是子类的函数,父类的被virtual屏蔽了.虚函数都不会影响到对象调用(对象调用都是编译器决定的),只会影响到指针这类调用!!!虚函数是可以继承的,可以不加了.
虚函数会把真正的对象找出来,也就是说解决了空间大小赋值的问题,是真正的地址(人的角度).
OC中的函数全是运行时判断调用的.(虚函数是可以继承的,子类不用加了.)
总结:编译时决定的是编译器角度,运行时决定的是人的角度.
最后一个知识点:
Static修饰的成员方法:
(OC中的+与-号有关系)
Static可以被对象调用.
没有对象的前提下也可以调用.
用static修饰的函数可以用类名去调用,同时也可以被对象调用.
形式如下:
类名::hello()
但是不能访问成员变量,应为要有对象才会有空间!
OC中-号只能用类的对象,+号只能用类名.