「C/C++技术基础」const关键字的理解和使用

1. C/C++中的const

全局 const 修饰的变量在 C++ 语言和 C 语言下情况相同,在 C++ 下无法修改局部 const 修饰的变量,但可以用来初始化数组,是真正意义上的常量。

const注意事项:

  • const 在 C 语言下默认是外部链接属性,在 C++ 下默认是内部链接属性
  • C 语言下的全局变量默认增加了extern 关键字,C++ 中需要手动添加 extern 关键字来增加变量的作用域
  • 在 C 语言下可以使用 #define 定义常量,而在 C++ 下尽量使用 const 来定义常量

2. 尽量以 const、enum、inline 来替换 #define 来定义常量

#define 定义的宏常量是没有类型的,在预处理的时候将使用该常量的地方进行拷贝替换,因此程序在执行过程中系统并不为宏定义的常量分配内存。使用宏定义的常量在编译过程中很难发现运用常量所产生的编译错误,因此应该尽量使用 const 常量来替换宏(#define):

#define ASPECT_RATIO 1.653						// 宏定义的常量
const double AspectRatio = 1.653;
const char * const authorName = "Scott Meyers"; // 定义常量字符串首先定义常量指针,其次定义常量字符串
const string authorName1("Scott Meyers");       // 通常使用 string 更适合

在特定 class 类中使用常量,为了将作用域限制于 class 类中,必须让常量成为 class 类的成员。为确保常量最多只有一份实体,必须让其成为一个 static 成员。此外,#define 不重注作用域,一旦定义就在其后的编译过程中有效,因此我们无法使用 #define 定义一个 class 量,而且不能提供封装性。

class GamePlayer {
private:
    static const int NumTurns = 5;  // 常量声明式
    int scores[NumTurns];           // 使用常量
    /* 使用 enum 可以防止使用指针指向该常量 */
    enum { NumTurns1 = 5 };
};
const int GamePlayer::NumTurns;      // 常量定义式

对于宏定义的宏函数不会产生函数调用而带来的开销,但宏函数在使用过程中有许多需要注意的问题,每一个参数都需要加小括号,即使如此还是会带来其他一些问题:

#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))
int a = 5, b = 0;
CALL_WITH_MAX(++a, b);		// a 累加两次
CALL_WITH_MAX(a, b + 10);	// a 累加一次

上述的例子中,参数 a 的值不同,得到的结果也不同。定义内联函数 inline 可以解决宏函数的问题:

/* 使用内联函数 inline 来替换宏函数 */
template<typename T>
inline void callWithMax(const T& a, const T& b)
{
    f(a > b ? a : b);
}

总结

  • 尽量使用 const 或 enum 来替换 #define 定义常量
  • 使用 inline 函数替换 #define 定义的宏函数

3. 尽可能使用 const

const 允许你指定一个语义约束,告诉编译器其值应该保持不变。const 可以用于 class 外部修饰 global 或 namespace 中的变量,也可以用于 class 内部的 static 和 non-static 成员变量。

char greeting[] = "Hello";
const char* p1 = greeting;          // 指针指向常量,其值不可变
char* const p2 = greeting;          // 指针常量,指针的指向不可变
const char* const p3 = greeting;    // 指针指向和指向的值都不可变

STL 迭代器的作用就像是 T* 指针,声明迭代器就像声明一个指针一样。

void stlConstIterator()
{
    vector<int> vec(10, 1);
    const vector<int>::iterator iter = vec.begin();
    *iter = 10;     // 指针指向的值可以修改,即修改vec[0]=10,指针指向不可修改
    vector<int>::const_iterator cIter = vec.begin();
    ++cIter;        // 指针指向下一个,*cIter == vec[1];
    return;
}

在函数声明中,const 可以于函数返回值、函数参数,以及函数自身产生关联。

class Rational {
public:
    int a;
    Rational(int num) : num(a) {}
};
const Rational operator* (const Rational& lhs, const Rational& rhs);

两个成员函数只有常量性不同,可以重载。

class TextBlock {
private:
    string text;
public:
    TextBlock(string str) { text = str; }
    const char& operator[](size_t position) const { return text[position]; }
    char& operator[](size_t position) { return text[position]; }
};
void print(const TextBlock& ctb)
{
    cout << ctb[0] << endl;     // 调用const TextBlock::operator[]
}

注意

  • 将某东西声明为 const 可以帮助编译器找出错误用法;
  • 编译器强制实施 bitwise constness,而在编程时应使用“概念上的 constness”;
  • const 和 non-const 成员函数有实质等价的实现时,让 non-const 调用 const 可以避免代码重复。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值