const,static关键字,extern“C“用法

static

考虑类的情况

static成员变量

  • 只与类关联,不与类的对象关联,对于类的所有对象只有一份拷贝
  • 定义时分配空间
  • 不能在类声明中初始化,必须在类定义外部初始化
  • 初始化的时候不需要标示为static,可以被非static成员函数任意访问
  • 为什么类的静态成员变量需要在类定义外进行初始化?
  1. 静态成员变量是类的属性,与任何具体对象实例无关,所以内存在程序启动的时候就已经分配好了,如果在类定义中初始化静态成员变量,就相当与在定义类的时候就要为其分配内存空间并且初始化,这时不合理的。(static修饰的变量先于对象存在)
  2. 如果将静态成员变量的初始化放在类定义中,那么每个包含该头文件的文件都会有一个静态成员变量的定义,这样就会出现重复定义的错误。

static成员函数

  • 不具有this指针

因为static修饰的类成员属于类不属于对象所以没有this指针,所以static类成员函数不能访问非static的类成员

  • 无法访问类对象的非static成员变量和非static成员函数
  • 不能被声明为const,虚函数和volatile

const成员函数的作用是不改变对象的状态,但是静态成员函数不属于任何具体对象,没有意义也不被允许;虚函数需要通过运行时类型信息来选择调用哪个函数,但是静态函数不与任何对象关联,因此不属于类的对象,没有运行时类型信息可供选择(还有一个原因,静态成员函数没有this指针,虚函数的实现是为每一个对象分配一个虚表指针而虚表指针是通过this指针调用的,所以不能用virtual);volatile关键字用于指示变量可能被意外修改,从而告诉编译器不需要进行某些优化,但是静态成员函数不属于对象,不涉及任何变量(只能访问静态成员变量和其它静态成员函数),没必要声明为volatile。

  • 可以被非static 成员函数任意访问。

静态成员与普通成员的区别

生命周期

静态成员变量从类被加载开始到类被卸载,一直存在。普通成员变量只有在类创建对象之后才开始存在,对象结束,生命周期就结束。

类的加载和卸载:类是在程序运行的时候动态加载的。
类的加载通常发生在以下几种情况下

  • 当创建类的对象时:当程序执行到创建类的对象的语句时,编译器会根据类的定义,在内存中为该对象分配空间并初始化。
  • 当调用类的静态成员或静态函数时:静态成员在程序启动时会被加载到内存中,并且在整个程序执行期间一直存在。
  • 当调用类的成员函数时:成员函数的定义通常在类的声明中,但是实际的函数代码并不会在类加载时被加载,而是在调用函数时才会被加载。

类的卸载是在以下情况发生的

  • 当对象的生命周期结束时:当一个对象超出其作用域,或者通过delete关键字释放动态分配的内存,对象会被销毁并从内存中卸载。
  • 当程序终止时:在程序结束时,所有已加载的类和对象都会被卸载,内存会被释放。

类的加载和卸载是由编译器和操作系统自动管理的。

共享方式

静态变量是全类共享,普通成员变量是每个对象单独享用的。

定义位置

普通成员变量存储在堆或者栈中,而静态成员变量存储在静态全局区。

初始化位置

普通成员在类中初始化,静态成员变量在类外初始化

默认实参

可以使用静态成员变量作为默认实参

不考虑类的情况

隐藏

只能在该文件所在的编译模块中使用

默认初始化0

包括未初始化的全局静态变量与局部静态变量,都存在全局未初始化区【这个区域在编译好的目标文件中不被分配内存(因为默认为0不需要存储大量零值),只是记录所需要的大小,它位于数据段和栈区之间】。其实全局变量也具备这一属性,因为全局变量也存储在静态数据区

静态变量在函数内定义,始终存在

在函数内部定义就可以控制作用域。只需要进行一次初始化(在程序刚开始运行的时候就完成初始化),具有记忆性,作用范围与局部变量相同,函数退出后仍然存在,但是不能使用
一共又两种变量存储在静态存储区:全局变量和static变量,但是static可以控制变量的可见范围。

静态变量什么时候初始化

  • 在主程序之前,编译器已经为其分配好了内存。静态局部变量和全局变量一样都存放在全局区域,所以在主程序之前,编译器已经为其分配好了内存。
  • C和C++中静态局部变量的初始化节点不太一样:
    • C语言中初始化发生在代码执行之前,编译阶段分配好内存之后就会进行初始化。所以C语言中无法使用变量对静态局部变量进行初始化,程序运行结束的时候,变量所处的全局内存会被回收。
    • 在C++中,初始化的时候在执行相关代码的时候才会进行初始化,因为C++引入了类这个概念,在构造函数与析构函数中经常会需要进行一些特定操作而不是简单的分配内存。所以C++标准定位全局或者静态对象是有首次用到时才会进行构造,通过atexit()来管理。在程序结束的时候,按照构造顺序反方向进行析构。所以C++中式可以使用变量对静态局部变量进行初始化的

全局变量和static变量的区别

  • 全局变量本身就是静态存储方法,静态全局变量也是静态存储的方式。非静态全局变量作用域是整个源程序,当一个源程序由多个源文件组成的时候,非静态全局变量在各个源文件中都有效。而静态全局变量限制了作用域,只是在定义该变量的源文件内有效。
  • static全局变量与普通的全局变量的区别是static全局变量只是初始化一次,防止在其它文件单元被引用。

const

不考虑类

  • 定义的时候初始化,之后无法修改
  • const形参可以接收const和非const类型的形参
  • 在一个函数声明中,const可以修饰形参表明它是一个输入参数在函数内部不能改变其值
  • const类型变量可以通过类型转换符const_cast将const类型转换为非const类型

考虑类

const成员变量

  • 不能在类定义外部初始化,只能通过构造函数初始化列表进行初始化,必须有构造函数
  • 不同类对其const数据成员的值可以不同,所以不能在类中声明的时候初始化
  • const成员变量也有隐藏作用
  • const类型变量必须定义的时候进行初始化,因此也导致如果类的成员变量有const类型的变量,那么该变量必须在类的初始化列表中进行初始化
  • 只有引用传递和指针传递可以用是否加const来重载。

编译器在编译阶段通过匹配函数名称和参数列表来确定调用哪个函数。但是,顶层const对编译器来说是透明的,即编译器在函数选择时无法区分一个带有顶层const的形参和另一个没有顶层const的形参。因此,如果存在两个函数定义,一个具有顶层const的形参,另一个没有顶层const的形参,编译器将无法区分它们,从而导致函数重载冲突。
然而,当形参是引用传递或指针传递时,情况会有所不同。在这种情况下,编译器可以根据实参是否具有底层const来选择正确的函数。因为指针传递和引用传递都可以修改所指向的对象,所以在函数重载中通过是否加上底层const来区分形参是很有意义的

const成员函数

  • const对象不可以调用非const成员函数(因为一个没有明确被声明为const的成员函数被看作是将要修改对象中数据成员的函数,而且编译器不允许它为一个const对象所调用),非const对象都可以调用
  • 不可以改变非mutable数据的值
  • 对于类的成员函数,若指定其为const类型,则表明是一个常函数,不能修改类的成员变量,类的常对象只能访问类的常成员函数。
  • 对于类的成员函数,有时候必须指定返回值为const类型,以使得其返回值不为左值
  • const成员函数可以访问非const对象的非const数据成员,const数据成员,也可以访问cosnt对象内的所有数据成员;非const成员函数可以访问非const对象的非const数据成员、const数据成员,但是不可以访问const对象的任意数据成员

顶层const和底层const

  • 顶层const:const修饰的变量是一个常量(指的是指针),const在*右边
  • 底层const: const修饰的变量所指的对象是一个常量(指的是所指的变量),const在*左边
  • 常量的底层const不能赋值给非常量的底层const
  • 使用命名的强制类型转换函数const_cast的时候,只能改变运算对象的底层const

extern “C”

加上extern "C"之后,相当于告诉程序这段代码是C语言写的,所以按照C语言进行编译。但是不能在C语言中用这种方法。

  • C++调用C函数
//xx.h
extern int add(...)

//xx.c
int add(){
    
    
    
}

//xx.cpp
extern "C" {
    
    
    #include "xx.h"
}

  • C调用C++
//xx.h
extern "C"{
    
    
    int add();
}
//xx.cpp
int add(){
    
        
}
//xx.c
extern int add();

猜你喜欢

转载自blog.csdn.net/qaaaaaaz/article/details/130734680