前言
在《cosnt关键字到底该怎么用》一文中介绍了C语言中的const关键字,本文说说C++中的const关键字,它的大部分特点和C语言中的类似,所以本文主要针对不同之处。
修饰普通变量—只读
在C语言,虽然表面是不允许被修改,但是看下面的代码:1
2
3
4
5
6
7
8
9
int main(void)
{
const int a = 10;
int *p = &a;
*p = 11;
printf("a=%d\n",a);
return 0;
}
它的输出结果是1
a=11
所以C语言里,表面上它是只读的,然而你违规操作仍然能改变。但是,千万不要写这样的代码!!!另外它们的作用也是一样的,声明一个只读变量,不希望被修改,一旦被修改,编译器将会报错。
与C语言中const关键字不同的是,C++中使用const关键字定义的b变量的值在被改变时会被检测。
看一个例子就明白了:1
2
3
4
5
6
7
8
int main()
{
const int a = 10;
int *p = &a;
*p = 11;
return 0;
}
编译报错如下:1
2
3main.cpp: In function ‘int main()’:
main.cpp:5:14: error: invalid conversion from ‘const int*’ to ‘int*’ [-fpermissive]
int *p = &a;
再次强调:在实际中千万不要写这样的代码,这里只是为了说明问题。
我们常常看到传递const char*参数,像这样:1
void test(const char* str);
却似乎从来没有见到过const int作为参数的函数:1
void test(const int val);
为何?因为前者传递指针的副本,指针指向不会被改变,但可以改变指向的内容;但是int类型参数,它也是传递副本,但是永远不会被函数改变,自然也没有必要加const。更多解释可以参考《传值和传指针》。
同样的,修饰函数返回值时,修饰内置类型与不加const修饰是一样的,但对于自定义类型,不能对返回值进行修改,即返回的是常量。
作用在成员函数-不改变成员变量
举个例子:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Test
{
private:
int a;
public:
void printA() const
{
a = 10;
}
};
int main()
{
Test test;
test.printA();
return 0;
}
类test中有一个成员变量a,并且有一个成员函数printA,现在假设你的设计是printA函数不会改变任何成员变量,那么你可以在printA函数后加上const关键字,这样一旦函内部尝试修改成员变量,都会报错:1
2
3main.cpp: In member function ‘void Test::printA() const’:
main.cpp:9:13: error: assignment of member ‘Test::a’ in read-only object
a = 10;
正因如此,const修饰成员函数不与static关键字同用,因为static修饰的静态成员函数不能实例化,也就没有实例的成员变量一说,自然不存在修改成员变量。
即下面的声明是非法的:1
static void printA() const
修饰类成员变量—构造函数中初始化
与修饰普通变量不同的是,修饰类成员变量还可以在构造函数中初始化。如:1
2
3
4
5
6
7
8
9
10
11
12
13
class Test
{
private:
const int a;
public:
Test(int val):a(val){}
};
int main()
{
Test test(1);
return 0;
}
修饰引用
例如,有一个常量1
const int a = 10;
你不能再这样定义它的引用:1
int &ref = a;
而需要定义对常量的引用,即:1
const int &ref = a;
为什么呢?因为不能直接为a赋值,也不能间接赋值,所以自然不能定义普通引用去间接改变它。
总结
关于const关键字在C和C++中的区别,想必到这里你已经清楚了。
如果你对下面的问题还不清楚,建议阅读《cosnt关键字到底该怎么用》
你能分清下面的声明区别吗?1
2
3const int *p;
int * const p;
int const * const p;