inline 函数
inline 关键字用来修饰函数,一般是函数过于简单时使用,避免子函数返回等一系列开销,实际使用与函数一致,但编译器会自行处理,类似与给代码贴在调用处
1.inline 函数中应过分简单,函数体中不能使用循环语句(for,while.do while等) ,switch语句
2.inline 函数定义必须要在第一次调用以前
3.inline 函数中不能进行异常接口声明
CONSTEXPR 函数
CONSTEXPR 关键字修饰的函数返回值一定是一个常量表达式,也就是说编译时可计算
函数重载
参数类型不同,参数个数不同 ,返回值不能区分重载函数 (const 函数与 普通函数也可以重载)
C++ 系统功能属于哪个库
cplusplus.com 上可以根据名字查询
类
C++ class 中默认访问控制符是 private
C++ struct 中默认访问控制符是 public
直接在类中给出函数体,形成内联成员函数
委托构造函数
使用当前类的其他构造函数来帮助当前构造函数完成初始化,也就是说,真实构造函数可以只写一个,然后都使用这个构造函数来初始化对象,保持代码的一致性, 方便后期维护与修改
复制构造函数
使用一个对象去初始化另一个新对象
不定义的时候,编译器默认生成一个复制构造函数,使得类中成员一一对应复制
classname(const classname &对象名)
复制构造函数何时调用
1.函数中以对象作为形参时,在调用函数时,形参实参结合的时候,会调用复制构造函数
2.定义一个对象时,以本类的另一个对象做为初始值
3.如果函数返回值时类的对象,函数执行完成返回主调函数时,将使用return语句中的对象初始化一个临时无名对象,传递给主调函数时
析构函数
析构函数,在对象释放的时候自动调用,可以用来释放资源
类的组合
组合类的构造函数调用顺序
首先对初始化列表中列出的成员进行初始化(包括基本数据成员与对象成员),初始化顺序是成员在类中的定义顺序
成员对象构造函数调用次序 按对象的定义顺序,先声明先构造
初始化列表中未出现的成员对象,调用默认无参的构造函数
处理完初始化列表后,再调用构造函数体内的内容
前向引用声明
class A; //前向引用声明
主要使用在两个类互相引用时
给类一个标识符,但不完善细节
前向引用声明局限
1.在提供一个完整的类声明之前,不能声明该类的对象,也不能在内联函数中使用该类的对象
2.当使用前向引用声明时,只能使用被声明的符号,不能涉及类的细节
//前向引用声明错误做法
class A;
class B
{
A a;
}
class A
{
B b;
}
//上述问题的解决
class A;
class B
{
A *a; //定义指针时不需要细节,在构造A的时候使用动态内存分配 new 完成A的初始化
}
class A
{
B b;
}
结构体
C++中的结构体是一种特殊的类
与类的唯一区别
类中缺省访问权限是private
结构中缺省访问权限是public
结构体一般用来定义数据
结构体中若没有用户定义的构造函数,没有基类和虚函数
那么可以使用下面的语法来初始化
类型名 变量名 = {成员数据1初值,成员数据2初值,.....}
联合体
联合体的目的:存储空间的共用
union unionName
{
共有成员 跟结构一样,缺省就是public
protected:
保护型成员
private:
私有成员
}
联合体特点
成员共用同一组内存单元
任何两个成员不会同时有效
给另一类型成员赋值时,之前数据会被冲掉
联合体所占字节为成员中所占内存最大那个
枚举类型
C++ 11标准之前,枚举可以与整数类型直接隐含转换 属于弱类型枚举
C++11之后出现强类型枚举
enum class 枚举类型名:底层类型{枚举值列表} //底层类型缺省默认为int类型
强类型枚举优势
1.强作用域
2.转换限制
3.可以指定底层类型
标识符的作用域与可见性
作用域分类:
1.函数原型作用域
2.局部作用域(块作用域)
3.类作用域
4.文件作用域
5.命名空间作用域
函数原型作用域
函数原型中的参数,其作用域开始与"(",结束与")" //函数原型声明 不是定义函数
局部作用域
函数的形参 在块中定义的标识符(大括号{}),作用域自声明起,限与块中
类作用域
范围包括类体和成员函数体
文件作用域
开始于声明点, 结束于文件尾
可见性
如果某个标识符在外层中声明,且内层中没有同一标识符的声明,那么此标识符在内层中可见
如果某个标识符在外层中声明,且内层中声明同名标识符,那么外层的标识符不可见
void Print()
{
int i = 0;
if(1)
{
int i = 10;
printf(i) //结果会是10,这个i访问的是if中定义的标识符,外部的i 虽然在作用域中,但是不可见
}
printf(i) //结果为0
}
对象的生存期
对象的产生到结束的这段时间,叫做对象的生存期
静态生存期
这种生存期与程序的运行期相同,在文件作用域中声明的对象(全局变量) 跟静态对象
动态生存期
开始与程序执行到声明点 结束与命名该标识符的作用域结束处
数组的定义与使用
int a[10]
数组下标从0开始
数组的存储
数组元素在内存中顺序存放,地址是连续的,元素间地址相邻,对应逻辑顺序上也相邻
数组名是数组首元素的内存地址
数组名是一个常量,不能被赋值
数组的初始化
1.列出全部元素的初始化
static int a[10] = {0,1,2,3,4,5,6,7,8,9}
2.可以只给一部分元素指定初始值
static int a[10]={0,1,2,3,4,5}
3.给出全部初始值时可以不指定数组长度
static int a[]={0,1,2,3,4,5,6,7,8,9}
二维数组 行优先存储
二维数组初始化
1.分行列出二维数组元素的初始值
static int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
2.可以对部分元素初始化
static int a[3][4]
数组作为函数参数
数组元素做实参,与单个变量一样
数组名做参数,传入首地址
基于范围的for循环
for(int &e : array)
{
e+=2; //e就是array[i]
}
指针
指针表示是存储地址的变量,本身也占字节,指针为什么需要带类型,因为指针做指针运算(寻址)时需要知道连续取多少字节,比如int 就连续取4个字节
指针的定义
int i = 10;
int* p = &i;
指针运算与地址运算
*p = 10; //寻找p中存储的内存地址,然后将地址中指向的内存单元存入数据10
p = &i; //把变量i的地址返回给指针变量
指针运算与地址运算 互为相反
指针变量的赋值
语法形式 指针变量 = 地址
地址中指向的类型必须与指针变量类型相符
向指针变量赋值必须是地址常量或变量,不能是普通整数
可以赋值的
通过地址运算& 求得的已定义的变量和对象的初始地址
动态分配成功时返回的地址(new)
整数0可以赋值给指针,表示空指针(nullptr)
允许定义void 类型的指针,该指针可以被赋予任意类型的地址,但此指针不能做指针运算(因为无法知道内存大小,无法取值)
指向常量的指针(只读)
const int* p =&i;
p = &u; //合法,可以修改指针变量中指向的地址
*p = 10; //非法,这里是尝试修改常量的值
常量指针
int* const p = &i;
常量指针初始化后无法修改指针变量中指向的地址
p = &u; //非法
*p = 10; //合法
指针的算数运算
指针的加减,指针的加减并不是直接加减多少个字节的地址,而是加上N*指针类型所占字节数个字节
运算结果总是指向一个完整的数据起始
指针的++ -- 是指向下一个或者上一个完整数据的起始
当指针指向连续存储的同类型数据时,指针与整数的加减才有意义(比如在数组中代替索引等)
指针的关系运算
同类型的指针可以进行关系运算
p==i //p i 都为同类型指针
指针还可以与0 做关系运算 p == 0//判空











网友评论