当前位置: 首页 > news >正文

C++关键字之const、inline、static

C++ 关键字总结

1.const

  • const是 constant 的缩写,本意是不变的、不易改变的意思。
  • 在C++中用来修饰内置类型变量,自定义对象,成员函数,返回值,函数参数
  • 使用如下:
//修饰普通类型变量
const int a =7;
int b=a; //true
a=8;  //false
  • 修饰指针变量
//1.const修饰指针指向的内容,则内容为不可变量
const int *p=8;//2.const修饰指针,则指针为不可变量
int a=9;
int* const p=&a;
*p=9; //正确
int b=7;
p=&b; //错误,指针不可变,改变其地址不对//3.const修饰指针和指针指向的内容,则指针和指针指向的内容都为不可变量
int a=8;
const int * const p=&a;
  • const 参数传递和函数返回值
//1. A:值传递的 const 修饰传递,一般这种情况不需要 const 修饰,因为函数会自动产生临时变量复制实参值。
#include<iostream>
using namespace std;void Cpf(const int a)
{cout<<a;// ++a;  是错误的,a 不能被改变
}int main(void)
{Cpf(8);system("pause");return 0;
}//2. 当 const 参数为指针时,可以防止指针被意外篡改。
#include<iostream>
using namespace std;void Cpf(int *const a)
{cout<<*a<<" ";*a = 9;
}int main(void)
{int a = 8;Cpf(&a);cout<<a; // a 为 9system("pause");return 0;
}//3. 自定义类型的参数传递,需要临时对象复制参数,对于临时对象的构造,需要调用构造函数,比较浪费时间,
//因此我们采取 const 外加引用传递的方法,并且对于一般的 int、double 等内置类型,我们不采用引用的传递方式。
#include<iostream>
using namespace std;class Test
{
public:Test(){}Test(int _m):_cm(_m){}int get_cm()const{return _cm;}private:int _cm;
};void Cmf(const Test& _tt)
{cout<<_tt.get_cm();
}int main(void)
{Test t(8);Cmf(t);system("pause");return 0;
}
  • const 修饰函数的返回值
//1. const 修饰内置类型的返回值,修饰与不修饰返回值作用一样。
#include<iostream>
using namespace std;const int Cmf()
{return 1;
}int Cpf()
{return 0;
}int main(void)
{int _m = Cmf();int _n = Cpf();cout<<_m<<" "<<_n;system("pause");return 0;
}//2. const 修饰自定义类型的作为返回值,此时返回的值不能作为左值使用,既不能被赋值,也不能被修改。//3. const 修饰返回的指针或者引用,是否返回一个指向 const 的指针,取决于我们想让用户干什么。
  • const修饰类成员函数
//const 修饰类成员函数,其目的是防止成员函数修改被调用对象的值
//如果我们不想修改一个调用对象的值,所有的成员函数都应当声明为 const 成员函数。
#include<iostream>
using namespace std;class Test
{
public:Test(){}Test(int _m):_cm(_m){}int get_cm()const{return _cm;}private:int _cm;
};void Cmf(const Test& _tt)
{cout<<_tt.get_cm();
}int main(void)
{Test t(8);Cmf(t);system("pause");return 0;
}
//***************************************************
class Test
{
public:Test(int _m,int _t):_cm(_m),_ct(_t){}void Kf()const{++_cm; // 错误++_ct; // 正确}
private:int _cm;mutable int _ct;
};

2.inline

  • 引入inline关键字原因

    • 在 c/c++ 中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了 inline 修饰符,表示为内联函数。
    • 栈空间就是指放置程序的局部数据(也就是函数内数据)的内存空间。
    • 在系统下,栈空间是有限的,假如频繁大量的使用就会造成因栈空间不足而导致程序出错的问题,如,函数的死循环递归调用的最终结果就是导致栈内存空间枯竭。
  • 内联函数并不是一个增强性能的灵丹妙药。只有当函数非常短小的时候它才能得到我们想要的效果;但是,如果函数并不是很短而且在很多地方都被调用的话,那么将会使得可执行体的体积增大。

下面我们来看一个例子:

#include <stdio.h>inline const char *num_check(int v)
{return (v % 2 > 0) ? "奇" : "偶";
}int main(void)
{int i;for (i = 0; i < 100; i++)printf("%02d   %s\n", i, num_check(i));return 0;
}

3. static

  • 静态全局变量

    • 该变量在全局数据区分配内存
    • 未经初始化的静态全局变量会被成需自动初始化为0
    • 静态全局变量在声明它的整个文件都是可见的,而在文件之外是不可见的
    • 静态全局变量都在全局数据区分配内存,包括后边将要提到的静态局部变量。对于一个完整程序,在内存中的分布情况为:代码区//low address全局数据区堆区栈区//high address
    • 一般程序把新产生的动态数据存放在堆区,函数内部的自动变量存放在栈区。自动变量一般会随着函数的退出而释放空间,静态数据(即使是函数内部的静态局部变量)也存放在全局数据区。全局数据区的数据并不会因为函数的退出而释放空间。
      //示例1
      #include <iostream>
      using namespace std;
      void fn();  //声明函数
      static int n;  //声明静态全局变量
      int main()
      {n = 20;  //为n赋初值cout<<n<<endl;//输出n的值fn();  //调用fn函数
      }
      void fn()
      {n++;  //n的值自加一(n=n+1)cout<<n<<endl; //输出n的值
      }
      
  • 静态全局变量和全局静态变量的区别

    • 1)全局变量是不显视用static修饰的全局变量,全局变量默认是有外部链接性的,作用域是整个工程,在一个文件内定义的全局变量,在另一个文件中,通过extern 全局变量名的声明,就可以使用全局变量。
    • 2)全局静态变量是显式用static修饰的全局变量,作用域是声明此变量所在的文件,其他的文件即使用extern声明也不能使用。
//示例2
//File1.c第一个代码文件的代码
#include <iostream>
using namespace std;
void fn();  //声明fn函数
static int n;  //定义静态全局变量
int main()
{n = 20;cout<<n<<endl;fn();
}
//File2.c第二个代码文件的代码
#include <iostream>
using namespace std;
extern int n;
void fn()
{n++;cout<<n<<endl;
}

编译并运行Example 2,您就会发现上述代码可以分别通过编译,但运行时出现错误。试着将
static int n; //定义静态全局变量
改为
int n; //定义全局变量
再次编译运行程序,细心体会全局变量和静态全局变量的区别。

  • 静态局部变量

    • 该变量在全局数据区分配内存;
    • 静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化;
    • 静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0;
    • 它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束;
//示例3
#include <iostream>
using namespace std;
void fn();
int main()
{fn();fn();fn();return 0;
}
void fn()
{static int n = 10;cout<<n<<endl;n++;
}
  • 静态函数

    • 静态函数不能被其他文件所用
    • 其他文件中可以定义相同名字的函数,不会发生冲突
  • 面向对象(类)中的静态属性使用

      1. 静态数据成员
      • 1)静态数据成员可以实现多个对象之间的数据共享,它是类的所有对象的共享成员,它在内存中只占一份空间,如果改变它的值,则各对象中这个数据成员的值都被改变。
      • 2)静态数据成员是在程序开始运行时被分配空间,到程序结束之后才释放,只要类中指定了静态数据成员,即使不定义对象,也会为静态数据成员分配空间。
      • 3)静态数据成员可以被初始化,但是只能在类体外进行初始化,若未对静态数据成员赋初值,则编译器会自动为其初始化为 0。
      • 4)静态数据成员既可以通过对象名引用,也可以通过类名引用。
      1. 静态成员函数
      • 1)静态成员函数和静态数据成员一样,他们都属于类的静态成员,而不是对象成员。
      • 2)非静态成员函数有 this 指针,而静态成员函数没有 this 指针。
      • 3)静态成员函数主要用来方位静态数据成员而不能访问非静态成员。

再给一个利用类的静态成员变量和函数的例子以加深理解,这个例子建立一个学生类,每个学生类的对象将组成一个双向链表,用一个静态成员变量记录这个双向链表的表头,一个静态成员函数输出这个双向链表。

#include <stdio.h>
#include <string.h>
const int MAX_NAME_SIZE = 30;  class Student  
{  
public:  Student(char *pszName);~Student();
public:static void PrintfAllStudents();
private:  char    m_name[MAX_NAME_SIZE];  Student *next;Student *prev;static Student *m_head;
};  Student::Student(char *pszName)
{  strcpy(this->m_name, pszName);//建立双向链表,新数据从链表头部插入。this->next = m_head;this->prev = NULL;if (m_head != NULL)m_head->prev = this;m_head = this;  
}  Student::~Student ()//析构过程就是节点的脱离过程  
{  if (this == m_head) //该节点就是头节点。{m_head = this->next;}else{this->prev->next = this->next;this->next->prev = this->prev;}
}  void Student::PrintfAllStudents()
{for (Student *p = m_head; p != NULL; p = p->next)printf("%s\n", p->m_name);
}Student* Student::m_head = NULL;  void main()  
{   Student studentA("AAA");Student studentB("BBB");Student studentC("CCC");Student studentD("DDD");Student student("MoreWindows");Student::PrintfAllStudents();
}

程序将输出:
在这里插入图片描述

http://www.lryc.cn/news/5236.html

相关文章:

  • 【成为架构师课程系列】怎样进行概念架构(Conceptual Architecture)?
  • PostgreSQL的下载安装教程(macOS、Windows)
  • 98年的确实卷,公司新来的卷王,我们这帮老油条真干不过.....
  • 软件架构知识2-系统复杂度
  • JavaSE学习day4_02 数组(超级重点)
  • Theano教程:Python的内存管理
  • Linux | Liunx安装Tomcat(Ubuntu版)
  • 缓冲区浅析
  • Day888.MySQL是怎么保证主备一致的 -MySQL实战
  • 互联网舆情监测系统的发展阶段,TOOM互联网舆情监测系统有哪些?
  • GIT命令操作大全
  • 突破传统开发模式,亚马逊云科技助力中科院加速推动合成生物学
  • 分享开放通达信l2接口的过程,开发之后怎么使用?
  • 33、基于51单片机老人防跌倒蜂鸣器报警系统加速度检测
  • 【项目】基于SpringBoot+Freemarker+Mybatis+MySQL+LayUI实现CRM智能办公系统
  • 手写识别字体的步骤是什么?怎么识别图片中的文字?
  • Mysql 存储过程
  • 【LeetCode】每日一题(3)
  • websocket学习
  • Java面试题及答案整理汇总(2023最新版)
  • 公司来了个卷王,我愿称之为王中王,让人崩溃
  • 波奇学c语言:代码的编译和链接
  • 计算机网络原理--传输层协议(TCP协议十大特性)
  • nvm控制node版本
  • 从0到1一步一步玩转openEuler--13 openEuler用户组管理
  • 知不知道什么叫米筐量化?怎么来的?
  • Urho3D 事件Events
  • Rust学习入门--【8】复合类型
  • 【整理六】
  • Ubuntu20.04安装MySQL5.7与远程连接