目录
C中的const
const只读变量
const修饰的变量是只读的,本质还是变量,const只在编译期有用,在运行期无用
const修饰的局部变量在栈上分配空间,const修饰的全局变量在全局数据区分配空间
const修饰的变量不是真的变量,它只是告诉编译器该变量不能出现在赋值符号的左边
const全局变量的分歧
现代C编译器中的const将具有全局生命周期变量存储于只读存储区,修改const全局变量将导致程序崩溃
标准C语言编译器不会将const修饰的全局变量存储于只读存储区(.rodata)中,而是存储于可修改的全局(静态)数据区(.bss 和 .data),其值依然可以改变
const不能定义真正意义上的常量!!!而enum的枚举值却可以
#include <stdio.h>
const char* s = "wss";
const int g_cc = 2;
int main()
{
const int cc = 1;
int* p = (int*)&cc;
printf("cc = %d\n", cc);
*p = 3;
printf("cc = %d\n", cc);
p = (int*)&g_cc;
printf("g_cc = %d\n", g_cc);
*p = 4;
printf("g_cc = %d\n", g_cc);
return 0;
}
gcc:
BCC(标准C):
#include <stdio.h>
const int g_array[5] = {0}; // 只读存储区(现)
void modify(int* p, int v)
{
*p = v;
}
int main()
{
int const i = 0; // 栈
const static int j = 0; // 只读存储区
int const array[5] = {0}; // 栈
modify((int*)&i, 1); // ok
modify((int*)&j, 2); // error
modify((int*)&array[0], 3); // ok
modify((int*)&g_array[0], 4); // error
printf("i = %d\n", i);
printf("j = %d\n", j);
printf("array[0] = %d\n", array[0]);
printf("g_array[0] = %d\n", g_array[0]);
return 0;
}
BCC:
gcc:
const修饰函数参数和返回值
const修饰函数参数表示在函数体内不希望改变参数的值,const修饰函数返回值表示返回值不可改变,多用于返回指针的情形
C++中的const
C++对const的升级
C++在C的基础上对const进行了进化处理,当碰见const声明时在符号表中放入常量,编译过程中若发现使用常量则直接以符号表中的值替换,此时为真正意义上的常量
C语言中的const变量:C语言中const变量是只读变量,会分配存储空间
C++中的const常量:C++编译器虽然可能会为const常量分配空间,但不会使用其存储空间的值 ,兼容C语言
#include <stdio.h>
int main()
{
const int c = 0;
int* p = (int*)&c;
printf("Begin...\n");
*p = 5;
printf("c = %d\n", c);
printf("*p = %d\n", *p);
printf("End...\n");
return 0;
}
gcc:
g++:
C++中的const常量与宏定义不同:const常量由编译器处理,编译器对const常量进行类型检查和作用域检查,宏定义由预处理器处理,单纯的文本替换
#include <stdio.h>
void f()
{
#define a 3
const int b = 4;
}
void g()
{
printf("a = %d\n", a);
//printf("b = %d\n", b); //error: 'b' was not declared in this scope
}
int main()
{
const int A = 1;
const int B = 2;
int array[A + B] = {0};
int i = 0;
for(i=0; i<(A + B); i++)
{
printf("array[%d] = %d\n", i, array[i]);
}
f();
g();
return 0;
}
const常量的判别准则
只有用字面量初始化的const常量才会进入符号表 ,使用其它变量初始化的const常量仍然是只读变量,被volatile修饰的const常量不会进入符号表
注意:在编译期间不能直接确定初始值的const标识符,都被作为只读变量处理!!!
const引用的类型与初始化变量的类型 如果相同:初始化变量成为只读变量 ,如果不同:生成—个新的只读变量
#include <stdio.h>
int main()
{
const int x = 1; // 进入编译器符号表
const int& rx = x; // 只读变量
int& nrx = const_cast<int&>(rx);
nrx = 5; // 修改内存值
printf("x = %d\n", x); // 1 编译器直接从符号表替换
printf("rx = %d\n", rx); // 5 从内存获值
printf("nrx = %d\n", nrx); // 5
printf("&x = %p\n", &x);
printf("&rx = %p\n", &rx);
printf("&nrx = %p\n", &nrx); // &x == &rx == &nrx
volatile const int y = 2; // 不会进入符号表
int* p = const_cast<int*>(&y);
*p = 6;
printf("y = %d\n", y); // 6
printf("p = %p\n", p);
const int z = y; // 只读变量
p = const_cast<int*>(&z);
*p = 7;
printf("z = %d\n", z); // 7
printf("p = %p\n", p);
char c = 'c';
char& rc = c;
const int& trc = c; // 类型不同,生成新的只读变量
rc = 'a'; // 不会影响新的只读变量
printf("c = %c\n", c); // a
printf("rc = %c\n", rc); // a
printf("trc = %c\n", trc); // c
return 0;
}
mutable关键字
目的:统计对象中某个成员变量的访问次数
mutable是为了突破const函数的限制而设计的 ,mutable成员变量将永远处于可改变的状态 ,mutable在实际的项目开发中被严禁滥用
mutable成员变量破坏了只读对象的内部状态 ,const成员函数保证只读对象的状态不变性 ,mutable成员变量的出现无法保证状态不变性
#include <iostream>
using namespace std;
class Test
{
int m_value;
mutable int m_count;
public:
Test(int value = 0)
{
m_value = value;
m_count = 0;
}
int getValue() const
{
m_count++; //const成员函数内部不能直接改变成员变量值,所以需要mutable关键字
return m_value;
}
void setValue(int value)
{
m_count++;
m_value = value;
}
int getCount() const
{
return m_count;
}
};
int main(int argc, char *argv[])
{
Test t;
t.setValue(100);
cout << "t.m_value = " << t.getValue() << endl;
cout << "t.m_count = " << t.getCount() << endl;
const Test ct(200);
cout << "ct.m_value = " << ct.getValue() << endl;
cout << "ct.m_count = " << ct.getCount() << endl;
return 0;
}
下面是更好的解决方案(不用mutable)
#include <iostream>
#include <string>
using namespace std;
class Test
{
int m_value;
int * const m_pCount;
/* mutable int m_count; */
public:
Test(int value = 0) : m_pCount(new int(0))
{
m_value = value;
/* m_count = 0; */
}
int getValue() const
{
/* m_count++; */ //只读对象内部不能直接改变
*m_pCount = *m_pCount + 1;
return m_value;
}
void setValue(int value)
{
/* m_count++; */
*m_pCount = *m_pCount + 1;
m_value = value;
}
int getCount() const
{
/* return m_count; */
return *m_pCount;
}
~Test()
{
delete m_pCount;
}
};
int main(int argc, char *argv[])
{
Test t;
t.setValue(100);
cout << "t.m_value = " << t.getValue() << endl;
cout << "t.m_count = " << t.getCount() << endl;
const Test ct(200);
cout << "ct.m_value = " << ct.getValue() << endl;
cout << "ct.m_count = " << ct.getCount() << endl;
return 0;
}
const成员变量、函数、对象
类中的const成员
类中的const成员会被分配空间的 ,类中的const成员的本质是只读变量 ,类中的const成员只能在初始化列表中指定初始值 (引用数据成员也是)
编译器无法直接得到const成员的初始值,因此无法进入符号表成为真正意义上的常量。
#include <stdio.h>
class Value
{
private:
int mi;
public:
Value(int i)
{
printf("i = %d\n", i);
mi = i;
}
int getI()
{
return mi;
}
};
class Test
{
private:
const int ci;
Value m2;
Value m3;
Value m1;
public:
Test() : m1(1), m2(2), m3(3), ci(100)
{
printf("Test::Test()\n");
}
int getCI()
{
return ci;
}
int setCI(int v)
{
int* p = const_cast<int*>(&ci);
*p = v;
}
};
int main()
{
Test t;
printf("t.ci = %d\n", t.getCI());
t.setCI(10);
printf("t.ci = %d\n", t.getCI());
return 0;
}
const对象
const关键字能够修饰对象 ,const修饰的对象为只读对象 ,只读对象的成员变量不允许被改变 ,只读对象是编译阶段的概念,运行时无效
const成员函数
const对象只能调用const的成员函数 ,const成员函数中只能调用const成员函数 ,const成员函数中不能直接改写成员变量的值(使用mutable可以)
类中的函数声明与实际函数定义中都必须带const关键字。const成员函数的定义:Type ClassName::function(Type p) const
#include <iostream>
using namespace std;
class Test
{
public:
int ci;
Test():ci(1){}
int getCI() const//本质:const成员函数 ==> const Test* const this
{
return ci;
}
void print()
{
cout << "我是非const成员函数" << endl;
}
};
int main()
{
Test t;
cout << t.getCI() << endl;//1
const Test tt;
cout << tt.getCI() << endl;//1
//tt.print();//error: 'this' argument to member function 'print' has type 'const Test', but function is not marked const
Test* ptt = const_cast<Test*>(&tt);
ptt->ci = 0;
cout << ptt->getCI() << endl;//0
ptt->print();
return 0;
}