C++ 面向对象特征:封装——抽象——继承——多态 https://www.runoob.com/ <命名空间> —————————————————————————————————————————————————————————————————————————————————————————— #include <iostream> 1 using namespace std; 2 using std::变量; 3 std::变量; cout cin 定义: namespace spaceA{ int a = 10; namespace spaceB{ struct std{ int age ; } } } 使用: using namespace spaceA; using spaceA::a; spaceA::a; <bool增强> —————————————————————————————————————————————————————————————————————————————————————————— bool a = true ; 1 bool b = false; 0 sizeof(a) = 一个字节; <三目增强> —————————————————————————————————————————————————————————————————————————————————————————— c = a < b ? a : b (a<b?a:b = 10) 三目运算符可以当左值;c不行! <const增强> —————————————————————————————————————————————————————————————————————————————————————————— const int a == int const a (const编译器阶段,宏是在预处理阶段)必须初始化 (C语言:a可以通过指针改变,所以a为假常量,如:int arr[a] 报错!) (C++:a不可以通过指针改变,所以a为真常量,如:int arr[a],正常!) const int * a int* const a const int* const a <枚举增强> —————————————————————————————————————————————————————————————————————————————————————————— enum nu { aa, bb, cc, }; enum nu p = aa;(只能赋值枚举类型!) <inline 内敛函数> —————————————————————————————————————————————————————————————————————————————————————————— 函数:不断的压栈出栈,再压栈出栈,开销比较大。(解决宏的弊端!) 宏:预处理阶段展开;不需要压栈出栈,弊端数据不处理。 内联函数:编译器阶段展开;不需要压栈出栈, 本质:牺牲代码段空间为代价,提高运行的时间效率; 特点:定义声明加inline c++编译器将函数体直接插入到被调用的地方; 没有普通函数调用时的开销(压栈,跳转,返回) 弊端:函数不能庞大,循环,判断,取址 <引用> —————————————————————————————————————————————————————————————————————————————————————————— int a = 1; int &aa = a;(引用实质为常指针,变量的别名!) == int *const aa = a; 1.声明必须初始化; 2.&前有类型为引用,其他皆为取址 3.可对引用再次引用,多次引用只是取别名 4.引用于变量地址相同 引用于函数: int& fun(int& a); 1.做函参(不需要拷贝动作) 2.做返值(不需要拷贝动作) 可以作为左值!因为返回值为引用(别名) 引用于结构:(不需要值拷贝) struct info{ int age; }; const info& fun(info &a); 引用于指针:(用于简化指针) 引用类对象: 引用于const: const int a = 1; const int& aa = a; int b = 1; const int& bb = b; <函数默认占位参数> —————————————————————————————————————————————————————————————————————————————————————————— 默认参数:定义函数参数直接赋值,默认起始从右至左 ! 占位参数:没什么意义! <函数重载> —————————————————————————————————————————————————————————————————————————————————————————— 函数重载:函名相同,函参不同,缺一不可! 注意:函数重载,不要写默认参数,避免函数冲突! 类型严格匹配,不匹配隐式转换,字符串不行! 编译器:void fun(char a,int b) --> fun_ci 与函数指针:typedef int(fun)(int,int); fun *pfun; typedef int(*fun)(int,int); fun pfun; int (*fun)(int,int)=NULL; 函数指针通过形参匹配函数重载(指向匹配函数),自身不能重载! 1.函数指针指向成员函数存在this指针不成功,静态成员函数没有this指针! 2.重载函数在本质上是相互独立的不同函数。 <类与对象> —————————————————————————————————————————————————————————————————————————————————————————— 类的机制后实现了数据的隐藏与封装,类的成员变量一般定义为私有成员,成员函数一般定义为公有的,依此提供类与外界间的通信接口 类: 成员函数 -> 对象 //(当做升级版的结构体 !) 成员变量 封装:对外开放数据,屏蔽数据,提供接口 将属性方法进行封装,进行访问控制 public protected private //(后两个仅在继承的时候才有区别!) class Data{ //(内部访问) public: void init(void); private: int year; int mon; int day; } Date one; one.init(); struct 默认所有成员为 public class 默认所有成员为 private #类的get方法:int getter(){ return a ;} #类的set方法:void setter(int new){ a = new ; } 1.类加上一个增强版的结构体 <构造与析构> —————————————————————————————————————————————————————————————————————————————————————————— 构造:类中定义与类名相同的特殊成员函数,叫做构造函数 (定义立刻初始化,避免危险!) 类名(); 自动调用:一般c++编译器自动调用,特殊情况手动调用; 分类: 有参: class stu{ public: stu(int a){ age = a; } private: int age; } stu t(6); // 创建直接初始化,避免危险! 无参: 拷贝:深拷贝 浅拷贝 拷贝:class 类名{ 类名(const 类名 & another){ //const保护被拷贝值! 拷贝结构体 } } 类名 t ; 类名 t1(t); //t给t1拷贝初始化 (类没有定义,系统默认提供,两个一模一样!) == 类名 t2 = t ; //属于构造拷贝(无参初始化) !== 类名 t3 ;t3 = t ; // 没有在初始化进行,不是拷贝属于赋值 默认: 默认无参构造函数:当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空 默认拷贝构造函数:当类中没有定义拷贝构造函数时,编译器默认提供一个默认拷贝构造函数,简单的进行成员变量的值复制 深拷贝: 浅拷贝: 初始化列表: 重点: 1.无返无参,类名相同,可重载,可默认参,一经实现,默认不复存在 2.对象创建时自动调用,完成初始化工作! 3.如果类中自定义构造函数,对象定义必须初始化! 4.显示提供构造函数,默认构造不存在;不提供默认构造自动调用,什么都不干! 5.显示拷贝构造不存在时!系统提供默认拷贝构造函数,将拷贝值一模一样复制;(如:拷贝:) 6.没有任何显示构造函数(显示无参,有参,拷贝)时,系统默认构造才出现 7.当自定义构造函数时,定义对象的同时必须进行初始化!!! 8.构造对象成员的顺序跟初始化列表无关,跟定义顺序有关 9.对象的初始化只能通过构造函数! 析构:对构造函数进行清理 //(构造函数临死之前,系统自动调用析构函数) ~类名(); // 如:构造函数new,栈空间回收之前调析构回收堆空间,防止内存泄露 默认:同理 默认无参析构函数:当类中没有定义析构函数时,编译器默认提供一个无参析构函数,并且其函数体为空 重点: 1.无类型无参无返,不可重载!和默认参 2.每个类只能有一个析构 3.构造new,析构delete 4.提供显示析构,默认析构不存在! 5.有垃圾写析构,无垃圾不用写,只是系统提供的接口! 6.析构调用与构造顺序相反!谁先构造,谁后析构!!(栈原理:先进后出,压栈弹栈) <动态创建/释放 new/delete> —————————————————————————————————————————————————————————————————————————————————————————— 形式:指针变量 = new 类型(常量) // ()初始化空间! 指针变量 = new 类型 [表达式] delete 指针变量 delete[] 指针变量 作用:从堆空间分配一块类型大小存储空间,返回首地址 常量为初始化值,可省略;数组对象不能指定初始值 重点: 1.malloc和free是库函数(Cpp建议不要用) 2.new和delete是运算符,不是函数,因此执行效率高(不是函数,不用压栈弹栈) 3.malloc不会触发类的构造函数,而new会触发类的构造函数 4.Free不会触发类的析构函数,而delete会触发类的析构函数 <静态> —————————————————————————————————————————————————————————————————————————————————————————— 静态成员变量: 声明: static 数据类型 成员变量; //在类的内部 初始化:数据类型 类名::静态数据成员=初值;//在类的外部 调用: 类名::静态数据成员 类对象.静态数据成员 静态成员函数: 声明:static 函数声明 //在类的内部 调用:类名::函数调⽤用 类对象.函数调⽤用 静态成员函数只能访问静态数据成员。 原因:非静态成员函数,在调用时this 指 针被当作参数传进。而静态成员函数属于类,而不属于对象,没有 this 指针。 #编译器对属性方法的处理机制: 静态存储空间: 处理机制:成员变量和成员函数是分开存储! 成员变量: 普通:存储于对象中,与struct变量有相同的内存布局和字节对齐方式 静态:存储于全局数据区中(在类里面) 成员函数:存储于代码段 重点: 1.C语言中的内存四区模型仍然有效! 2.CPP中类的普通成员函数都隐式包含一个指向当前对象的this指针。!!! 3.静态成员函数、成员变量属于类!!! 4.静态与普通成员函数的区别: 静态成员函数不包含指向具体对象的指针 普通成员函数包含一个指向具体对象的指针 5.静态成员函数没有this指针,可以通过函数指针指向! 6.Cpp静态成员属于整个类而不是某个对象,静态成员变量只存储一份供所有对象共用 7.所有对象都可以共享它,使用静态成员变量实现多个对象之间的数据共享不会破坏隐藏的原则,保证了安全性还可以节省内存! 8.类的静态成员,属于类,也属于对象,但终归属于类! 9.static成员只能类外初始化 <this指针> —————————————————————————————————————————————————————————————————————————————————————————— 成员函数隐含定义this指针,接受调用对象的地址,指向调用对象; #类成员函数形参和类属性,名字相同,通过this指针解决 #类成员函数可通过const修饰 1.把全局函数转化成员函数,通过this指针隐藏左操作数 Test add(Test &t1,Test &t2) -> Test add(Test &t2) 2.把成员函数转换全局函数,多了一个参数 void prit() -> void prit(Test *pthis) 3.函数返回元素和返回引用 如图: 1.类定义对象时,私有成员被分配,公有函数接口通过调用者的地址进行操作 2.this就是当前调用函数对象的地址! 3.this指针不是const text* 类型 4.this指针是一个常指针,是 text const * 类型(只能指向对象本身!可以改变值,不可改变址!) 5.int getk() const :修饰this指针为const text const * 只读 <友元> —————————————————————————————————————————————————————————————————————————————————————————— #同类对象间无私处,异类对象间有友元! 友元三友: 友元函数 友元类 友元成员函数 诠释:类的机制后实现了数据的隐藏与封装,类的数据成员一般定义为私有成员,成员函数一般定义为公有 的,依此提供类与外界间的通信接口。但是,有时需要定义一些函数,这些函数不是类的一部分,但又 需要频繁地访问类的数据成员,这时可以将这些函数定义为该函数的友元函数。除了友元函数外,还 有友元类,两者统称为友元。 作用:提高了程序的运行效率,但它破坏了类的封装性和隐藏性,使得非成员函 数可以访问类的私有成员。 友元函数:可以直接访问类的私有成员的非成员函数。它是定义在类外的普通函数,它不属于任何类,但需要在类的定义中加以声明,声明时只需在友元 的名称前加上关键字 friend friend 类型 函数名(形参) //一个函数可以是多个类的友元函数,只需要在各个类中分别声明。 全局函数作友元函数; 类成员函数作友元函数 1.全局函数作为友元函数 2.成员函数作为友元函数 友元类 :友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的隐藏信息 #当希望一个类可以存取另一个类的私有成员时,可以将该类声明为另一类的友元类 friend class 类名;(类名必须是程序中已定义的类) 注意: 1.友元关系不能被继承 2.友元关系是单向的,不具有交换性(若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明) 3.友元关系不具有传递性(若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的声明。) 4.元友函数没有this指针 <重载> —————————————————————————————————————————————————————————————————————————————————————————— #所谓重载,就是重新赋予新的含义!!! 本质:运算符重载的本质是函数重载 重载函数: 函数类型 函数名 operator 运算符名称(形参表列){ 重载实体; } //operator 和 运算符名称(一起构成了新的函数名)== operator+重载了运算符+ //函数名是由 关键字operator和其后要重载的运算符符号构成的 const Complex operator+(const Complex &c1,const Complex &c2); operator+ 重载了运算符+ 友元重载: 成员重载: 操作符重载: 关系重载:>, <, ==, >=, <=, != 逻辑重载:|| && ! 赋值重载:= 单目重载:+ - * & 双目重载:+ - * / % 位运算重载:& | ~ ^ << >> 自增减重载:++ -- 申请释放重载:new new[] delete delete[] 输入输出重载: 其他运算重载:()函数调用 ->成员访问 ,逗号 []下标 不可重载: 成员访问:. 成员指针访问:.* ->* 长度:sizeof 条件:?: 域运算::: 预处理: # 重载规则: 1.Cpp不允许用户自己定义新的运算符,只能对已有的Cpp运算符进行重载。 2.Cpp允许重载的运算符 如图: 3.重载不能改变运算符运算对象(即操作数)的个数 4.重载不能改变运算符的优先级别。 5.重载不能改变运算符的结合性 6.重载运算符的函数不能有默认的参数 7.重载的运算符必须和用户定义的自定义类型的对象一起使用,其参数至少应有1个是类对象(或类对象的引用) 8.用于类对象的运算符一般必须重载,但有两个例外,运算符=和运算符&不必用户重载 9.应当使重载运算符的功能类似于该运算符作用于标准类型数据时候时所实现的功能 10.运算符重载函数可以是类的成员函数,也可以是类的友元函数,还可以是既非类的成员函数也不是友元函数的普通函数 <继承派生> —————————————————————————————————————————————————————————————————————————————————————————— #C++代码重用性是通过继承这一机制来实现,提高执行时间的效果 类与类关系:has-A uses-A is-A has-A:包含关系,用以描述一个类由多个“部件类”构成。实现has-A关系用类成员表示,即一个类中的数据成员是另一种已经定义的类 uses-A:一个类部分地使用另一个类。通过类之间成员函数的相互联系,定义友员或对象参数传递实现 is-A:机制称为“继承”。关系具有传递性,不具有对称性。 #基类 ——> 继承 + 新增 = 派生类 class 派生类名:[继承方式] 基类名{ 派生类成员声明; }; 继承方式:公有继承,私有继承,保护继承(常用:公有继承) 公有继承:当公有继承时,基类的公有变成派生类的公有,保护变成派生类的保护,私有不可以直接被派生类访问,可通过公有和保护间接访问! 保护继承:当保护继承时,基类的公有和保护成员变成派生类的保护成员,私有成员不可直接访问 私有继承:当私有继承时,基类的公有和保护成员变成派生类的私有成员,私有成员不可直接访问 多继承:派生类继承多个基类(继承多父类特性) #鱼与熊掌兼得的做法! 语法:class <派生类名>: <继承方式1><基类名1> , <继承方式2><基类名2> , ...{ <派生类类体>; } 虚继承: 1.如果一个派生类从多个基类派生,而这些基类又有一个共同的基类,则在对该基类中声明的名字进行访问时,可能产生二义性 2.如果在多条继承路径上有一个公共的基类,那么在继承路径的某处汇合点,这个公共基类就会在派生类的对象中产生多个基类子对象 3.要使这个公共基类在派生类中只产生一个子对象,必须对这个基类声明为虚继承,使这个基类成为虚基类。 4.虚继承声明使用关键字virtual 继承与构造析构: 在子类对象构造时,需要调用父类构造函数对其继承得来的成员进行初始化 在子类对象析构时,需要调用父类析构函数对其继承得来的成员进行清理 继承中同名成员变量处理方法: #同名成员变量和成员函数通过作用域分辨符进行区分 父类和子类成员变量成员函数同名,但存储位置不同,子类依然继承父类 通过作用域分辨符::区分 1.子类对象在创建时会首先调用父类的构造函数 2.父类构造函数执行结束后,执行子类的构造函数 3.当父类的构造函数有参数时,需要在子类的初始化列表中显示调用 4.析构函数调用的先后顺序与构造函数相反 5.原则:先构造父类,再构造成员变量,最后构造自己 先析构自己,在析构成员变量,最后析构父类 继承中关键字static: 派生类中访问静态成员: 类名 :: 成员 通过对象访问: 对象名. 成员 1.基类定义的静态成员,将被所有派生类共享 重点: 1.一个类可以派生自多个类,可以从多个基类继承数据和函数 2.is-a 关系:例如,哺乳动物是动物,狗是哺乳动物,则:狗是动物! 3.派生类可以访问基类中所有的非私有成员(即:public protected) 4.派生类继承基类所有方法,除去:构造,拷贝,析构,友元函数和重载运算符!! <多态> —————————————————————————————————————————————————————————————————————————————————————————— #C++多态:意味着调用成员函数时,会根据调用函数对象的类型来执行不同的函数 多态: 继承 + 虚函数重写 + 父类指针(父类引用)指向子类对象 动态联编与静态联编: 联编是指一个程序模块、代码之间互相关联的过程 静态联编:程序的匹配、连接在编译阶段实现,也称为早期匹配。重载函数使用静态联编 动态联编:程序联编推迟到运行时进行,所以又称为晚期联编(迟绑定)switch语句和if语句是动态联编的例子 1.Cpp与C相同,是静态编译型语言 2. 重点: 1.多态是设计模式的基础,多态是框架的基础 <模板> —————————————————————————————————————————————————————————————————————————————————————————— <类型转换> —————————————————————————————————————————————————————————————————————————————————————————— <异常> —————————————————————————————————————————————————————————————————————————————————————————— 笔记 —————————————————————————————————————————————————————————————————————————————————————————— 1.未初始化全局变量,在bss段(数据全为0) 2.cpp定义全局变量两次——int a;(bss段) int a = 1;(data段) 报错! 3.cpp结构体定义变量-> 结构体名+变量 4.变量随用随定义,如for; 5.对常量取地址,编译器会临时开辟空间,返回临时空间地址(看const) 6.计算机速度:cpu(寄存器)>缓存>内存>硬盘>网线 7.引用在常量区 8.函数重载就是对一个已有的函数赋予新的含义,使之实现新功能,因此,一个函数名就可以用来代表不同功能的 函数,也就是”一名多用”。 9.类代表类型,对象代表对象 拷贝构造函数代码 new/delete 代码