C++
2020-04-22 10:35:12 0 举报
AI智能生成
C++详细总结
作者其他创作
大纲/内容
必须初始化的变量
指针
引用
函数
定义
类型
函数名
形式参数表
函数体
参数传递
按值
按地址
将形参的类型说明成指针
void swap(int *a,int *b)
引用传递
输入——常引用,输入/出——引用
void swap(int &a,int &b)
函数与指针
指针函数
函数指针
回调函数
void(*p)(int)= &add
内联函数
在函数的定义或者声明前加上关键字inline
编译时,编译器会把 每次调用内联函数的地方替换为该函数体的代码
类结构中在类中说明的函数都是内联函数
目的
解决程序中函数体代码不是很大。却被频繁调用的函数的函数调用效率问题
注意事项
内联函数的定义必须出现再该函数被第一次调用前
内联函数不能有复杂的调用语句
递归函数不能是内联函数
消除了宏定义的不安全性(宏定义没有确定的数据类型)
函数重载
指同一个函数名可以对应多个函数的实现
条件:参数个数不同 参数类型不同 返回值类型不能作为重载的条件
函数重载即函数的多态性
带默认参数值的函数
通常要为函数的每个形参给定对应的实参,若没有给定则按指定的默认值进行工作
形参默认值再声明中给定,不能放在定义中。没有声明时才在定义中指定形参的默认值
默认值的定义遵循从右往左的顺序,如果某个形参没有默认值,则它左边的参数就不能有默认值。 void fun(int a,double b = 4.5,int c = 3);
使用缺省参数时,主要满足函数重载条件
void fun(int x,int y = 0); void fun(int x); 不是函数重载,不提供缺省值时,才可以进行区分调用哪个函数
CONST的使用
const与#define
C++中用const代替宏定义
const常量有数据类型,而宏定义没有
编译器可对const进行类型安全检测,宏定义只进行字符替换
const会进行常量折叠,在编译时预先计算出常量表达式的值
const与指针
const int *p 指向常量的指针 &p 可修改 *p不可修改
char * const p; 指针常量 &p不可修改 *p可修改
const char * const p 指向常量对象的指针常量 均不可修改
可以把一个非const对象的指针赋给一个const指针,但反之不可
const与函数
不使用const
用值传递函数参数时
返回值为内部数据类型时,因为二者皆传递数据的副本,而与源数据无关,一般情况下不用const修饰
使用const
参数是指针,且仅作输入用,则在类型前 加const,防止指针在函数体内被意外修改
如果输入参数以值传递的方式传递对象,则宜改用“const&”方式来传递,省去临时对象的构造析构
传递一个参数时,通常先选择用常引用
void指针与const指针
一般来说只能用指向相同类型的指针给另一个指针赋值,而在不同类型的指针之间进行赋值是错误的。但是void是一个例外
void
除指针基类型之外,任何变量不能void
指针的基类型可以是void,表示该指针变量可以变换指向任何类型
void *v = str; 访问*v 无意义,仅仅存放了一个地址
函数传参不确定类型时用viod*
特殊关键字
register
用register声明的变量称为寄存器变量在可能的情况下会直接存放在辑器的寄存器中。
对32位编译器不起作用,当全局优化时,会自行选择,用户无法控制。
auto
存储类型标识符,表明变量自动具有本地范围。
块范围的变量声明(如for循环体内的变量声明)默认为auto
volatile
直接存取原始内存地址
用法:中断服务中修改的供其他程序检测的变量 \t多任务环境下任务间共享的标志加volatile \t存储器映射的硬件寄存器加volatile
extern
用extern声明的变量或函数应该在别的文件或同一文件的其他地方定义(实现)。在文件内声明的一个变量或函数默认为可被外部使用。
extern修饰的变量(外部变量)静态分配内存
可指定使用另一语言进行链接——条件编译 extren “C”声明语句 extern “C”{声明语句块}
OOP封装性
生活中:封装成类
代码上是为何代码复用,将类和对象抽象为数据类型和变量的关系
封装后类可以把自己的的数据和方法只让可信的类或者对象操作,让不可信的隐藏
类与对象
生活中:将对象抽象出属性和行为划分成一个类
内存(代码):将类和对象抽象为数据类型和变量的关系
关键字:public、protected、private 破环封装:友元 friend
定义类注意事项
类体中不允许对所定义的数据成员进行初始化
另一个类的对象可以作该类的成员
自身类的对象不可以做该类的成员,但自身的对象的指针和引用可以作为该类的成员
类中的inliine不利于类的接口与类的实现分离,不利于信息隐藏,并不是个好方法
this指针
隐含于每一个类的成员函数中的特殊指针,指向正在被某个成员函数操作的对象的指针,也就是操作该成员函数的对象
使用机制
当对一个对象调用成员函数时,编译程序先将对象的地址赋给this指针,然后调用成员函数,每次成员函数存取数据成员时,则隐含使用this指针,而通常不去显示的使用this指针来引用数据成员
同样也可以使用*this指针来标示调用该成员函数的对象。
指向本对象开始的指针
构造函数
用来创建对象是初始化对象,即为对象成员赋初值,总与new运算符一起使用再创建对象的语句中。 可以重载
不需要用户调用,建立对象时自动执行。 名字必须与类名相同, 没有数据类型,不返回任何值
;默认构造函数
不带参数的构造函数
带参数的构造函数 + 缺省值
显/隐式构造函数
C++ 中最长字符串是1G
析构函数
~类名 无参数,无返回值,不可重载
作用:在撤销对象占用的内存之前完成一些清理工作,使这部分内存可以被程序分配给新的对象使用。
拷贝构造函数
用一个一直的对象来初始化一个被创建的同类对象
函数名同类名,无返回值 只有一个参数,是对某个对象的常引用 <类名 > ::<类名>(const<类名>&<引用名>) A ::A(const A &a);
每个类都必须有一个拷贝构造函数, 如果用户没有提供,系统会自动提供一个缺省的拷贝构造函数,进行对象之间的位拷贝;
倘若类中含有指针变量时,两个缺省的函数就隐含了错误, 即两个变量指向同一块儿空间,任何一方变动都会有影响,且释放两次。
深拷贝、浅拷贝
两者不同的是对引用的处理,深拷贝将会在新对象中创建一个新的和原始对象中对应字段相同的字段,这个引用和原始的引用是不同的,改变新对象中的字段不会影响原来的内容。
缺省拷贝构造函数是采用浅拷贝即位拷贝。
使用情况
明确表示一个对象初始化另一个对象 tpoint N(M);
当对象作为函数实参传递给函数形参时(传值调用) p = f(N);
当对象作为函数的返回值 return R;
赋值函数
用于更新类类型的对象
该函数函数名是一个操作符,必须与operator合用; 该函数只有一个参数,是对该类某个对象的常引用 一般赋值操作返回对被赋值对象的引用 <类名> & operator = (const <类名>&<引用名>)
例:Person P1 = P2 = P; P2 = P赋值函数 P1 = P2 拷贝构造函数
Static 常见用法
静态数据成员
目的:用来解决数据共享问题,在类的范围内让类的所有对象共享某个数据,也就是不通过全局变量,实现多个对象之间的数据共享
静态数据成员存储再全局的静态存储区,贡多有对象使用,所以也教类成员或类变量
静态数据成员的值对于每个对象都是一样的,但其值可被任何一个对象更新
它是静态存储的所以必须初始化,在类体外实现 静态数据成员<数据类型><类名>::<静态数据成员名> = 值: int A :: m_sb = 10;
初始化是不加该成员的访问控制符(因初始化位置与访问权限无关) 初始化时用作用域运算符表明他所属的类 引用格式<类名>::<静态成员名> A::m_sb
静态成员函数
公有的静态数据成员可以直接访问,私有的或者保护的惊天数据成员必须通过公有的接口进行访问,即静态成员函数(类成员函数) <类名> :: <静态成员函数名>(参数表)
注意
静态成员函数的实现中不能直接引用类中说明的非静态成员,可引用类中说明的静态成员。
静态成员函数中要引用非静态数据成员时,可通过对象来引用 static void f1(M m); cout << m.m_a<<endl;
在类中的static变量属于整个类所拥有,这个函数不接受this指针,因为只能访问类的static成员变量和调用静态成员函数
类中的常量const
const在每个对象中分配空间
const修饰的数据成员生命周期就是对象的生命周期,不同起的对象其const数据成员的值可以不同
不能在类中初始化const数据成员,只能再类构造函数的初始化列表中进行初始化 A::A():mc_c(100){ }
常成员函数
任何不修改成员数据的函数最好都声明成常成员函数 用const修饰的函数,在声明和定义时都要加const 构造和析构均不能是const int f()const;
用const修饰的对象只能调用const成员函数 const修饰成员函数 const放在函数前面表示返回值为const 放在函数后面则为常成员函数
子主题 4
要是某个变量在const对象中可修改 1.把常函数中隐含的this指针强制转换为非const void Y::f()const{ ((Y*)this)-> = 100; 2.把希望可被改变的数据成员声明为mutable mutable int j; void Y::f()const { this ->j = 100; } mutale 修饰的变量只能是类的成员函数
友元
类必须通过对象来调用成员函数访问私有成员 类可以不通过成员函数就可以访问类的私有成员
友元函数
非成员函数 在调用上与普通函数相同 可直接访问该类私有成员 类外定义加friend 定义不加
运算符重载
友元的唯一应用
赋予已有的运算符多重含义,增强了C++语言的扩充能力
作为类成员的重载函数,其形参看起来比操作数数目少1; string & operator = (const string &trs) 运算符定义为非成员函数时,通常必须将它们设置为所操作类的友元。因为这种情况下,通常需要访问类的私有部分。
单目运算符;friend A& operator ++(A&) A& operator ++();
选择成员和非成员
= [] ()->必须为成员 (+=不是必须) 自增自减,类成员 对称的操作符,算术运算、关系操作符、位操作符 用友元
继承性
自动获得另一种事务的部分或者全部的东西(属性能力),是一种从属关系
继承与组合均为提高代码的可重用性 继承:是什么 组合:有什么
提高了代码的可重用性
class 子类名 :public 父类名
子类成员函数可以访问共有的和私有的, 子类对象只可以访问公有的
基类与派生类
派生类构造函数
构造函数不能被继承 派生类构造函数的工作
对自己的数据成员进行初始化
负责调用基类构造函数进行初始化
调用子对象构造函数,对派生类的子对象进行初始化
若某项的参数表为空时,则从成员初始化列表中省略,表示使用缺省构造函数初始化该类子对象
派生类析构函数
析构函数不能被继承,调用顺序与构造函数严格相反
隐藏
成员同名时,派生类覆盖基类
调用方法:+作用域
多继承与二义性问题
多继承
多继承构造函数 <派生类名>(<总参数表>):<基类名>(<参数表1>),<基类名>(<参数表2>){ }
多继承构造函数 负责所有基类构造函数的调用
处于同一层次的各基类构造函数的执行顺序取决于定义派生类时所指定的各基类顺序,与派生类构造函数中所定义的成员初始化列表中的各项顺序无关;
二义性
产生的原因
在多继承情况下,造成的对基类中某个成员的访问出现了不唯一的情况
对于不同基类中同名成员的访问,成为二义性
重名定义
解决方法:
成员名限定法
在派生类中定义一个同名成员
使用声明
using A::f; using B ::f; 重载条件:同一个作用域
作用域限定
多路径继承
菱形继承
d.a与d.A::a会产生二义性
作用域限定:a.B::a或d.C::a
虚基类
目的:解决多路径二义性问题 多一个指针 只创建一份儿空间
格式:virtual<继承方式><基类名>关键字 \tclass D:virtual public A
虚基类构造函数必须只被调用一次,目的是保证虚基类子对象制备初始化一次;
最派生类:继承结构中建立对象时所指定的类; 虚基类的子对象由最派生类的构造函数通过调用虚基类的构造函数进行初始化 在初始化列表中 虚基类的构造函数先于非虚基类的构造函数执行 最派生类的构造函数的成员初始化列表中必须给出对虚基类的构造函数的调用
多态性
目的:发出同样的消息被不同类型的对象接收导致完全不同的行为 对于软件功能的扩展和软件重用都有作用。
运行多态:通过基类的指针,调用不同派生类的同名函数,表现出不同的行为
重载多态
同一作用域
通过调用相同名字的函数,表现出不同的行为。运算符重载也是一种多态
实现:
联编(绑定)
确定具有多态性的语句究竟调用哪个函数的过程
静态联编
在程序编译时刻就决定调用哪个函数==编译时多态 主要通过函数重载和运算符重载
动态的多态性(动态束定)
运行时多态在程序运行时确定操作所指的对象 主要靠虚函数
虚函数
virtual<类型说明符><函数名>(<参数表>)
在基类函数前加上virtual关键字,则派生类自动加上
优点,编写程序时,使用继承层次中任意类型的对象,无须关心对象的具体类型。 point *p = new point3D(3,4,5); p->point(); 输出p的[3,4]; point3D *p = new point3D(3,4,5); p->point(); 输出p的[3,4,5];
实现机制
建立虚函数表(hash表)存放函数的指针(代码段) 存放同名,同参,同返回值的虚函数地址。
纯虚函数
不在类中给一个有意义的实现
为派生类提供一个公共的接口,再派生类中实现 virtual <类型><函数名>(<参数表>) = 0;
含纯虚函数的类成为抽象类,抽象类只作为基类使用,
抽象类不能实例花,即不能创建对象,一般将该类的构造函数说明为保护的访问控制权限
只能作为别的类的基类
在成员函数内可以调用纯虚函数,但在构造函数中不能调用纯虚函数(纯虚函数没有代码)
函数的重载、重写与隐藏
多态:重写 只派生类函数覆盖基类函数
特征
不同的范围()基类与派生类
函数名字,参数,返回值均相同
基类函数必须由virtual关键字
const可能会使虚函数成员函数的重写失效
封装:重载 同一作用域的不同函数使用相同的函数名,但是函数的参数类型或参数个数不同
相同范围(同一个类作用域)
函数名字相同
参数不同
virtual关键字可有可无
继承:隐藏 指派生类函数隐藏了与其同名的基类函数
规则
如果派生类和基类的函数同名但参数不同 无论有无virtual关键字 基类函数都将隐藏
如果派生类的函数与基类同名,并且参数相同,但是基类无virtual关健字 此时,基类函数被隐藏
类型转换符
const_cast
去掉类型的const或volatile属性
常量指针被转化成非常量指针,并且仍然指向原来的对象
常量引用被转换成非常量引用,并且仍然指向原来的对象
常量对象转换成非常量对象
const int a = 10; int *p = const_cast <int *>(&a);
const SA ra = {100}; SA & rb = const_cast<SA &>(ra);
static_cast
类似C风格的强制类型转换。无条件转换,静态数据类型的转换
父类和子类的转换:其中子类指针转换为父类指针是安全的;但父类指针转换为子类是不安全的(dynamic_cast)
基本数据类型转换:enum, struct, int, char, fioat等。 不能进行无关类型的转换
int n = 6; double d = static_cast<double>(n);
把空指针转换为目标类型的空指针
把任何类型的表达式转换为void类型
int *pn = &n; double *d = static_cast<double *>(&n); //无关类型指针转换,错误 void *p = static_cast<void *>(pn); //任意类型转换成void类型
static_cast不能去const\\volitale属性
reinterpret_cast
重新解释定义
转换的类型是一个指针、引用、算术类型、函数指针或成员指针
再比特位级上进行转换。可以把一个指针转换为一个整数,也可任意把一个整数转换为一个指针(但不能将非32bit的实例转成指针。)
最普通的用途就是再函数指针类型之间进行转换
很难保证移植性
dynamic_cast
动态类型转换 如子类和父类之间的多态类型转换
安全的父类与子类转换
子类转父类
BaseClass *pd2 = dynamic_cast<BaseClass *>(pb);
父类转子类,静态类型转换,危险,访问子类时可能会越界
父类转子类,动态类型转换,安全,结果是NULL
向上转型
释放不完全,用虚析构函数可释放完全
向下转型
安全
基类必须有虚函数,保持多态性才能使用
相同父类不同子类之间的相互转换,但结果是NULL
有条件转换,动态类型转换,运行时类型安全检查(转换失败返回NULL)
C++
初始化列表中初始化
const数据成员,只能再类构造函数的初始化列表中进行初始化,不能在类中初始化 A::A():mc_c(100){ }
类的成员函数是某个其他类的对象时,该对象成为子对象,当类中有子对象时,类的构造函数应通过初始化列表对子对象进行初始化
在初始化列表中 虚基类的构造函数先于非虚基类的构造函数执行
最派生类的构造函数的成员初始化列表中必须给出对虚基类的构造函数的调用
多继承构造函数
<派生类名>(<总参数表>):<基类名>(<参数表1>),<基类名>(<参数表2>){ }
初始化在类外进行的
静态数据成员<数据类型><类名>::<静态数据成员名> = 值: int A :: m_sb = 10;
总结
什么东西
目的是为什么
怎么做(代码实现)
oop面向对象程序设计
方法强调直接以问题域中的事物为中心来思考认识问题并按照这些事物的本质特征把它们抽象为对象作为构成软件系统的基础
特点
以“对象”或“数据”为中心
类
封装性和继承性
提高代码复用率
与C语言的比较
以C语言为基础(完全包含)
与C的不兼容之处
void*
C语言中可进行隐式转换为其他类型的指针但C++不允许
new class 作为新的关键字
IDE(集成开发环境)
用于程序开发环节的应用程序,包括代码编辑器、编译器、调试器、图形用户界面
linu下编译(g++)
输入输出流
C++中I/O是以字节流的形式实现的,流实际上就是一个字节序列
istream输入流
从输入设备到内存(读操作)
ostream输出流
从内存到输出设备(写操作)
命名空间
是一个由程序设计者命名的内存区域,根据需要指定一些有名字的空间域,把一些全局实体放在某个命名空间中,从而与其他全局实体分隔开
从ANSIC++引用的可以由用户命名的作用域,用来处理程序中常见的同名冲突。
使用: using std::cout using namespace std;
基本数据类型
从数据作用域的大小
全局变量
局部变量
从生命周期
静态存储变量
在程序运行中固定的占用空间
动态存储变量
仅在需要的时候分配和占用内存
C++是强类型语言
每个变量都有确定的数据类型
整型、字符型、实型、逻辑型(bool)
bool为C++中独有的(true、false)
运算符
算术、逻辑、关系、位
C++内存管理
动态内存分配
静态存储区
在程序运行开始前就分配的存储空间都在静态存储区
栈
局部变量的内存分配
堆
动态内存分配的存储空间
内存分配运算符
C语言中的malloc/free是C++中标准库函数不是运算符,不在编译器的控制权限之内。
new/delete是C++ 的运算符
new用法:指针 = new 数据类型 int *p; p=new int;int *p = new int(5); int *p = new int[30]; 系统为指针p分配了30个int的内存单元
delete功能:用来删除是用new创建的对象/对象数组或一般指针 delete 指针名 delete 【】指针名
new/delete与malloc/free比较
new运算符根据对象的类型,自动决定其大小,不使用sizeof运算符,而malloc要指定分配存储空间的大小
new返回指向此类型的指针,不用进行强制指针类型转换。 malloc返回void*类型的指针。
若在动态申请内存时找不到足够大的内存块,malloc和new将返回NULL指针,宣告内存申请失败。
用free或delete释放内存后,将指针置空。防止野指针。
动态内存的申请与释放必须配对,防止内存泄漏
数组与指针
数组
数目固定,类型相同的若干个变量的有序集合
创建:要么再全局存储被创建(全局数组),要么在栈上创建。其地址与容量在生命周期内保持不变,只有内容可以改变。
存放某个变量的地址值的一种变量
本身数据值的类型是long int型《linux》4个字节
杜绝野指针
野指针不是NULL指针,是指向垃圾内存的指针,危险之处是if语句对它不起作用
任何指针变量刚在创建时不会自动成为NULL指针,它的缺省值是随机的。所以,指针变量在创建是应该初始化, 1.设为NULL指针char *p = NULL; 2.指向合法内存 char *str = (char *)nalloc(100); 指针p被free或者delete后应设置为NULL; 指针操作超出了变量的作用范围:程序不能返回指向栈内存的指针或引用。
指针和数组
C++中可通过指针访问数组
任何一个数组的名字都是一个常量指针(存放地址),该数组的首元素地址值。
下标法a[j] 指针法 *(a+j)
区别
指针名指代一种数据结构,即数组
数组名可以转换为指向其指代实体的指针,一个指针常量,数组不能自增自减,不能修改
数组名作为函数形参时,退化成一个指针
指向数组的指针是一种变量类型仅仅意味着数组的存放地址。4
数组a复制给b,strcpy 数组a和b比较,strcmp
数组指针:指向数组的指针 指针数组:多维数组(指向指针的指针)
释放堆中的空间
int *p = &n; delete p;
是某一个变量的别名,对引用操作=对变量直接操作
<类型>&<引用名>=(变量名)
必须初始化
将一个引用赋予一个变量
引用声明完毕后,不饿能再做为其他变量名的别名
一般作为函数的参数或函数返回值
常引用
提高使用效率,保护传递给函数的数据不在函数中被修改
const 类型说明符 & 引用名 = 目标变量名
不能通过引用对变量的值进行修改但目标本身能够改变
非const引用只能绑定相同类型的变量,const引用可以绑定到相关类型或右值(创建一个副本 栈中创建空间临时分配空间)
使用细则
使用引用作为传递函数的参数,再内存中并没有产生实参的副本,它是直接对实参操作。
如果输入参数以值传递的方式传递对象,宜使用“const&”方式来传递,可省去临时对象的构造和析构过程。
不允许返回的引用对应于一个局部变量(有警告)
0 条评论
回复 删除
下一页