共计 3163 个字符,预计需要花费 8 分钟才能阅读完成。
const
const
位于*
的左侧: 用来润饰指针所指向的变量,指针指向常量。
// 指针不容许扭转数据
int b = 500;
const int *a = &b;
*a = 600; // error
// 能够通过变量自身去批改
int b = 500;
const int *a = &b;
b = 600;
cout << *a << endl; // 600
const
位于*
的右侧: 用来润饰指针自身,指针是常量。
// 指针自身是常量,定义时须要初始化
int b = 500;
//int * const a; // error
int * const c = &b; // error
*c = 600; // 失常,容许改值
cout << *c << endl;
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;
}
- 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 变量的几个例子:
- 并行设施的硬件寄存器(如状态寄存器)
- 一个中断服务子程序中会拜访到的非主动变量(Non-automatic varbliaes)。
- 多线程利用中被几个工作共享的变量。
例 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 成员变量。