mutable
当类的数据成员声明为mutable时,该数据成员的值在const成员函数中也可以被改变。
不可与const和static一起用。
class Base
{
public:
Base():i(0){}
void ChangeValue() const
{
//assign value to the mutable data member
i=1;
}
mutable int i;
};
extern
1.extern用在变量声明中常常有这样一个作用,你在*.c文件中声明了一个全局的变量,这个全局的变量如果要被引用,就放在*.h中并用extern来声明。
2.如果函数的声明中带有关键字extern,仅仅是暗示这个函数可能在别的源文件里定义,没有其它作用。即下述两个函数声明没有区别:
extern int f(); 和int f();
如果不想包含头文件,那么在c/cpp中声明该函数。一般来说,声明定义在本文件的函数不用“extern”,声明定义在其他文件中的函数用“extern”,
这样在本文件中调用别的文件定义的函数就不用包含头文件include “*.h”来声明函数,声明后直接使用即可。
extern "C"
c++编译器会将函数改名后保存到obj文件中,c编译器不会。
所以在c++代码中include一个声明c函数的头文件,link该c函数库时,会找不到函数定义。
这时在该函数声明前加上关键字extern "C", 可以保证编译器link时按照c函数的规则去库中寻找该函数定义。
//extern "C"的惯用法
//1.在C++中引用C语言中的函数和变量,在包含C语言头文件(假设为cExample.h)时,需进行下列处理:
extern "C"
{
#include"cExample.h"
}
//而在C语言的头文件中,对其外部函数只能指定为extern类型,C语言中不支持extern "C"声明,在.c文件中包含了extern "C"时会出现编译语法错误。
//C++引用C函数例子工程中包含的三个文件的源代码如下:
/**//* c语言头文件:cExample.h */
#ifndef C_EXAMPLE_H
#define C_EXAMPLE_H
extern int add(int x,int y);
#endif
/**//* c语言实现文件:cExample.c */
#include"cExample.h"
int add( int x, int y )
{
return x + y;
}
// c++实现文件,调用add:cppFile.cpp
extern "C"
{
#include"cExample.h"
}
int main(int argc, char* argv[])
{
add(2,3);
return 0;
}
//如果C++调用一个C语言编写的.DLL时,当包括.DLL的头文件或声明接口函数时,应加extern "C" { }。
//2.在C中引用C++语言中的函数和变量时,C++的头文件需添加extern "C",但是在C语言中不能直接引用声明了extern "C"的该头文件,应该仅将C文件中将C++中定义的extern "C"函数声明为extern类型。
//C引用C++函数例子工程中包含的三个文件的源代码如下:
//C++头文件 cppExample.h
#ifndef CPP_EXAMPLE_H
#define CPP_EXAMPLE_H
extern "C" int add( int x, int y );
#endif
//C++实现文件 cppExample.cpp
#include"cppExample.h"
int add( int x, int y )
{
return x + y;
}
/**//* C实现文件 cFile.c
/* 这样会编译出错:#include"cExample.h" */
extern int add( int x, int y );
int main( int argc, char* argv[] )
{
add( 2, 3 );
return 0;
}
volatile
由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化,当某个值被优化时,程序可能只访问寄存器中的拷贝,这时,其它线程对内存中的值进行的修改,不会影响到前面程序从寄存器中读到的值,程序的运行结果就会出现错误。
使用volatile关键字声明的变量不会被编译器进行优化,因为它表示可能被其它原因修改,比如其它线程。
static
1.在固定的地址上分配,也就是说对象是在一个特殊的静态数据区上创建的,而不是每次函数调用时在堆栈上产生的。这也是静态存储的概念。
2.对一个特定的编译单位来说是本地的,这里static控制名字的可见性,所以这个名字在这个单元或类之外是不可见的。
在文件范围内,一个被明确声明为static的对象或函数的名字对编译单元来说是局部变量;这些名字有内部连接。
这意味着我们可以在其他的编译单元中使用同样的名字,而不会发生名字冲突。
#include <iostream>
using namespace std;
//static的两种基本的含义:
//1.在固定的地址上分配,也就是说对象是在一个特殊的静态数据区上创建的,而不是每次函数调用时在堆栈上产生的。这也是静态存储的概念。
int SaveTime()
{
static int iTime; //只在函数第一次调用时初始化一次.
//如果说没有为一个预定义类型的静态变量提供一个初始值的话,编译器也会确保在程序开始时它被初始化为零.
iTime++;
return iTime;
}
class X {
public:
X(const char* c);
~X();
private:
char* m_c;
};
X::X(const char* c = "Default")
{
m_c = new char[strlen(c)+1];
strcpy(m_c,c);
cout<<"Construct: "<<m_c<<endl;
}
X::~X()
{
cout<<"Destruct: "<<m_c<<endl;
delete []m_c;
}
void TestX()
{
static X x("static");
}
X x("Global");
void main()
{
cout<<"Enter Main"<<endl;
TestX();
for(int i=0; i<5; ++i) {
SaveTime();
}
cout<<SaveTime()<<endl;
cout<<"Leave Main"<<endl;
}
const
1、声明变量只读。
2、const 变量必须被初始化。
3、定义常量。
const常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查,而对后者只进行字符替换,没有类型安全检查,并且在字符替换时可能会产生意料不到的错误。
由于const定义常量从汇编的角度来看,只是给出了对应的内存地址, 而不是象#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存中有若干个拷贝。
4、类中的const数据成员。
const数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的。因为类可以创建多个对象,不同的对象其const数据成员的值可以不同。所以不能在类声明中初始化const数据成员,因为类的对象未被创建时,编译器不知道const 数据成员的值是什么。
const数据成员的初始化只能在类的构造函数的初始化表中进行。要想建立在整个类中都恒定的常量,应该用类中的枚举常量来实现。枚举常量不会占用对象的存储空间,他们在编译时被全部求值。但是枚举常量的隐含数据类型是整数,其最大值有限,且不能表示浮点数。
5、修饰类成员函数。
一般放在函数体后,任何不会修改数据成员的函数都因该声明为const类型。如果在编写const成员函数时,不慎修改了数据成员,或者调用了其他非const成员函数,编译器将报错,这大大提高了程序的健壮性。
const对象优先调用同名的const成员函数,实现了const重载。
6、修饰函数参数和返回值。
const通常用于参数为指针或引用的情况,且只能修饰输入参数;若输入参数采用“值传递”方式,由于函数将自动产生临时变量用于复制该参数,该参数本就不需要保护,所以不用const修饰。
返回值用const修饰可以防止允许直接对函数结果进行赋值。