C/C++面试:尽量以const、enum、inline替换#define

1059 篇文章 278 订阅

#defnie常量

原因

因为#define不被视为语言的一部分

问题

当定义:

#define ASPECT_INFO 1.632

它可能不会被编译器看见:

  • 在编译器开始处理源码之前就被预编译器移走了(将所有的ASPECT_INFO都用1.632代替是预编译器的事)
  • 因此ASPECT_INFO有可能没有进入记号表
  • 这可能导致编译错误不明晰:错误只提到1.632而没有提及ASPECT_INFO

解决方法

用一个常量替换上面的宏:

const double AspectRatio = 1.623; // 大写名称通常用于宏,因此这里改变名称
  • AspectRatio 一定会进入记号表
  • 预处理器盲目的将宏名称ASPECT_INFO 替换为1.632会出现多份1.653,而使用常量解决了这个问题。

注意,这里有两种特殊情况

定义常量指针

  • 由于常量定义式常常被放在头文件内(以便被不同的源码包含),因此有必要将指针(而不只是指针所指之物)声明为const:
const char * const authorName = "Sabe Botter";
  • 注意,这里更推荐使用string而不是char*:
const std::string authorName("Sabe Botter");

类专属常量

  • 为了将常量的作用域限制在类内,必须令该常量成为类的一个成员
  • 为了确保此常量最多只有一个实体,必须令该常量是一个静态成员
class GamePlayer{
private:
	static const int NumTurns = 5;  // 常量声明式
	int scores[NumTurns ];  //使用该常量
};

上面的NumTurns 是声明式而不是定义式:

  • 通常C++要求你对你所使用的任何一个东西提供一个定义式,但是如果它是类专属常量又是static而且为整型(integral type,比如 int、char、bool),则需要特殊处理
  • 只要不取它们的地址,你可以声明并使用它们而无须提供定义式
  • 如果需要取出它们的地址,必须提供一个定义式:
    • 请把这个式子放进一个实现文件而不是头文件
    • 由于类常量已经在声明的时候获得了初值,因此定义时不可以在设置初值
const int GamePlayer::NumTurns;

完全代码:

#include <iostream>

class GamePlayer{
private:
    static const int NumTurns = 5;  // 常量声明式
    int scores[NumTurns ];  //使用该常量
    
    void test();
};
const int GamePlayer::NumTurns;
void GamePlayer::test() {
    printf("%p", &NumTurns);
}

注意:

  • 无法用#define创建一个类专属常量,因为#define并不重视作用域。一旦宏被定义,它就在其后的编译过程中有效(除非在某处被#undef
  • 也就是说,#define不但不能用来定义类专属常量,也不能提供任何封装性。而const成员变量是可以被封装的
#include <iostream>

class GamePlayer{
private:
    #define N  7
};


int main(){
    printf("%d", N);
};

有的编译器不支持类内定义的语法,这时可以使用the enum hack替代(理论基础是:一个属于枚举类型的数值可以当作ints被使用):

  • enums和#define一样不会导致非必要的内存分配
  • 如果你不想让别人获取一个指针或引用指向某个整数常量时,用enum hack而不是const常量(不可能取到enum的地址或者#define常量的地址)
class GamePlayer{
private:
    enum {NumTurns = 5};
    int scores[NumTurns];
};

#defnie函数

宏函数虽然不会导致调用带来的额外开销,但是有太多的缺点,不建议使用:

  • 如果你一定要用的话,必须为宏中所有实参加上小括号
#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b));

建议使用内联函数模板代替:

  • 可以获得宏带来的效率
  • 具有一般函数的所有可预料行为和类型安全性
template<typename T>
inline void callWithMax(const T& a, const T& b){  // 因为不知道T是什么,所有采用pass by reference-to-const
	f(a > b ? a : b);
}

总结

  • 对于常量,最好以const对象或enums替换#define
  • 对于形似函数的宏,最好用inline函数替换#define

#define和const有什么区别

  • 编译器处理方式不同:
    • #define宏是在预处理阶段展开,不能对宏定义进行调试
    • const常量是在编译阶段使用
  • 类型和安全检查不同:
    • #define宏没有连续,不做任何类型检查,仅仅是代码展开,可能产生边际效应等错误
    • const常量有具体类型,在编译阶段会执行类型检查
  • 存储方式不同:
    • #define宏仅仅是代码展开,在多个节点进行字符串替换,因此在运行时系统不会分配内存,存储于程序的代码段中。但是从汇编的角度来看,#define却以立即数的方式保留了多份数据的拷贝
    • const常量会分配内存,存储在程序的数据段中;而且从汇编的角度来讲,const常量在出现的父保留的是真正数据的内存地址,只保留了一份数据的拷贝,省去了不必要的内存空间。而且,有时候编译器不会为普通的const常量分配内存,而是直接将const常量添加到符号表中,省去了读取和写入内存的操作,效率更高
  • 定义域不同:
    • #define宏不受定义域限制
    • const常量只在定义域内有效
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
填充下面这个程序中所有出现// TODO: fill the code这个任务的地方#include <iostream> #include <cstring> #include "ourstring.h" #include "strlib.h" using namespace std; OurString::OurString(){ // TODO: fill the code } OurString::OurString(const char *str){ // TODO: fill the code } OurString::OurString(const OurString &dstr){ // TODO: fill the code } OurString::~OurString(){ // TODO: fill the code } string OurString::toString() const{ // TODO: fill the code } OurString OurString::subStr(unsigned int start, unsigned int n) const{ // TODO: fill the code } bool OurString::operator > (const OurString &dstr) const{ // TODO: fill the code } bool OurString::operator < (const OurString &dstr) const{ // TODO: fill the code } bool OurString::operator == (const OurString &dstr) const{ // TODO: fill the code } unsigned int OurString::length () const{ // TODO: fill the code } const OurString& OurString::operator = (const OurString &dstr){ // TODO: fill the code } const OurString& OurString::operator = (const char *str){ // TODO: fill the code } char& OurString::operator[](int index){ // TODO: fill the code } const OurString OurString::operator + (const OurString &dstr) const{ // TODO: fill the code } const OurString OurString::operator + (const char *str) const{ // TODO: fill the code } const OurString& OurString::operator += (const OurString &dstr){ // TODO: fill the code } const OurString& OurString::operator += (const char *str){ // TODO: fill the code } ostream & operator<<(ostream &os, const OurString &dstr){ // TODO: fill the code } istream & operator>>(istream &is, OurString &dstr){ // TODO: fill the code }
05-29

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值