乐趣区

关于c++:C-static-const-volatile-总结

const

  1. const 位于 *的左侧: 用来润饰指针所指向的变量,指针指向常量。
// 指针不容许扭转数据
int b = 500;
const int *a = &b;
*a = 600; // error 
// 能够通过变量自身去批改
int b = 500;
const int *a = &b;
b = 600;
cout << *a << endl; // 600
  1. const 位于 *的右侧: 用来润饰指针自身,指针是常量。
// 指针自身是常量,定义时须要初始化
int b = 500;
//int * const a;  // error
int * const c = &b;  // error

*c = 600;  // 失常,容许改值
cout << *c << endl;
  1. const 用在成员函数中, 位于 function_name () const {}

咱们在定义类的成员函数中,经常有一些成员函数不扭转类的数据成员。也就是说这些函数的 readonly function,而有一些函数要批改类的数据成员的值。如果在 readonly function 都加上 const 进行标识,无疑会进步程序的可读性。其实还能进步程序的可靠性,已定义成 const 的成员函数,一旦希图扭转数据成员的值,则编译器按错误处理。

class A
{
public:
    A(int x) : a(x) {}
    int get() const { return a;}
    int get2() const { return a++;}  // ERROR read-only object
private:
    int a;
};

int main(int argc, char const *argv[])
{A obj(10);
    cout << obj.get() << endl;
    cout << obj.get2() << endl;
    return 0;
}

const 润饰之后,readonly function 也将只能调用 readonly function, 其实很好了解,如果readonly function 函数调用了其余函数,可能数据就会发生变化。

class A
{
public:
    A(int x) : a(x) {}
    int get() const { get2(); return a; }  // 谬误,不能调用非 readonly function
    int get2() const {}
private:
    int a;
};

int main(int argc, char const *argv[])
{A obj(10);
    int b = obj.get();
    cout << b << endl;
    return 0;
}

如果类的数据成员加上 mutable 后,润饰为 const的成员变量,也能够批改。

class A
{
public:
    A(int x) : a(x) {}
    int get() const { return ++a;}
private:
    mutable int a;
};


int main(int argc, char const *argv[])
{A obj(10);
    int b = obj.get();
    cout << b << endl;
    return 0;
}
  1. const 位于函数申明前,意味着函数的返回值是常量

面试时须要留神的是: 面试时应该形容 const 的 只读 ,而不仅仅是常量,关键字 const 的作用是为读你代码的人传播十分有用的信息。实际上,申明一个参数为常量是为了通知用户这个参数的利用目标。如果你曾花很多工夫清理其他人留下的垃圾,你就会很快学会感激这点儿多余的信息。当然,懂得用 const 的程序员很少会留下垃圾让他人来清理。通过给优化器一些附加的信息,应用关键字 const 兴许能产生更紧凑的代码。
正当地应用关键字 const 能够使编译器很天然地爱护那些不心愿被扭转的参数,避免其被无心的代码批改。简而言之,这样能够缩小 bug 的呈现。

volatile

volatile 关键字是一种类型修饰符,用它申明的类型变量示意能够被某些编译器未知的因素更改,比方:操作系统、硬件或者其它线程等。

遇到这个关键字申明的变量,编译器对拜访该变量的代码就不再进行优化。

volatile 限定修饰符的用法与 const 十分类似都是作为类型的附加修饰符。例如:

volatile int display_register;
volatile Task *curr_task;
volatile int ixa[max_size];
volatile Screen bitmap_buf;

display_register 是一个 int 型的 volatile 对象;
curr_task 是一个指向 volatile 的 Task 类对象的指针;
ixa 是一个 volatile 的整型数组,数组的每个元素都被认为是 volatile 的;
bitmap_buf 是一个 volatile 的 Screen 类对象,它的每个数据成员都被视为 volatile 的。

volatile 的语法与 const 是一样的,然而 volatie 的意思是“在编译器意识的范畴外,这个数据能够被扭转”。不知什么起因,环境正在扭转数据(可能通过多任务处理),所以,volatile 通知编译器不要擅自做出无关数据的任何假设,在优化期间这是特地重要的。如果编译器说:我曾经把数据读进寄存器,而且再没有与寄存器接触。”在个别状况下,它不须要再读这个数据。然而,如果数据是 volatile 润饰的,编译器则不能做出这样的假设,因为数据可能被其余过程扭转了,编译器必须重读这个数据而不是优化这个代码。
就像建设 const 对象一样,程序员也能够建设 volatile 对象,甚至还能够建设 const volatile 对象。这个对象不能被程序员扭转,但可通过里面的工具扭转。

例 1: 关键字 volatile 有什么含意?并给出 3 个不同的例子。

一个定义为 volatile 的变量是说这变量可能会被意想不到地扭转,这样,编译器就不会去假如这个变量的值了。准确地说就是,优化器在用到这个变量时必须每次都小心地从新读取这个变量的值,而不是应用保留在寄存器里的备份。

上面是 volatile 变量的几个例子:

  1. 并行设施的硬件寄存器(如状态寄存器)
  2. 一个中断服务子程序中会拜访到的非主动变量(Non-automatic varbliaes)。
  3. 多线程利用中被几个工作共享的变量。

例 2: 一个参数能够既是 const 又是 volatile 吗?一个指针能够是 volatile 吗?解释为什么。

第一个问题:能够。一个例子就是只读的状态寄存器。它是 volatile,因为它可能被意想不到地扭转;它又是 const, 因为程序不应该试图去批改

第二个问题: 能够。只管这并不很常见。一个例子是当一个中断服务子程序批改一个指向一个 buffer 的指针时。

例 3: 上面的函数有什么谬误?

int square(volatile int *ptr) {return *ptr * *ptr;}

这段代码的目标是用来返还指针 *ptr 指向值的平方,然而,因为 *ptr 指向一个 volatlie 型参数,编译器将产生相似上面的代码:

int square(volatile int *ptr) {
    int a, b;
    a = *ptr;
    b = *ptr;
    return a * b;
}

因为 *ptr 的值可能被意想不到地扭转,因而 a 和 b 可能是不同的。后果,这段代码可能无奈返回你所冀望的平方值。

正确的代码如下:

int square(volatile int *ptr) {
    int a = *ptr;
    return a * a;
}

static

  • 函数体内 static 变量的作用范畴为该函数体,不同于 auto 变量,该变量的内存只被调配一次,因而其值在下次调用时仍维持上次的值。
  • 在模块内的 static 全局变量能够被模块内所有函数拜访,但 不能被模块外其余函数拜访
  • 在模块内的 static 函数只可被这模块内的其余函数调用,这个函数的应用范畴被限度在申明它的模块内。
  • 在类中的 static 成员变量属于整个类所领有,对类的所有对象只有一份复制
  • 在类中的 static 成员函数属于整个类所领有,这个函数不接管 this 指针,因此只能拜访类的 static 成员变量。

退出移动版