C++基础知识笔记
-
申请/释放空间 new deletenew后面字节类型要匹配int *p=new int() ;课括号内可初始化delete p;数组int *p=new int[数组元素个数5];memset(空间p,数值0,空间大小5*4); 对连续的空间进行初始化delete[] p;
-
空间申请与释放 C++区别C:可以触发构造和析构int a=12;int *p=&a;声明变量的时候 *p的p是指针变量,取地址符&指向a的地址*p是地址操作符cout<<*p<<endl; 输出的是a的值
-
引用就是给变量起别名int a=12;int &c=a; 此时&是引用符,声明变量a是c的一个别名。此时a,c只是某个空间的两个名字。c只能作为a的别名,不能再引用其他,但是a,c仍可以起其他别名,也可以被赋值。指针*p 假设p=a,则p取a那个空间的地址。声明的时候必须初始化
-
引用所起别名的空间同时也包含地址。const数组的引用int 数组名arr[数组元素个数12];int (引用&p)[12]=arr; ()是为了将引用的优先级提前,不然[]会优先,计算机会理解成一个数组,出错。多维数组同样如此int arr2[维一元素量2][维二元素量3];int (&p)[2][3]=arr2;指针的引用int b=12;int *point=&b;int* &p3=point; &p3可加可不加()
-
引用做参数(本笔记为理解后改进型笔记)void fun(int &a){a=13;}void fun1(int d){d=14;}int main(){int b=12;fun(b);cout<<b<<endl;int c=15;fun1(c);cout<<c<<endl;}结果为13 15b为13的原因是:普通变量b被声明时有一个空间b,b的值代入函数fun时,引用变量&a的值被被改变为12,同时a成了b空间的别名,然后函数将a改为13,因为此时a成了b空间的别名,改a就是改b,所以b是13了。c为15的原因是:普通变量c和普通变量d被声明时都各有一个空间,c为15。当变量c代入函数fun1时,改变的是d空间的值,虽然函数里又将d空间的值变成14,但c空间的值仍为15。说白了c d为独立空间,没有被关联起来。
-
引用变量名没有独立空间,而普通变量名有,操作时药要注意实际操作的空间。C#里的指针传递,其实是传递地址空间的值 。
-
指针和引用的区别|:1.引用需要初始化,指针不用。2.引用无法引用其他空间,指针可以。3.引用占存储空间,指针占。4.引用效率高,是对空间的直接操作,指针为间接操作。5.引用更安全,指针可以偏移(可以自加减)。6.指针更为灵活,c、c++通用,引用只有c++有。
-
缺省值就是默认值。指定声明时,同一格式的要从左向右连续指定,不能随便写。对:int a,cha c='a',float f=123.123;错:int a=12,char c,float f=123.123;函数调用时,函数里的参数有默认值,在调用时可以不用传递实参,传递则覆盖默认值。如果没有默认值,必须传递实参。如果多个函数声明,则同一形参可以多次被声明,但该形参的默认值只能在函数中指定一次。void fun(int a=12);void fun1(int a);
-
函数重载:函数名字相同,参数列表不同(参数类型或列表不同等)。可以用于函数使用,智能识别,减少程序复杂度。void fun(int a){}void fun(int a,int b){}void fun(char c){}以上为函数重载,但函数不能完全一样,不然就是重定义了。当有函数重载时,调用函数自动识别数据类型自动调用:int main(){fun(a);fun(12,13);}
-
函数重载注意点:1.调用浮点函数(float)时,参数值数字末尾要加f。2.函数作用域。只有在主函数共同包含的情况下,才能算函数重载。如果在主函数包含之外要同样调用,需要在公共域函数声明一下。void fun(int a){} //1void fun(int a,float b){} //2int main(){}void fun(char c){} //31包2,1、2函数为重载函数,3不是。如果需要包含3,则在main上面声明一下就行。3.返回值不作为函数重载的条件。重载函数中不能有返回值。(return)*这个不太明白,希望同学们指教。4.默认参数和重载结合使用,可能会造成调用不明确。函数声明时,如果函数的某个参数已经初始化定义值了,此时若调用函数时,会忽略已初始化的参数,在传递参数时自动识别可能会调用不明确。void show(char c,char b='a'){}void show(char c){}int main(){show('a');}此时就不明确。但编译显示正确。
-
防止头文件重复 MSDN,可查库和函数用法等。一次性定义:#ifdef aaa#define aaa#endifC++中可以用#pragma once#pragma once,直接表明以下内容只编译一次。此方式不一定所有编译器都支持。
-
private :纯私有 类内使用protected:类内 只能被子类使用 成员类外不可见,对子类可见(父类的受保护成员可以被子类使用)public: 公有都可以使用
-
声明友元函数: friend void fun();声明友元类: friend class teacher;
- 友元函数和友元类可以访问类内public,private,protected成员
- 非静态的字段可以不初始化,也可以在类内直接初始化,不可在类外初始化
- 静态的字段(static)只有const、static、int同时存在才可以在类内初始化
- 静态的字段必须初始化,除了const\static\int同时存在,其余的静态字段都必须在类外初始化
- 成员初始化顺序至于声明顺序有关,与初始化列表书写无关,c是个局部变量,构造函数外a引用c返回的是一个无意义值,函数内可以使用
-
双冒号(::)用法(1)表示“域操作符”例:声明了一个类A,类A里声明了一个成员函数void f(),但没有在类的声明里给出f的定义,那么在类外定义f时,就要写成void A::f(),表示这个f()函数是类A的成员函数。(2)直接用在全局函数前,表示是全局函数例:在VC里,你可以在调用API 函数里,在API函数名前加::(3)表示引用成员函数及变量,作用域成员运算符例:System::Math::Sqrt() 相当于System.Math.Sqrt()
- memset(&a[0],0,16)从啊【0】开始初始化为0一共16个字节的元素
-
指针对象 空间在堆区,不会自动释放。主动释放:delete stu;临时对象:作用域只在当前一行语句CStu(); (类名加上括号)类型加括号就是临时变量析构函数: 清理作用
-
maloc free和new,delete的区别new, delete可以触发构造和析构malloc free不会触发构造和析构
-
创建对象时才会有this指针一个对象一个this指针this指针不是成员,是类成员函数的隐含参数作用域是在类的内部,且在函数内部起作用
- 指针变量用浅拷贝进行结构复制后,再调用析构函数,会导致空间重复释放,造成程序崩溃。
-
拷贝构造形式 T a;T b=a;T b(a);T b=T(a);T *b=new T (a);四种形式。赋值是不会调用拷贝构造函数;如T b;T d;b=d;这是不可行的。调用时才会分配空间函数调用,还有就是返回值时都用到拷贝构造构造临时对象时会用到拷贝构造函数,但是有的编译器不会显示这个过程
- 递归函数不能作为内联函数,函数体过大不宜作为内联函数
- 内联函数在程序执行到此处时将函数内部代码全部复制到函数调用的地方,和常规函数相比少了跳转的过程。跳转过程耗费时间,要寻址。内联函数执行速度略快。
- 宏只是单纯的替换,宏只能传递 不能将参数计算后再传递 但是内联函数可以先计算再将结果传递
- 运算符重载不允许重新实现系统原有的运算符功能。
- 重载运算符,定义时要传引用。
- 内类重置运算符,必须左边是类型。
- ist.fail()判断输入失败的,如果失败为1,输入数据匹配判断用
- 重载等号必须返回引用(*this),其余的返回对象时,构建一个新的对象来返回
- 多态需要new空间,父类指针指向子类时,调用该子类的函数,一个指针会因为指向有不同的结果
- 虚函数即子类重写父类同名函数
- 虚表,对象空间的最开始四字节内容,就是虚表(虚函数列表)的地址,叫虚指针
- (int*)*(int*)p+0、(int*)*(int*)p+1、(int*)*(int*)p+2取到的是函数地址,要想调用,要使用typedef来转换成对应类型的函数指针void(*p)(int a)=(void(*)(int))(*(int*)*(int*)p+1
- 虚析构可以解决使用父类指针调用子类函数时,直接delete父类析构不全的问题
- 纯虚函数形式:virtual void fun()=0;其特点在于,纯虚函数在其所在类内没有实现与声明与函数主体,就一个声明
- 纯虚函数可以没有函数实现,要定义对象的话要用一个子类去继承这个基类,并实现它
- 总结,有纯虚函数的类必须用子类重写该纯虚函数才能实例化对象
- 只要有有纯虚函数的就是抽象类
- 全部都是纯虚函数的就是接口类,成员不影响
- 虚继承可以多个继承,即多个子类,以及子类的子类
- 虚继承主要解决孙子类重复的函数部分,防止调用不清
- 联编就是将模块或者函数合并在一起生成可执行代码的处理过程,(函数调用),按照联编所进行的阶段不同,可分为两种:静态联编和动态联编
- 静态是在编译阶段,动态联编针对多态(switch等需要用户插入操作的)
- 单例模式即一个类只创建一个对象
- 单例化第一步,先让构造函数成为私有或者受保护,然后通过静态成员(static))函数申请空间并返回地址
- 第二步要定义一个静态标记,记录对象的个数,并且借此来控制
- 第三步析构函数,是将标记清空并重置,已达到重复申请的对象的目的
- 赋值函数,拷贝构造也要是是有的,跟构造函数放到一起,这就是完全的单例模式
- try检测函数中的异常,在该函数中throw一个异常,再在try函数下catch该抛出的异常
- 抛出时若不是引用或指针就会调用拷贝构造,一般要避免浅拷贝
- 内部类(嵌套类)有两种,内访问外,外访问内
- 内部类使用外部类,现场创建外部类对象,才能使用外部类成员
- 外部类使用内部类成员也需要现场创建内部类对象
- 内部类的调用需要在外包类的里面创建一个内部类
- 当两种类型可以相互隐式类型转换,用
- static_cast<type>(expression),强制类型转换不需要这个条件
- 类型转换(新):static_cast;const_cast;dynamic_cast;reinterpret_cast;
- 函数模板:template<typename T,typename Y>/template<class T>但是作用域仅为下方挨着的代码块
- 函数模板具体化指的是一些特殊的类型的具体书写:template<>void fun<job>(job& j1,job& j2);
- 类模板也是写了模板之后,在下方挨着的类可用,继承的模板,在继承的父类后面一定要写模板参数列表
部分易错点总结:
- C++标识符作用域,标识符指的是标识个体的符号:group,group_qq,_sum之类其具有一定的作用域,有
- 局部作用域
- 全局作用域
- 文件作用域
- 函数作用域
- 函数原型作用域
- 名空间作用域
- 类作用域
- (int&)是一个地址
- 非常量引用的初始值必须为左值,即int& y=0,不可以,0没有地址,所以不能修改,0改成别的数是错误的
- const后*,所指向的值不变,但所指可以变;*后const,所指向的地址不能变,但所指值可以变化。(*在前面说明,指更重要,所以不能变,这么记忆)
- ( static)静态函数只能访问静态成员,非静态成员函数可以访问静态和非静态成员
- 静态的字段必须初始化,除了const\static\int同时存在,其余的静态字段都必须在类外初始化
- 静态的字段(static)只有const、static、int同时存在才可以在类内初始化
- 非静态的字段可以不初始化,也可以在类内直接初始化,不可在类外初始化
- UML图中类之间的关系:
- 依赖:在一个类的方法中使用另一个类的对象
- 泛化:继承关系
- 关联:两个类的属性间互相存在联系,各自的对象在对方的属性中
- 聚合:整体类和成员类,相比关联的平等,存在不等
- 组合:成员类是整体类的一部分,而且整体类可以控制成员类的生命周期,即成员类的存在依赖于整体类(也是一种复用类的方式)
- 实现:规定借口和实现借口的类,主要是实现关系
- 用类创建对象的过程称为实例化:User xiaoming=new User();
- 使用静态成员函数的目的就是使这个函数成为“类”级别的,而不是“对象级别”的,所以函数返回值不能使用*this,即静态成员函数不能调用普通成员函数和使用普通成员变量
- 静态存储区主要存放静态数据、全局数据和常量
- 栈区是在执行函数时,函数(包括main函数)内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放
- 堆区亦称动态内存分配。程序在运行的时候用malloc或new申请任意大小的内存
- 抽象类是不完整的,它只能用作基类,并且抽象类里面的方法只有名字都还没有被实现,所以不能实例化。
- 只要有有纯虚函数的就是抽象类,全部都是纯虚函数的就是接口类,成员不影响
- 纯虚函数形式:virtual void fun()=0;其特点在于,纯虚函数在其所在类内没有实现与函数主体,就一个声明,纯虚函数可以没有函数实现,有纯虚函数的类必须用子类重写该纯虚函数才能实例化对象
- 只要有有纯虚函数的就是抽象类
- 全部都是纯虚函数的就是接口类,成员不影响
- 发生逻辑错误不要使用异常处理机制
- 异常处理的catch语句,类型不同也可以捕获异常
- 运算符重载中赋值,下标,调用和成员访问必须为成员函数形式,其余可以选择非成员函数
- 参数列表,类成员定义引用时不用初始化,留下引用类型即可