C++笔记(from B站玄马科技)

地址:https://space.bilibili.com/477729104

bool类型

  • C++中的新类型(bool:0为假,非0为真)
  • 占用的字节数(bool:1,BOOL:4)
  • bool类型正确的使用

const常量

  • const用于修饰变量,将变量变为常量(常量一旦定义,就不可以修改)
  • 编译器在程序的编译时期做的检查

默认参数

  • 函数允许提供默认参

  • 默认参可以写在声明或者定义处,但只能出现在一个地方,一般写在声明处

  • 当一个参数有默认参时,该参数的右边必须都出现默认参

  1. 使用宏定义
#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所代替。

  1. 也可以采用以下方式
#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

  1. 当采用以下方式时
#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. 只有常量引用可以使用常量来初始化
  3. 引用的关系一旦建立则无法修改
  4. 引用的使用:1)作为函数参数;2)作为函数返回值
  5. 不存在二级引用,可以使用指针的引用来替代二级指针

引用的本质:编译器产生的关联对应的存储器地址常量指针

C++作用域

  1. 全局作用域 名字空间域(namespace)
  2. 局部作用域 块作用域
  3. 类域(class)

名字空间

  • 一种作用域的划分,通常用于区分项目中的模块或组件

使用方法

  • 关键字namespace,可以分开使用
  • 名字空间可以嵌套
  • 名字空间可以取别名

引用名字空间的方法

数据隐藏

  • 在不同的作用域可以定义多个相同名字的变量
  • 内部的变量会隐藏外部的变量(从内->外查找)

名字空间的引用

  1. 直接使用名字空间::内容 Shellmad::n(建议用法)
  2. 声明名字空间 using namespace ShellMad;
  3. 声明只使用名字空间的部分变量或函数usingShellMad::n;
  4. ::n表示使用全局域中的n

名字空间的原理

  • .c/.cpp --cl.exe --> .obj --linker.exe-->exe

函数重载

C++中允许出现同名的函数

重载的条件(函数要素:函数名,函数参数,函数返回值,函数的调用约定)

  1. 函数名必须相同

  2. 函数参数个数不同,类型不同,顺序不同

  3. 函数返回值,调用约定不做考虑

  4. 作用域相同

函数重载的调用规则

  1. 根据函数名找对应的函数,作为候选函数。

    1.1如果候选函数个数为0,则报未定义错误(找不到标识符)

  2. 候选函数个数 > 0,从候选中找匹配的函数(完全匹配,可以转换的匹配(char <->int,float <-> double))

    2.1 如果匹配的函数个数 == 0,则错误(隐式转换失败)

    2.2 如果匹配的函数个数 > 0,找最佳匹配。

  3. 最佳匹配的个数 = 1,就会调用该函数

    最佳匹配的个数 > 1,就会报二义性

面向对象及类

  1. 面向对象编程介绍:对象=数据(数据类型)+行为(函数)

类的介绍

  1. 面向对象语言的三大特点:封装、继承、多态
  2. C++中对结构体的扩展:允许在结构体中写函数

  1. 3种访问权限:对结构体/类中的数据或函数的访问加以限制
public:公有权限,可以随意访问(类的里面,类的外面)
protected:保护权限,与继承有关,(限制儿子可以访问)
private:私有权限,自己(类的里面)可以访问        
  1. 结构体与类的区别:默认访问权限不同
结构体:默认公有public
类:默认私有private
  1. 通常数据私有,部分成员函数公有(接口函数)

  2. 对于访问权限的检查是编译期而不是运行期

类大小

  1. 类的大小与结构体大小一样(结构体的对齐方式)(暂时)

  2. 同一个类的对象,其成员函数地址是一样的,表示同一个类的对象的成员函数是共用的:

    数据是独立的
    成员函数是共用的
  3. 类成员函数指针(_thiscall _stdcall _cdcel _fastcall)

  4. 定义类的成员函数指针

    typedef int (_thiscall CClock::*PFN_GETHOUR)(void);
  5. 成员函数是用来暴露给外面的调用者使用

this指针

成员函数调用时会偷偷的传递this指针,通过寄存器ecx传递,这种传递方式称之为thiscall

成员函数指针的定义

类名::成员函数名   &tagClock::SetTime

成员函数指针的使用

对象调用:(cli.*pfn_class)(1,2,3);
对象指针调用:(pCL->*pfn_class)(1,2,3);

构造函数

  1. 由编译器在合适的时机调用

  2. 用于初始化对象的成员

  3. 构造函数不允许定义返回值

  4. 构造函数可以有参数,支持默认参(可以有多个参数,也可以没有参数)

    1. 仅有一个参数时,该函数既表示构造函数,又表示一种隐式转换从而支持:CTest obj = 1,可以使用关键字explicit表示只支持显示调用构造函数,不允许隐式转换
    2. 支持函数重载
    3. 如果不重载构造函数,编译器会提供一个无参的默认构造函数
    CTest() = default    //表示使用默认的构造函数
    CTest() = delete //表示禁止使用某函数(删除)。
  5. 构造函数可以使用初始化列表来初始化成员

    CTest(int n,int k)
        :m_n(n), m_k(k)
  6. 构造函数通常不要手动调用(t1.CTest::CTest(2, 3);)

析构函数

  1. 用于资源的反初始化,来完成资源的释放
  2. 编译器在合适的时机调用(对象不在使用)
  3. 析构函数不能有参数和返回值,不能重载
  4. 析构函数的写法:
    1. 类名前加上~
    2. 通常是由编译器决定调用时机,不需要手动调用

访问权限

  1. 构造函数和析构函数通常是公有访问权限

构造函数和析构函数

  1. 栈对象调用时机
  2. 全局对象调用时机
  3. 作为参数和返回值的调用时机
  4. 构造函数和析构函数的调用时机
    1. 栈上的局部对象调用时机:
      1. 构造:声明该对象时构造
      2. 析构:对象出作用域时调用析构
    2. 全局对象的调用时机
      1. 构造:进入到main函数之前
      2. 析构:出main函数之后

拷贝构造函数

  1. 拷贝构造函数的作用:本质上也是一种构造函数
  2. 拷贝构造函数的调用时机:当用一个对象创建另外一个对象赋值时调用
  3. 缺省的拷贝构造的作用(完全地把对象1拷贝给对象2,memcpy)
  4. 何时需要重写拷贝构造函数:当对象中的成员函数存在一种需要分配的资源时,为了避免在析构时重复释放,需要重写拷贝构造函数或禁用。

堆对象

运算符:

  1. new 分配空间,调用构造函数
  2. delete 调用析构函数,释放空间

对基本的数据类型而言,new与delete仅仅分配内存空间。

new[ ] 与delete [ ]

  1. 当申请一个堆上的对象 时,使用new 和delete,不能用malloc和free替换。
  2. new [ ] 分配数组,delete[ ] 释放数组空间。
  3. new[ ]与delete[ ] 要配套使用(特别是申请对象数组时)
  4. vs编译器会在new[ ] 申请对象数组时,在堆开始的前4个字节写入当前数组的长度,用于delete[ ]释放时,析构调用的次数。

类的派生与继承

  1. 面向对象:继承

  2. 继承的可见性

    保护成员(protected):保护成员除了自身或者派生类以外,不能在其他类外使用。

    父类(CPerson) <--公有继承public-- 子类(CStudent)
    公有成员(public) 公有成员(public)
    保护成员(protected) 保护成员(protected)
    私有成员(private) 不可见
    父类 <--保护继承protected-- 子类
    公有成员(public) 保护成员(protected)
    保护成员(protected) 保护成员(protected)
    私有成员(private) 不可见
    父类 <--私有继承private-- 子类
    公有成员(public) 私有成员(private)
    保护成员(protected) 私有成员(private)
    私有成员(private) 不可见
  3. 继承的可见性在何时做检查:由编译器在编译时刻做的限制

  4. 指针转换的安全性:

    1. 子类指针转换为父类指针(安全)
    2. 父类指针转换为子类指针(不安全)
  5. 派生类的构造顺序:

    1. 先父类
    2. 后子类
  6. 派生类的析构顺序:

    1. 先子类
    2. 后父类
  7. 成员类的构造顺序:

    1. 先成员类
    2. 后自己
  8. 成员类的析构顺序:

    1. 先自己
    2. 后成员类

初始化列表

  1. 用于调用父类的有参构造
  2. 对于自身成员的初始化
  3. 对于常量成员的初始化

函数隐藏

  1. 需要的条件:
    1. 作用域不同
    2. 函数名相同
    3. 参数列表和调用约定,返回值均不考虑

多态

虚函数原理

  1. 虚函数的调用方法是间接调用(先查虚表地址,再查虚表中的虚函数指针)
  2. 增加了虚函数virtual关键字的对象头部4个字节是虚表地址(某些情况单继承)。

猜你喜欢

转载自www.cnblogs.com/zonkidd/p/12329629.html