C&C++:const和static总结

30 篇文章 1 订阅
16 篇文章 0 订阅

C&C++:const和static总结

C语言要点总结-关键字总结:https://blog.csdn.net/qq_41605114/article/details/104497181

C和C++的区别:https://blog.csdn.net/qq_41605114/article/details/104239945

C++ 类的静态成员及静态成员函数https://www.cnblogs.com/codingmengmeng/p/5906282.html

 


目录

1const

1.1C中的const

1.1.1const修饰常量(在C中不稳定)

1.1.2const修饰指针类型:

1.2C++中的const

1.3C++:const修饰函数和对象

1.3.1常函数 

1.3.2常对象(包括常指针和常引用)

1.3.3const修饰普通成员

1.4const对重载的影响

1.5const总结

2static

2.1C语言中的static

2.1.1static(静态)

2.1.2static修饰局部变量

2.1.3static修饰全局变量

2.1.4static修饰函数

2.1C++中的static

2.2.1静态数据成员

2.2.3static总结 

3const&static

3.1 const和static修饰普通成员函数

3.2 const和static修饰普通成员变量

4 this指针在const和static中的存在情况


 

const和static,是32个关键字中的存储类关键字,平常用的比较多,而且都比较特殊,功能和组合也比较繁杂,在此进行简单的总结。

1const

  1. C中的const不稳定,可以通过指针进行修改;
  2. C++中的const稳定,因为变量被放置在了符号表中,正在意义上成为了常量 ;
  3. C++中,被const修饰的函数——常函数;
  4. 常函数不能对普通成员变量(除mutable修饰外)进行写操作 
  5. 常函数可以被普通对象或者常对象调
  6. 被const修饰的对象——常对象
  7. 常对象不能调用所有普通函数,只调用常函数
  8. 常对象可以读成员变量

1.1C中的const

1.1.1const修饰常量(在C中不稳定)

const int a = 10;//const定义不稳定常量

之所以不稳定,是因为以上方式使用const,在C中,是可以使用一级指针对变量a的值进行修改的。

const int a = 10;//使用const定义不稳定常量
int* p = &a;//将不稳定常量a的地址赋给int类型的指针p,及p指向a的地址
*p = 100;//对a进行修改,是可以进行如此操作
//所以综上所述,使用const定义常量在C中不稳定

1.1.2const修饰指针类型:

C++:引用变量:https://blog.csdn.net/qq_41605114/article/details/88416448

1:可以重新赋值,但是不能访问内容

int a = 10;
int b = 20;
const int * p = &a;
p = &b;//成立
*p = 100;//不成立

注意,此时的const后面跟的是指针的定义及赋值过程,const此时修饰指针类型,就const作用于int *

对指针进行重新赋值是可以的,但是无法改变指针所指向地址的内容;

const修饰的指针类型,此时是无法修改内存中的数值的。

2:可以修改内容,但是无法改变指向(引用声明的本质)

int a = 10;
int b = 20;
int * const p = &a;//只能在此时进行赋值
*p = 100;//成立
p = &b;//不成立

注意,此时的const后面跟的是指针变量,const此时修饰指针变量,就const作用于p

对指针进行重新赋值是不允许的,指针指向是固定的,但是可以改变指针所指向地址的内容;

const修饰的指针变量,此时是无法修改指针的指向。

那么综上,定义一个指针,其指向无法被修改且指向内存中的内容也无法修改,定义如下:

const int * const p = &a;//注意,只能在此时进行赋值

但是如此定义依旧不稳定,我们可以通过二级指针,对其指向的内存中的内容,进行修改。

const修饰全局变量时,存储在常量区,不能修改。

const修饰局部变量时,放在栈区,是可以修改的,通过指针。

const也常用于函数结构参数的修饰:

void Function(const int i,const double j,const struct MODE * Test)
{
   .......
}

在一般传递占用较大内容的数据时,多选用指针传递,快捷方便,但是当函数只是使用这段数据而不是修改时,会出行一个风险

指针成为参数时,效率很高,但是副作用也很明显,就是有能会出现失误,导致原始数据被修改

所以此时,使用const关键字,使得其无法被修改。高效率的同时,加强了程序的稳定性。

虽然依旧可以使用二级指针对其进行修改,但是如此明显的意图与参数加const矛盾

const此时更多提供的是一种声明,防止意外,这个参数,请不要修改,改了也没有意义。

在C++中,const会更加稳定和适用 

1.2C++中的const

C++中,const修饰常量,比C更加稳定,其余一样。

const修饰全局变量,无法被修改,在C/C++中一样,非常稳定。

但是对于局部变量,二者还是有区别的,C中可以通过指针修改局部变量的值,所以const在C中不稳定

 

在C++中,const很稳定

C++中,const修饰局部变量时,不会分配内存

int main(void)
{
    const int a = 10;
}

直接将变量放入符号表中,因为不会分配内存,所以无法修改。

                                           

    const int a = 10;
    int * p = (int *)&a;
    *p = 20;
    qDebug()<<"a"<<a;
    qDebug()<<"*p"<<*p;

          

可以看到,以上代码在C中,a的值会被修改,但是在C++中,a的值无法被修改

                

代码中修改的是P的值,不是a的值,a本身还在符号表中。

只要是初始化的时候分配了内存,不放在符号表里面,即可使用指针进行修改

用变量初始化const变量时,可以使用指针修改

//全局变量

//用变量初始化const变量
int a = 10;
const int b = a;//这个时候会分配内存
int * pp = (int *)&b;
*pp = 20;
qDebug()<<"b:"<<b;//局部定义b也是一样的

 

链接性的变化

C中:const修饰后,变量依旧是外部链接性。

C++中:const修饰后,变量为内部链接性。

1.3C++:const修饰函数和对象

常函数不能对普通成员变量(除mutable修饰外)进行写操作 

常函数可以被普通对象或者常对象调用

常对象不能调用所有普通函数,只能调用常函数

常对象可以读成员变量

1.3.1常函数 

常函数声明方式

返回值  函数名称 (参数) const;

class Person : public QMainWindow
{
    Q_OBJECT

public:
    Person();
    Person(const int i);
    Person(const int i,const QString C_name);
    Person(const Person & p);
    ~Person();

    int Age;
    QString name;

    void Const_func() const;//常函数的声明

};

上述

void Const_func() const;//常函数的声明

为常函数的声明

具体定义如下:

void Person::Const_func() const
{
  
}

 常函数,只能访问成员变量,不能修改,在常函数中成员变量均是只读(only-read)

成员函数中访问成员变量都是借助this指针,又因为this指针永远指向调用函数的对象,所以相当于

Person * const this;

指向是固定的,但是访问变量可以修改值

但是在常函数中,this指针再次受到了限制:

const Person * const this; 

如果在常函数中改变成员变量,编译器会报错:

void Person::Const_func() const
{
    this->Age = 20;
}

在常函数中只能读取,不能写!

void Person::Const_func() const
{
    qDebug()<<this->Age;
}

普通对象和常对象都可以访问常函数 ,常对象只能访问常函数

1.3.2常对象(包括常指针和常引用)

常对象只能访问常函数,读成员变量,无法写

const 类名 对象名;

只能访问常函数,访问其他函数会报错

void Person::test()
{
    this->Age = 30;
}
const Person C_p;

qDebug()<<C_p.Age;

C_p.test();

 程序会报错:

常对象只能访问常函数,不管普通函数中有没有写操作,都不能访问

只能读成员变量,不能写

const Person C_p;
C_p.Age = 10;
qDebug()<<C_p.Age;

程序报错 

常对象访问普通成员变量,只能读,不能进行写操作! 

使用关键字mutable是解决上述限制的方法

1.3.3const修饰普通成员

定义都很好定义,就是const修饰之后,到底要如何初始化呢?

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

    const double Pi;

private:
    Ui::MainWindow *ui;
};

初始化列表的方式进行初始化

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    Pi(3.1415923)
{
    ui->setupUi(this);
    NotStatic_variable = 10;
}

1.4const对重载的影响


1:常函数,支持重载。一个是普通对象访问,当常对象访问时变成常函数

#include<iostream>
using namespace std;
 
class Test
{
protected:
    int x;
public:
    Test (int i):x(i) { }
    void fun() const
    {
        cout << "fun() const called " << endl;
    }
    void fun()
    {
        cout << "fun() called " << endl;
    }
};
 
int main()
{
    Test t1 (10);
    const Test t2 (20);
    t1.fun();
    t2.fun();
    return 0;
}
————————————————
版权声明:本文为CSDN博主「水草」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/shltsh/article/details/45939977

常函数是由一个常对象调用的,而普通函数是由一个普通对象调用。

2,修饰参数,当按值传递时,不支持重载,当参数是引用或指针时,支持

按值传递,修改与否都不影响原来的值,但是按照引用和指针传递,会影响

//例子1,会编译失败
#include<iostream>
using namespace std;
 
void fun(const int i)
{
    cout << "fun(const int) called ";
}
void fun(int i)
{
    cout << "fun(int ) called " ;
}
int main()
{
    const int i = 10;
    fun(i);
    return 0;
}

————————————————
版权声明:本文为CSDN博主「水草」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/shltsh/article/details/45939977

按值传递会报错

//例子2,运行正常
#include<iostream>
using namespace std;
void fun(char *a)
{
cout << "non-const fun() " << a;
}
void fun(const char *a)
{
cout << "const fun() " << a;
}
int main()
{
const char *ptr = "hello world";
fun(ptr);
return 0;
}
————————————————
版权声明:本文为CSDN博主「水草」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/shltsh/article/details/45939977

不报错没有问题 

 重载是常函数,根据访问对象类型重载,重载参数,根据输入参数重载

参考:C++函数重载(3) - 函数重载中的const关键字:https://blog.csdn.net/shltsh/article/details/45939977

那么返回值一个是普通,一个是被const修饰呢?

C++重载返回值不一样不会重载

1.5const总结

const总有“固定”之意。

修饰变量后,此变量可读不可写;修饰对象后,该对象只可以访问成员变量,且只能读,不能写(不能访问成员函数);

修饰指针,要么无法修改所指向的内容,只能读,要么无法修饰其指向;

修饰成员函数,成员函数也是不能写(成员变量),只能读;

const对所有修饰对象的基本限制就是,只读,不写

在C++中更是如此,对成员函数的修饰,让成员函数变成了常函数,只能读一切,就是不能写。

修饰对象,对象只能读成员变量,而绝对不能访问一般的成员函数,谁知道一般的成员函数里面也能用写变量的操作,

所以只能访问常函数,毕竟常函数中,只能是读,不可能出现写操作。

 

2static

2.1C语言中的static

2.1.1static(静态)

c语言中static关键字用法详解:https://blog.csdn.net/guotianqing/article/details/79828100(主要参考,文章写的很好)

被声明为静态类型的变量,无论是全局的还是局部的。

都存储在全局区(静态区)(Global data Segment)中,其生命周期为整个程序。

静态变量如果没有被初始化,则自动初始化为0只能够初始化一次

2.1.2static修饰局部变量

静态局部变量使用static修饰符定义,即使在声明时未赋初值,编译器也会把它初始化为0。仅仅初始化一次,之后一直保留,保存在静态区/全局区中。

且静态局部变量存储于进程的全局数据区,即使函数返回,它的值也会保持不变。

主函数:
...........

for(int i = 0;i<10;i++)
{
   StaticFunction();
}
...........

void MainWindow::StaticFunction()
{
    static int a = 10;
    a++;
    qDebug()<<"a"<<a;

}

输出:

对比一下普通局部变量:

主函数:
...........

for(int i = 0;i<10;i++)
{
   StaticFunction();
}
...........

void MainWindow::StaticFunction()
{
    int a = 10;
    a++;
    qDebug()<<"a"<<a;

}

输出:

可见,静态局部变量的效果跟全局变量有一拼,但是位于函数体内部,就极有利于程序的模块化了。

 

2.1.3static修饰全局变量

全局变量定义在函数体外部,在全局数据区分配存储空间,且编译器会自动对其初始化。

普通全局变量对整个工程可见,其他文件可以使用extern外部声明后直接使用。

也就是说其他文件不能再定义一个与其相同名字的变量了(否则编译器会认为它们是同一个变量)。

静态全局变量对当前文件可见,其他文件不可访问,其他文件可以定义与其同名的变量,两者互不影响。

 

2.1.4static修饰函数

函数声明为static,除了对该函数声明所在的文件可见外,其他文件都无法访问。

static函数的作用域在本文件,在内存中只有一份,普通函数在每个被调用中维持一份拷贝。

函数的使用方式与全局变量类似,在函数的返回类型前加上static,就是静态函数。其特性如下:

  • 静态函数只能在声明它的文件中可见,其他文件不能引用该函数
  • 不同的文件可以使用相同名字的静态函数,互不影响

2.1C++中的static

静态数据成员和静态成员函数:https://blog.csdn.net/qiana_/article/details/82083313

2.2.1静态数据成员


在类内数据成员的声明前加上static关键字,该数据成员就是类内的静态数据成员。其特点如下:

  1. 静态数据成员存储在全局数据区,静态数据成员在定义时分配存储空间,所以不能在类声明中定义(在类声明在声明,在全局作用域中定义中进行定义)
  2. 静态数据成员是类的成员,无论定义了多少个类的对象,静态数据成员的拷贝只有一个,且对该类的所有对象可见。也就是说任一对象都可以对静态数据成员进行操作。而对于非静态数据成员,每个对象都有自己的一份拷贝。
  3. 由于上面的原因,静态数据成员不属于任何对象,在没有类的实例时其作用域就可见,在没有任何对象时,就可以进行操作
  4. 和普通数据成员一样,静态数据成员也遵从public, protected, private访问规则
  5. 静态数据成员的初始化格式:<数据类型><类名>::<静态数据成员名>=<值>  不赋值,默认是零
  6. 类的静态数据成员有两种访问方式:<类对象名>.<静态数据成员名> 或 <类类型名>::<静态数据成员名>

同全局变量相比,使用静态数据成员有两个优势:

静态数据成员没有进入程序的全局名字空间,因此不存在与程序中其它全局名字冲突的可能性


可以实现信息隐藏。静态数据成员可以是private成员,而全局变量不能

示例:

声明

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

    int NotStatic_variable;//非静态成员变量
    static int Static_variable;//静态成员变量

private:
    Ui::MainWindow *ui;
};

初始化

#include "mainwindow.h"
#include "ui_mainwindow.h"
int MainWindow::Static_variable = 10;//<数据类型><类名>::<静态数据成员名>=<值>
//静态成员变量

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    NotStatic_variable = 10;
}

具体操作如下:

#include "mainwindow.h"
#include <QApplication>
#include<QDebug>
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();

    MainWindow p1;//对象1
    MainWindow p2;//对象2
    MainWindow p3;//对象3

    qDebug()<<"NotStatic_variable部分";
    p1.NotStatic_variable = 20;
    qDebug()<<p1.NotStatic_variable;
    p2.NotStatic_variable++;
    qDebug()<<p2.NotStatic_variable;
    p3.NotStatic_variable++;
    qDebug()<<p3.NotStatic_variable;


    qDebug()<<"Static_variable部分";
    p1.Static_variable = 20;
    qDebug()<<p1.Static_variable;
    p2.Static_variable++;
    qDebug()<<p2.Static_variable;
    p3.Static_variable++;
    qDebug()<<p3.Static_variable;
    return a.exec();
}

输出: 

所有对象访问静态成员变量,都是同一块内存区域,对其进行的任何操作,都是对同一个值的操作

普通成员变量则不同,每个对象定义的时候,都有自己的成员变量,从上面操作的输出也可以看出来,普通成员变量初始化是10,跨成员变量对其的修改都是基于10的,没有连续性,对静态成员变量就不一样了。


2.2.2静态成员函数


与静态数据成员类似,静态成员函数属于整个类,而不是某一个对象,其特性如下:

静态成员函数没有this指针,它无法访问属于类对象的非静态数据成员,也无法访问非静态成员函数

它只能调用其余的静态成员函数


出现在类体外的函数定义不能指定关键字static


非静态成员函数可以任意地访问静态成员函数和静态数据成员

//h
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();


    int NotStatic_variable;//非静态成员变量
    static int Static_variable;//静态成员变量

    static void Test();//静态成员函数

private:
    Ui::MainWindow *ui;
};
void MainWindow::Test()//静态成员函数
{
    qDebug()<<this->NotStatic_variable;

}

 编译器报错(没有this指针)

对函数进行修改,去掉this指针:

不能访问普通成员变量,普通成员函数一样,我们再次修改

这次没有报错,因为将访问的变量变成了静态成员变量。

综上: 

与静态数据成员类似,静态成员函数属于整个类,而不是某一个对象,其特性如下:

静态成员函数没有this指针,它无法访问属于类对象的非静态数据成员,也无法访问非静态成员函数

 

其他性质:

1,静态成员函数不能为virtual,因为静态成员函数可以不使用对象来调用,vptr对它不适用,因为vptr只在构造函数中生成

2,静态成员函数不能做为重载函数

3,静态成员函数不能被声明为const, 或者volatile, 或者const volatile

 

还有其他内容,详细参考:C++静态成员(1) - 静态成员函数的特性:https://blog.csdn.net/shltsh/article/details/45949527

 

 

2.2.3static总结 

从static的使用更能看出来它是一个存储类型的关键字,是在全局(常量)区开辟了一块空间,存放相关的内容

无论是修饰局部变量,全局变量,还是修饰成员变量,全局变量我们先不说,对于局部变量和成员变量使用static,直接改变了该变量的作用域,因为存放的位置变量,静态局部变量永远存在,不会随着函数的出栈而销毁,静态成员变量也不会随着对象的变更而变更,所有对象访问的都是同一块地址。

而对全局变量,关键字static显然是限制了它的链接性,对普通函数的修饰也是如此,限制是其链接性。

而对成员函数的修饰,让成员函数也变成了所有对象公有的区域,在此区域this完全失效,因为this指针的核心就是指向调用该接口的对象的指针,且被const修饰,无法改变指向,既然该成员函数已经被static修饰变成了公共区域,this完全失去了意义,无法指向任何部分。

 

3const&static

3.1 const和static修饰普通成员函数

在C++中,对成员函数来说,不可能出现const和static同时修饰成员函数的情况。

参考:https://www.cnblogs.com/wen-ge/articles/5884122.html

C++编译器在实现const的成员函数的时候为了确保该函数不能修改类的实例的状态,会在函数中添加一个隐式的参数const this*。但当一个成员为static的时候,该函数是没有this指针的。也就是说此时const的用法和static是冲突的。

 

我们也可以这样理解:两者的语意是矛盾的。static的作用是表示该函数只作用在类型的静态变量上,与类的实例没有关系;而const的作用是确保函数不能修改类的实例的状态,与类型的静态变量没有关系。因此不能同时用它们。

3.2 const和static修饰普通成员变量

https://www.cnblogs.com/xiezhw3/p/4354601.html

static 变量在类内部声明,但是必须在类的外部进行定义和初始化。

const 常量在类内部声明,但是定义只能在构造函数的初始化列表进行。

现在要定义一个变量,要求该变量不可写只可读,且所有对象访问该变量时,该变量的值都是恒定的

显然这个变量需要用staticconst一起修饰。

修饰顺序是static const还是const static都无所谓。

3.2.1初始化方式1:类内声明,类外定义初始化

//.h
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

    const static int num1;//声明

private:
    Ui::MainWindow *ui;
};

//.cpp
const int MainWindow::num1 = 20;

,h类内声明,.cpp类外定义和初始化

    qDebug()<<"static const";
    qDebug()<<p1.num1;

输出: 

3.2.2初始化方式2:类内声明初始化,类外定义

//.h
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();


    const static int num1;//声明

    const static int num2 = 13;//声明

private:
    Ui::MainWindow *ui;
};
//.cpp
const int MainWindow::num2;
    qDebug()<<"static const";
    qDebug()<<p1.num1;
    qDebug()<<p1.num2;

输出如下: 

 

现在类中声明并且赋值,然后在类外定义;

显然,声明是为了程序的可读性,而关键问题在于定义和初始化,牵扯内存分配空间给这个变量,所以,尽量使用第一种

类内声明,类外定义和初始化,特殊情况,比如如下的情况;

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();


    const static int num1;//声明

    const static int num2 = 13;//声明

private:
    Ui::MainWindow *ui;
    int Matrix[num2];
};

只要如上情况,才能使用方式2。

如果不在声明位置初始化,那么会报错。

4 this指针在const和static中的存在情况

this指针存在于成员函数中,this指针的本质是:ClassName * const this = 调用该函数的对象;  

this在static修饰的成员函数中完全失去了意义,不存在,因为静态成员函数是公共的。

而在const修饰的成员函数,this指针存在,可以读,但是不能写。

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值