C++相关

243 阅读7分钟

const

用于常量,节省空间,必须初始化 类型检查:与#define相比 const对象为文件局部变量,要在其它文件中访问必须声明为extern 常量指针 const在的左侧,可以不初始化,不能通过指针修改对象的值 指针常量 const在的右侧,无法直接修改 const修饰函数参数,防止函数内修改,const引用也可以 类中使用const:不会修改数据成员的函数都应该声明为const类型

static

静态变量: 函数中的变量,类中的变量

当变量声明为static时,空间将在程序的生命周期内分配。
类中静态变量只被初始化一次,在静态区分配空间,仅有一个副本,不能使用构造函数初始化

静态类的成员: 类对象和类中的函数

对象为静态生命周期贯穿进程的周期
静态函数:不依赖对象,可以使用::访问

对象与对象之间的成员变量是相互独立的。要想共用数据,则需要使用静态成员和静态方法。
只要在类中声明静态成员变量,即使不定义对象,也可以为静态成员变量分配空间,进而可以使用静态成员变量。(因为静态成员变量在对象创建之前就已经被分配了内存空间)
静态成员变量虽然在类中,但它并不是随对象的建立而分配空间的,也不是随对象的撤销而释放(一般的成员在对象建立时会分配空间,在对象撤销时会释放)。静态成员变量是在程序编译时分配空间,而在程序结束时释放空间。
静态成员的定义和声明要加个关键static。静态成员可以通过双冒号来使用,即class::static。
初始化静态成员变量要在类的外面进行。初始化的格式如下:数据类型  类名::静态成员变量名 = 初值;
不能用参数初始化表,对静态成员变量进行初始化。
既可以通过类名来对静态成员变量进行引用,也可以通过对象名来对静态成员变量进行引用。
普通成员函数和静态成员函数的区别是:普通成员函数在参数传递时编译器会隐藏地传递一个this指针.通过this指针来确定调用类产生的哪个对象;但是静态成员函数没有this指针,不知道应该访问哪个对象中的数据,所以在程序中不可以用静态成员函数访问类中的普通变量.

this

类似python中的self 一个对象的this指针并不是对象本身的一部分,不会影响sizeof(对象)的结果。 this作用域是在类内部,当在类的非静态成员函数中访问类的非静态成员的时候,编译器会自动将对象本身的地址作为一个隐含参数传递给函数。也就是说,即使你没有写上this指针,编译器在编译的时候也是加上this的,它作为非静态成员函数的隐含形参,对各成员的访问均通过this进行。 其次,this指针的使用: 在类的非静态成员函数中返回类对象本身的时候,直接使用 return *this。 当参数与成员变量名相同时,如this->n = n (不能写成n = n)。

inline

提高函数效率,省去函数调用的开销。复杂函数不适合 虚函数可以是内联函数,但是当虚函数表现多态性的时候不能内联。

sizeof

空类的大小为1字节
一个类中,虚函数本身、成员函数(包括静态与非静态)和静态数据成员都是不占用类对象的存储空间。
对于包含虚函数的类,不管有多少个虚函数,只有一个虚指针,vptr的大小。
普通继承,派生类继承了所有基类的函数与成员,要按照字节对齐来计算大小
虚函数继承,不管是单继承还是多继承,都是继承了基类的vptr。(32位操作系统4字节,64位操作系统 8字节)!
虚继承,继承基类的vptr。

函数指针

纯虚函数

没有实现的虚函数 用 =0 来标识纯虚函数。只能用于继承,接口声明。有纯虚函数的类称为抽象类 一个类从抽象类派生而来,必须实现基类中的所有纯虚函数,才能成为非抽象类

虚函数表和虚函数指针

static成员函数不属于任何类对象或类实例,不可以声明为虚函数,不能被const和volatile修饰 构造函数不可以声明为虚函数。同时除了inline之外,构造函数不允许使用其它任何关键字。 析构函数一般声明为虚析构函数

volatile

与寄存器有关,告诉编译器不能优化,被声明的变量每次访问时都应该从内存中获取。

explicit

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

extern

c++代码中链接c函数

struct和class

C C++
不能将函数放在结构体声明 能将函数放在结构体声明
在C结构体声明中不能使用C++访问修饰符。 public、protected、private 在C++中可以使用。
在C中定义结构体变量,如果使用了下面定义必须加struct。 可以不加struct
结构体不能继承(没有这一概念)。 可以继承
若结构体的名字与函数名相同,可以正常运行且正常的调用 若结构体的名字与函数名相同,使用结构体,只能使用带struct定义!
最本质的一个区别就是默认的访问控制
默认的继承访问权限。struct 是 public 的,class 是 private 的。
struct 作为数据结构的实现体,它默认的数据访问控制是 public 的,而 class 作为对象的实现体,它默认的成员变量访问控制是 private 的。

using

取代typedef,起别名 改变访问性

::

全局作用域符(::name):用于类型名称(类、类成员、成员函数、变量等)前,表示作用域为全局命名空间 类作用域符(class::name):用于表示指定类型的作用域范围是具体某个类的 命名空间作用域符(namespace::name):用于表示指定类型的作用域范围是具体某个命名空间的

decltype

decltype的作用是“查询表达式的类型”

引用和指针

引用 指针
必须初始化 可以不初始化
不能为空 可以为空
不能更换目标 可以更换目标

左值引用:常规引用,一般表示对象的身份。 右值引用:右值引用就是必须绑定到右值(一个临时对象、将要销毁的对象)的引用,一般表示对象的值。 右值引用可实现转移语义(Move Sementics)和精确传递(Perfect Forwarding),它的主要目的有两个方面: 消除两个对象交互时不必要的对象拷贝,节省运算存储资源,提高效率。 能够更简洁明确地定义泛型函数。

算法

Knuth洗牌算法

for(int i = n - 1; i >= 0 ; i -- )
    swap(arr[i], arr[rand(0, i)]) // rand(0, i) 生成 [0, i] 之间的随机整数

对于生成的排列,每一个元素都能独立等概率地出现在每一个位置。或者反过来,每一个位置都能独立等概率地放置每个元素。

蓄水池算法

int[] reservoir = new int[m];

// init
for (int i = 0; i < reservoir.length; i++)
{
    reservoir[i] = dataStream[i];
}

for (int i = m; i < dataStream.length; i++)
{
    // 随机获得一个[0, i]内的随机整数
    int d = rand.nextInt(i + 1);
    // 如果随机整数落在[0, m-1]范围内,则替换蓄水池中的元素
    if (d < m)
    {
        reservoir[d] = dataStream[i];
    }
}
  1. 如果接收的数据量小于m,则依次放入蓄水池。
  2. 当接收到第i个数据时,i >= m,在[0, i]范围内取以随机数d,若d的落在[0, m-1]范围内,则用接收到的第i个数据替换蓄水池中的第d个数据。
  3. 重复步骤2。