地址:https://space.bilibili.com/477729104
bool类型
- C++中的新类型(bool:0为假,非0为真)
- 占用的字节数(bool:1,BOOL:4)
- bool类型正确的使用
const常量
- const用于修饰变量,将变量变为常量(常量一旦定义,就不可以修改)
- 编译器在程序的编译时期做的检查
默认参数
函数允许提供默认参
默认参可以写在声明或者定义处,但只能出现在一个地方,一般写在声明处
当一个参数有默认参时,该参数的右边必须都出现默认参
- 使用宏定义
#define TESTFoo(m,n,k) TestFoo(m,n,k,1,2,3)
int TestFoo(int n1, int n2, int n3, int n4, int n5, int n6)
{
return 0;
}
此时,TestFoo内n4, n5, n6的值将会被宏定义中的1,2,3所代替。
- 也可以采用以下方式
#define TESTFoo(m,n,k) TestFoo(m,n,k,1,2,3)
int TestFoo(int n1, int n2, int n3, int n4 = 4, int n5 = 5, int n6 = 6)
{
return 0;
}
此时,n4, n5, n6的值将由函数内部的形参决定,即值为4,5,6
- 当采用以下方式时
#define TESTFoo(m,n,k) TestFoo(m,n,k,1,2,3)
int TestFoo(int n1, int n2, int n3, int n4 = 4, int n5 = 5, int n6 = 6)
{
return 0;
}
int main()
{
TestFoo(1,2 ,3, 44);
return 0;
}
此时,n4的取值将有main函数内部的定义决定,即值为44.
内联函数
- inline是对编译器的建议
- debug版本没有inline,方便调试
- 内联函数必须写在头文件中
- 内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。
引用
向函数传递参数的引用调用方法,把引用的地址复制给形式参数。在函数内,该引用用于访问调用中要用到的实际参数。修改形式参数会影响实际参数。
按引用传递值,参数引用被传递给函数,就像传递其他值给函数一样。因此相应地,在下面的函数 swap() 中,您需要声明函数参数为引用类型,该函数用于交换参数所指向的两个整数变量的值。
例子如下:
// 函数定义
void swap(int &x, int &y)
{
int temp;
temp = x; /* 保存地址 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 x 赋值给 y */
return;
}
实例:
#include <iostream>
using namespace std;
// 函数声明
void swap(int &x, int &y);
int main ()
{
// 局部变量声明
int a = 100;
int b = 200;
cout << "交换前,a 的值:" << a << endl;
cout << "交换前,b 的值:" << b << endl;
/* 调用函数来交换值 */
swap(a, b);
cout << "交换后,a 的值:" << a << endl;
cout << "交换后,b 的值:" << b << endl;
return 0;
}
引用(左值)
- 引用在定义时必须初始化,不能用常量为引用赋值
- 只有常量引用可以使用常量来初始化
- 引用的关系一旦建立则无法修改
- 引用的使用:1)作为函数参数;2)作为函数返回值
- 不存在二级引用,可以使用指针的引用来替代二级指针
引用的本质:编译器产生的关联对应的存储器地址常量指针
C++作用域
- 全局作用域 名字空间域(namespace)
- 局部作用域 块作用域
- 类域(class)
名字空间
- 一种作用域的划分,通常用于区分项目中的模块或组件
使用方法
- 关键字namespace,可以分开使用
- 名字空间可以嵌套
- 名字空间可以取别名
引用名字空间的方法
数据隐藏
- 在不同的作用域可以定义多个相同名字的变量
- 内部的变量会隐藏外部的变量(从内->外查找)
名字空间的引用
- 直接使用名字空间::内容 Shellmad::n(建议用法)
- 声明名字空间 using namespace ShellMad;
- 声明只使用名字空间的部分变量或函数usingShellMad::n;
- ::n表示使用全局域中的n
名字空间的原理
- .c/.cpp --cl.exe --> .obj --linker.exe-->exe
函数重载
C++中允许出现同名的函数
重载的条件(函数要素:函数名,函数参数,函数返回值,函数的调用约定)
函数名必须相同
函数参数个数不同,类型不同,顺序不同
函数返回值,调用约定不做考虑
作用域相同
函数重载的调用规则
根据函数名找对应的函数,作为候选函数。
1.1如果候选函数个数为0,则报未定义错误(找不到标识符)
候选函数个数 > 0,从候选中找匹配的函数(完全匹配,可以转换的匹配(char <->int,float <-> double))
2.1 如果匹配的函数个数 == 0,则错误(隐式转换失败)
2.2 如果匹配的函数个数 > 0,找最佳匹配。
最佳匹配的个数 = 1,就会调用该函数
最佳匹配的个数 > 1,就会报二义性
面向对象及类
- 面向对象编程介绍:对象=数据(数据类型)+行为(函数)
类的介绍
- 面向对象语言的三大特点:封装、继承、多态
- C++中对结构体的扩展:允许在结构体中写函数
类
- 3种访问权限:对结构体/类中的数据或函数的访问加以限制
public:公有权限,可以随意访问(类的里面,类的外面)
protected:保护权限,与继承有关,(限制儿子可以访问)
private:私有权限,自己(类的里面)可以访问
- 结构体与类的区别:默认访问权限不同
结构体:默认公有public
类:默认私有private
通常数据私有,部分成员函数公有(接口函数)
对于访问权限的检查是编译期而不是运行期
类大小
类的大小与结构体大小一样(结构体的对齐方式)(暂时)
同一个类的对象,其成员函数地址是一样的,表示同一个类的对象的成员函数是共用的:
数据是独立的 成员函数是共用的
类成员函数指针(_thiscall _stdcall _cdcel _fastcall)
定义类的成员函数指针
typedef int (_thiscall CClock::*PFN_GETHOUR)(void);
成员函数是用来暴露给外面的调用者使用
this指针
成员函数调用时会偷偷的传递this指针,通过寄存器ecx传递,这种传递方式称之为thiscall
成员函数指针的定义
类名::成员函数名 &tagClock::SetTime
成员函数指针的使用
对象调用:(cli.*pfn_class)(1,2,3);
对象指针调用:(pCL->*pfn_class)(1,2,3);
构造函数
由编译器在合适的时机调用
用于初始化对象的成员
构造函数不允许定义返回值
构造函数可以有参数,支持默认参(可以有多个参数,也可以没有参数)
- 仅有一个参数时,该函数既表示构造函数,又表示一种隐式转换从而支持:CTest obj = 1,可以使用关键字explicit表示只支持显示调用构造函数,不允许隐式转换。
- 支持函数重载
- 如果不重载构造函数,编译器会提供一个无参的默认构造函数
CTest() = default //表示使用默认的构造函数 CTest() = delete //表示禁止使用某函数(删除)。
构造函数可以使用初始化列表来初始化成员
CTest(int n,int k) :m_n(n), m_k(k)
构造函数通常不要手动调用(t1.CTest::CTest(2, 3);)
析构函数
- 用于资源的反初始化,来完成资源的释放
- 编译器在合适的时机调用(对象不在使用)
- 析构函数不能有参数和返回值,不能重载
- 析构函数的写法:
- 类名前加上~
- 通常是由编译器决定调用时机,不需要手动调用
访问权限
- 构造函数和析构函数通常是公有访问权限
构造函数和析构函数
- 栈对象调用时机
- 全局对象调用时机
- 作为参数和返回值的调用时机
- 构造函数和析构函数的调用时机
- 栈上的局部对象调用时机:
- 构造:声明该对象时构造
- 析构:对象出作用域时调用析构
- 全局对象的调用时机
- 构造:进入到main函数之前
- 析构:出main函数之后
- 栈上的局部对象调用时机:
拷贝构造函数
- 拷贝构造函数的作用:本质上也是一种构造函数
- 拷贝构造函数的调用时机:当用一个对象创建另外一个对象赋值时调用
- 缺省的拷贝构造的作用(完全地把对象1拷贝给对象2,memcpy)
- 何时需要重写拷贝构造函数:当对象中的成员函数存在一种需要分配的资源时,为了避免在析构时重复释放,需要重写拷贝构造函数或禁用。
堆对象
运算符:
- new 分配空间,调用构造函数
- delete 调用析构函数,释放空间
对基本的数据类型而言,new与delete仅仅分配内存空间。
new[ ] 与delete [ ]
- 当申请一个堆上的对象 时,使用new 和delete,不能用malloc和free替换。
- new [ ] 分配数组,delete[ ] 释放数组空间。
- new[ ]与delete[ ] 要配套使用(特别是申请对象数组时)
- vs编译器会在new[ ] 申请对象数组时,在堆开始的前4个字节写入当前数组的长度,用于delete[ ]释放时,析构调用的次数。
类的派生与继承
面向对象:继承
继承的可见性
保护成员(protected):保护成员除了自身或者派生类以外,不能在其他类外使用。
父类(CPerson) <--公有继承public-- 子类(CStudent) 公有成员(public) 公有成员(public) 保护成员(protected) 保护成员(protected) 私有成员(private) 不可见 父类 <--保护继承protected-- 子类 公有成员(public) 保护成员(protected) 保护成员(protected) 保护成员(protected) 私有成员(private) 不可见 父类 <--私有继承private-- 子类 公有成员(public) 私有成员(private) 保护成员(protected) 私有成员(private) 私有成员(private) 不可见 继承的可见性在何时做检查:由编译器在编译时刻做的限制
指针转换的安全性:
- 子类指针转换为父类指针(安全)
- 父类指针转换为子类指针(不安全)
派生类的构造顺序:
- 先父类
- 后子类
派生类的析构顺序:
- 先子类
- 后父类
成员类的构造顺序:
- 先成员类
- 后自己
成员类的析构顺序:
- 先自己
- 后成员类
初始化列表
- 用于调用父类的有参构造
- 对于自身成员的初始化
- 对于常量成员的初始化
函数隐藏
- 需要的条件:
- 作用域不同
- 函数名相同
- 参数列表和调用约定,返回值均不考虑
多态
虚函数原理
- 虚函数的调用方法是间接调用(先查虚表地址,再查虚表中的虚函数指针)
- 增加了虚函数virtual关键字的对象头部4个字节是虚表地址(某些情况单继承)。