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 为 9
    system("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();
}

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

扫描二维码关注公众号,回复: 14693008 查看本文章

猜你喜欢

转载自blog.csdn.net/yohnyang/article/details/128986218
今日推荐