C++语法易混点(const修饰符,引用)

来讲讲C++中几个易混的点:const修饰符到底怎么用引用&的使用

const修饰符

声明常量

在C语言里我们曾经常使用#define预处理命令声明常量,在C++我们被建议使用const修饰符。
如:

1
2
const int N = 114514;
const double pi = 3.142

这样我们就可以声明常量了,如果我们尝试修改就会报错。

修饰指针

这就是一个很多人经常搞不明白的一个点了,const修饰的指针有两种,如下:(x为已声明的变量)

1
2
const int* p1 = &x;
int* const p2 = &x;

const放int前面,为 “指向const的指针” ,但是他是可以指向非const变量的,只是我们不能通过该指针去修改变量的值,如`p1 = 3这种操作就会报错。不过可以修改指针的值。如p1 = &y`是被允许的。

const放int后面,为 “const指针” , 说明这个指针的值是不能被修改的,即所指的地址是不能被修改的。(一生独一)。但我们可以通过该指针修改变量的值,如`p1 = 3是被允许的,而p1 = &y`就是不行的。

当然,只要你想,你还可以这么定义指针:

1
const int* const p3 = &x;

不过是上面两种结合而已。

修饰函数形参

如我们要定义一个打印A类型的函数,我们可以这么写:

1
void print(const A& x);

const修饰符修饰函数形参时就说明,函数不会修改该参数的值。当我们引用传参(pass by reference)时,可能会不小心修改参数的值,当你加了const修改就会报错,但不加const,也不修改参数的值,也不会报错。但这是一个编程好习惯,让人更清楚程序在做什么,所以写接口之前最好想清楚会不会修改参数的值。能用const尽量const。

修饰函数返回值

当函数返回值为引用时,我们可以将函数作为左值,这一点我们经常在编写类的时候使用。

代码参考知乎用户 学鶸

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<iostream>

using namespace std;

class A
{
private:
int data;
public:
A(int num):data(num){}
~A(){}
int& get_data()
{
return data;
}
};

int main()
{
A a(1);
a.get_data()=3;
cout << a.get_data() << endl; //data=3
return 0;
}

这样我们就修改了a.data的值
这时如果我们不想把函数作为左值,也就是不想通过这个修改成员变量的值,可以把返回值声明为const,写成
const int& get_data(){……}这样子。我们重载赋值运算符时经常使用。

当然不返回引用的时候也可以用const修饰,不过没什么用,主要是可以避免像:

1
(a + b) = c

这种情况发生。

修饰类对象

和const修饰基本类型常量一样,说明了类对象不能被改变。区别只在于:
类对象的 “改变” 定义:改变任何成员变量的值,或调用任何非const成员函数

改变成员变量的值好懂,下面讲讲const成员函数

修饰成员函数

放在函数参数列表(小括号)后面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class A
{
private:
int data;
public:
A(int num):data(num){}
A() = default;
~A(){}

//const 修饰成员函数
int& get_data() const{
return data;
}

//无const 修饰成员函数
void set_data(const int& x){
data = x;
}

//const 修饰成员函数,但修改数据成员,是不被允许的
void set_data_bad(const int& x) const{
data = x;
} //invalid
};

int main(){
using std::cout;
using std::endl;

A x;
const A y;

x.set_data(5); //valid
cout << x.get_data() << endl; //valid

y.set_data(5); //invalid
cout << y.get_data() << endl; //valid
}

上面这段代码所示,const 类对象不能执行非const成员函数
y.set_data(5); y是const对象,所以不能执行非const成员函数set_data
const修饰的成员函数也不能修改该成员的数据。如set_data_bad()这个函数就是错的

const修饰成员函数的本质就是修饰了this指针,像const A this一样,断了在该成员函数中通过this指针修改此对象其他数据的念想。

const 修饰静态类成员

在编写类的时候可能需要一个类中通用的常量,我们可以用枚举(enum)实现,但现在更流行的方式是通过const修饰。

1
2
3
4
5
6
7
8
9
10
11
12
class A
{
private:
int data;
const static int N = 12;//静态类常量
public:
A(int num):data(num){}

int& get_data() const{
return data;
}
};

这样子,我们在所有该类对象都能使用N这个常量了。

引用(&)的用法

传递参数

引用传参可以提高效率,还可以实现C++很多类特性(比如基类指针或引用可以指向派生类),以及修改参数的值,如果不希望修改参数的值,可以用const限定参数,如

1
void set_data(const int& x);

做返回值

参见上面const修饰函数返回值。

就讲这么多吧。