C++系列-基础

数据类型

基本类型

  • 整型(int) 4字节
  • 单精度浮点型(float) 4字节
  • 双精度浮点型(double) 8字节
  • 布尔型(bool) 1字节
  • 字符型(char) 1字节
  • 无类型(void)
  • 宽字符型(wchar_t) 2或4字节 typedef short int wchar_t;

扩展类型

  • 枚举类型(enum)
  • 结构体(struct)
  • 联合体(union)

基本类型可以使用一个或多个修饰符进行修饰,如:signed unsigned short long

  • signedunsigned不改变类型空间大小
  • short修饰占用空间为2个字节
  • long增加为2倍字节大小
  • 可以使用sizeof()获取字节长度,但是需要清楚了解每个大小,面试经常问

枚举类型

限定作用域

enum class open_modes {input, output, append};

不限定作用域

enum color{red, yellow, green};
enum {floatPrec = 6, doublePrec = 10};

结构体

structtypedef struct

typedef struct Student {
    int age;
} S;

等价于

struct Student {
    int age;
};
typedef struct Student S;

structclass

  • 继承访问权限:struct默认是public,class默认是private
  • 内部成员访问权限:structpublic,classprivate

字节对齐

各个硬件平台对存储空间的处理上有很大不同,保证字节对齐可以兼容各个硬件平台,但是会在存取效率上带来损失

内存对齐规则:

  • 结构体各个成员,第一个偏移量是0,排列在后面的成员其当前的偏移量必须是当前成员类型的整数倍
  • 结构体占用内存大小是体内最大数据成员的最小倍数
  • 如程序中有#pragma pack(n)预编译指令,所有成员对齐以n字节为准(偏移量是n的整数倍),不再考虑当前类型以及结构体内最大类型

具体可以参考内存对齐规则之我见

联合体

  • 默认访问控制为public
  • 可以含有构造函数和析构函数
  • 不能含有引用类型成员
  • 不能继承,不能被继承
  • 不能含有虚函数
  • 匿名union在定义所在作用域可直接访问成员
  • 匿名union不能包含protectedprivate成员
  • 全局匿名union必须是static

智能指针

  • shared_ptr
  • unique_ptr
  • weak_ptr
  • auto_ptr (C++11已弃用)

shared_ptr

  • 多个智能指针可以共享一个对象,对象最末一个拥有者有责任销毁对象
  • 支持定制型删除器,自动解除互斥锁

weak_ptr

  • 允许共享但不拥有某种对象,一旦最后一个失去了所有权,任何weak_ptr都会自动成空
  • 只拥有default copy 和接受shared_ptr的都构造函数
  • 可打破环状引用的问题

unique_ptr

  • 一种在异常时可以帮助避免资源泄露的智能指针

  • 采用独占式拥有,可以确保一个对象和其相应的资源同一时间只能被一个指针拥有

  • 一旦被销毁或还是拥有另一个对象,先前的资源都会被释放

  • 用于替代auto_ptr

  • auto_ptr 可以赋值拷贝;unique_ptr无拷贝赋值,但提供了move函数

  • auto_ptr 不能管理数组,析构调用delete; unique_ptr可以管理数组,析构调用delete[]

强制类型转换

  • static_cast
  • dynamic_cast
  • const_cast
  • reinterpret_cast

static_cast

  • 用于非多态类型转换
  • 不执行运行时类型检查,安全性不如dynamic_cast
  • 通常用于数值类型,如float -> int
  • 可以在整个类层次结构中移动指针,子类转换为父类安全,父类转子类不安全

dynamic_cast

  • 用于多态类型的转换
  • 执行运行时类型检查
  • 只适用于指针或引用
  • 对不明确的指针的转换将失败,但不引发异常
  • 可以在整个类层次结构中移动指针,包括向上转换,向下转换

const_cast

  • 用于删除const volatile __unaligned特性

关键字

const

  • 修饰变量,说明变量不可以被改变
  • 修饰指针,指向常量的指针(const char*)和指针常量(char* const)
  • 修饰引用,指向常量的引用,避免拷贝又避免函数对值修改
  • 修饰成员函数,该成员函数内部不能修改成员变量

被const修饰(在const后面)的值不可改变

static

  • 修饰普通变量,修改变量存储区和生命周期,使变量在静态区,在main函数运行前就分配了空间,默认会会初始化
  • 修饰普通函数,表明函数作用范围,仅定义该函数的文件内才能使用
  • 修饰成员变量,使所有的对象只保存一个该变量,不需要对象就可以访问该成员
  • 修饰成员函数,不需要对象就可以访问,但是static函数内不能访问非静态成员

this

  • this是一个隐含于每一个非静态成员函数中的一个特殊指针,指向调用该成员函数的那个对象
  • 每次调用成员函数时,编译程序先将对象的地址赋给this指针,然后调用成员函数,每次成员函数存取数据成员时,都隐式使用this指针
  • this指针被隐式声明为ClassName *const this,意味着不能给this赋值,不可修改
  • this是一个右值,不能取得this的地址
  • 需要显式引用this指针:对象的链式引用;避免同一对象进行赋值操作;实现一些数据结构时

inline

特征

  • 把内联函数里面的内容写在调用内联函数处
  • 不用执行进入函数的步骤,直接执行函数体
  • 相当于宏,比宏多了类型检查,具有函数特性
  • 编译器一般不内联包含循环、递归、switch等复杂操作的内联函数
  • 在类声明中定义的函数,除了虚函数的其他函数都会自动隐式当成内联函数

编译器处理

  • 编译器会把函数体复制到inline函数调用点处
  • 编译器为所用inline函数中的局部变量分配内存空间
  • 编译器将inline函数所有的输入参数和返回值映射到调用方法的局部变量空间中
  • 如果有多个返回点,编译器会将其变为inline函数代码块末尾的分支

优缺点

  • 省去了参数压栈、栈帧开辟与回收,提高运行速度
  • 会做安全检查,宏不会
  • 内联函数可以访问类的成员变量,宏不能
  • 内联函数运行时可调试,宏不能
  • 代码会膨胀
  • 是否内联,程序不可控。内联函数只是对编译器的建议,决定权在于编译器
  • 虚函数可以时内联函数,但虚函数表现多态性时不能内联(因为编译期内联,运行时多态)

volatile

  • 声明的类型变量可以被某些编译器未知的因素(操作系统、硬件、其它线程等)更改,所以使用volatile告诉编译器不应该对这样的对象进行优化
  • volatile声明的变量,每次访问时必须从内存中取值,没有被volatile修饰的变量,可能由于编译器的优化,从CPU寄存器取值
  • const可以是volatile 只读状态寄存器
  • 指针可以是volatile

assert

  • 是宏,而非函数
  • 如果条件返回错误,终止程序执行
  • 可以通过NDEBUG关闭assert,但需要在源代码的开头,include 之前

explicit

  • explicit修饰构造函数时,可以防止隐式转换和复制初始化
  • explicit修饰转换函数时,可以防止隐式转换,单按语境转换除外

friend

  • 能访问私有成员
  • 破坏封装性
  • 不可传递
  • 单向性
  • 声明形式及数量不受限制

using

  • using声明 引入命名空间的一个成员 using namespace_name::name;
  • using指示,使得特定命名空间的所有名字都可见 using namespace name;
  • 尽量少使用using指示,使用声明更安全且不会包含不需要的名称

decltype

  • 用于检查实体的声明类型或表达式的类型及值分类
template
auto fcn(It beg, It end) -> decltype(*beg)
{
    //todo
    return *beg;
}
template
auto fnc2(It beg, It end) -> typename remove_referenct::type
{
    //todo
    return *beg;
}

面向对象

封装

把客观失误封装成抽象的类,并且可以把变量和方法只让可信对象操作,对不可信对象进行隐藏。关键字public private protected.默认不写是private

  • public 任意对象可以访问
  • protected 子类和本类成员函数访问
  • private 本类,友元类或友元函数访问

继承

  • 基类 ----> 派生类

多态

消息以多种形式显示的能力,以封装和继承为基础的

  • 重载多态(编译期):函数重载,运算符重载
  • 子类型多态(运行期):虚函数
  • 参数多态(编译期):类模板,函数模板
  • 强制多态(运行期/编译期):基本类型转换,自定义类型转换

一般函数重载和虚函数两个比较最多

虚函数

注意

  • 普通函数不能是虚函数
  • 静态函数不能是虚函数
  • 构造函数不能是虚函数(因为在调用构造函数时,虚表指针并没有对象的内存空间,必须要构造函数调用完成后才会形成虚表指针)
  • 内联函数不能是表现多态时的虚函数
  • 模板类中可以使用虚函数
  • 类的模板成员不能时虚函数

虚析构函数

虚析构函数是为了剞劂基类的指针指向派生类对象,并用基类的指针删除派生类对象

纯虚函数

在基类中不能对虚函数给出有意义的实现,实现留给派生类去完成

  • 纯虚函数只是一个接口,要留到子类里实现;虚函数在声明的时候实现
  • 纯虚函数在子类里必须重写;虚函数可以不重写
  • 纯虚函数关注接口统一性,实现由子类完成;虚函数继承接口的同时也继承了父类的实现
  • 带虚函数的类叫抽象类,不能直接生成对象,只有被继承后并重写虚函数后才能被使用,子类可以是抽象类也可以是普通类

虚函数指针和虚函数表

  • 虚函数指针:在含有虚函数类的对象中,指向虚函数表,运行时确定
  • 虚函数表: 在程序只读数据段,存放虚函数指针,如果派生类实现了某个虚函数,则在虚表中覆盖原来基类的那个虚函数指针,在编译时根据类的声明创建

C++中虚函数(表)实现机制以及用C语言对其进行模拟实现

虚继承

  • 用于解决多继承条件下菱形继承问题(浪费存储空间,存在二义性)
  • 原理与编译器有关,一般通过虚基类指针虚基类表实现

每个虚继承的子类都有一个虚基类指针和虚基类表;当虚继承的子类被当作父类继承时,虚基类指针也会被继承;实际上虚基类表指针指向了一个虚基类表,虚表中记录了虚基类与本类的偏移地址,通过偏移地址就可以找到虚基类成员,虚继承不需要像普通多继承那样维持公共基类的两份拷贝,节省了存储空间

  • 虚继承和虚函数都利用了虚指针和虚表
  • 虚继承的虚基类依旧存在继承类中,占用存储空间;虚函数不会韩永存储空间
  • 虚继承的虚基类表存储的时虚基类相对于继承类的偏移;虚函数表存储的时虚函数地址

抽象类、接口类和聚合类

  • 抽象类:含有纯虚函数的类
  • 接口类:仅含有纯虚函数的类
  • 聚合类:可以直接访问其成员,并且具有特殊初始化语法形式。特点
    • 成员都是public
    • 没有定义任何构造函数
    • 没有类内初始化
    • 没有基类也没有虚函数

参考文献

C++总结
C++ Primer Edition 5

你可能感兴趣的:(C++系列-基础)