面试中经常被问到的【宏定义】,改变你对【C\C++】中宏定义的认识。

        最近遇到挺多宏定义的代码,其实挺烦的,每次看复杂的宏定义看到一半就懵了,今天盘一盘它。本篇设计宏定义的原理、使用方法、使用技巧。

目录

 一、宏定义原理

二、宏定义定义复杂功能函数

2.1 定义注册函数

三、宏定义实现条件编译

四、宏定义实现代码重用

4.1 代码重用

4.2 宏定义实现简单的类型转换


 一、宏定义原理

        C++的宏定义是一种预处理器指令,它可以用来在编译阶段进行简单的文本替换。当编译器遇到宏定义时,它将会把宏定义中的符号替换为其定义的文本内容,然后再继续编译程序。

        具体来说,C++代码在编译之前首先被送到预处理器进行预处理,预处理器对代码进行扫描和处理,将所有的宏定义替换为宏定义中指定的内容,生成预处理后的代码,然后编译器再对预处理后的代码进行编译生成目标代码。

        下面是一个简单的宏定义示例:

#define PI 3.1415926


#define SQUARE(x) ((x) * (x))

int main() {
    int a = 5;
    int b = SQUARE(a);
    return 0;
}

        在这个示例中,编译器会把所有出现的“PI”符号替换为其定义的文本内容“3.1415926”。宏定义SQUARE(x)将参数x的平方作为返回值。当宏定义被调用时,编译器将把函数调用中的参数替换成宏定义中的文本内容,从而实现了计算参数平方的功能。

tips:

需要注意的是,函数宏并不是真正的函数,它没有函数调用的开销和参数检查等功能。另外,由于宏定义只是简单的文本替换,因此在使用函数宏时需要注意其可能带来的意外行为,比如参数求值次数不确定、符号重定义等问题。

        这样的宏定义是你熟悉的,下面增加难度。

二、宏定义定义复杂功能函数

2.1 定义注册函数

第一个例子:

        在C++中,可以使用宏定义来模拟注册函数。注册函数是指程序在运行时向一个注册表中添加一个函数指针,从而允许在需要时动态调用这个函数。以下是一个简单的示例:

#include <vector>
#define REGISTER_FUNCTION(FUNC_NAME) \
    static void FUNC_NAME##_register() __attribute__((constructor)); \
    static void FUNC_NAME##_register() { \
        function_registry.push_back(&FUNC_NAME); \
    } \
    void FUNC_NAME()

std::vector<void (*)()> function_registry;

REGISTER_FUNCTION(my_function) {
    // 函数实现
}

        在上面的示例中,宏定义REGISTER_FUNCTION(FUNC_NAME)定义了一个注册函数,该函数在程序运行时向全局的函数注册表中添加函数指针。宏定义中使用了一些技巧来实现函数的自动注册,具体如下:

  • FUNC_NAME##_register() 定义了一个静态函数,函数名为FUNC_NAME_register,用于在程序启动时自动调用并向注册表中添加函数指针。这里使用了函数名连接符##,将宏定义中的函数名和_register连接起来,生成新的函数名。
  • __attribute__((constructor)) 是GNU C编译器提供的一种函数属性,表示该函数将在程序启动时自动调用。这里将函数FUNC_NAME##_register()设置为该属性,以实现自动注册的功能。
  • function_registry.push_back(&FUNC_NAME) 将函数指针&FUNC_NAME添加到全局的函数注册表中。
  • void FUNC_NAME() 定义了实际的函数实现,这里使用了空函数体,具体的函数实现可以根据需求进行修改。

第二个例子:

#include <iostream>
#include <string>
#include <unordered_map>

using namespace std;

// 定义一个宏,用于简化类的注册函数的定义
#define REGISTER_CLASS(class_name)                                          \
    class class_name;                                                       \
    namespace {                                                             \
        struct Register_##class_name {                                       \
            Register_##class_name() {                                        \
                ClassFactory::getInstance().registerClass(#class_name, []() -> void* { return new class_name; }); \
            }                                                               \
        };                                                                  \
        static Register_##class_name s_register_##class_name;                \
    }                                                                       \

// 类工厂,用于注册和创建类
class ClassFactory {
public:
    static ClassFactory& getInstance() {
        static ClassFactory instance;
        return instance;
    }

    void registerClass(const string& className, void* (*creator)()) {
        m_classRegistry[className] = creator;
    }

    void* createClass(const string& className) {
        auto it = m_classRegistry.find(className);
        if (it == m_classRegistry.end()) {
            return nullptr;
        }
        else {
            return it->second();
        }
    }

private:
    unordered_map<string, void* (*)()> m_classRegistry;
};

// 定义一个基类
class BaseClass {
public:
    virtual ~BaseClass() {}
    virtual void print() = 0;
};

// 定义一个派生类1
class DerivedClass1 : public BaseClass {
public:
    void print() override {
        cout << "DerivedClass1" << endl;
    }
};

// 定义一个派生类2
class DerivedClass2 : public BaseClass {
public:
    void print() override {
        cout << "DerivedClass2" << endl;
    }
};

// 注册类
REGISTER_CLASS(DerivedClass1);
REGISTER_CLASS(DerivedClass2);

// 主函数
int main() {
    auto obj1 = static_cast<BaseClass*>(ClassFactory::getInstance().createClass("DerivedClass1"));
    obj1->print();

    auto obj2 = static_cast<BaseClass*>(ClassFactory::getInstance().createClass("DerivedClass2"));
    obj2->print();

    return 0;
}

在上面的代码中,我们使用了#define宏定义了一个宏REGISTER_CLASS,用于简化类的注册函数的定义。这个宏定义的具体实现包括以下几个步骤:

  1. 定义一个类对象,这个类对象用于触发类的注册操作。

  2. 定义一个匿名命名空间,在这个命名空间中定义一个结构体Register_##class_name,这个结构体在构造函数中调用类工厂的registerClass函数,将类的名称和一个创建类的函数关联起来。在结构体定义之后,我们创建一个静态结构体对象s_register_##class_name,这个对象的唯一作用就是调用结构体的构造函数,从而触发类的注册操作。

  3. 最后,我们在类的定义后面使用REGISTER_CLASS宏来注册这个类。

使用宏定义可以让我们在类的定义中加入很少的代码,就可以将类注册到类工厂中。

三、宏定义实现条件编译

        为实现代码的移植能力,我们可以使用宏定义设置编译条件。例如实现调试模式、跨平台支持等。这种宏定义通常在开发过程中使用较多。

#ifdef DEBUG
    // 调试模式代码
#endif

#ifdef _WIN32
    // Windows 平台代码
#endif

#ifdef __linux__
    // Linux 平台代码
#endif

          另一个例子:

#include <iostream>
using namespace std;

#define DEBUG

int main() {
    #ifdef DEBUG
        cout << "Debug version" << endl;
    #else
        cout << "Release version" << endl;
    #endif

    return 0;
}

         我们使用了#define宏定义了一个名为DEBUG的宏。在主函数中,我们使用#ifdef指令和#endif指令将一段代码包裹起来,这段代码只有在DEBUG宏被定义的情况下才会被编译。在这个例子中,我们只是简单地输出一句话,但在实际的应用中,我们可以在条件编译中包含或者排除一些特定的代码,以实现特定的功能或者优化。   

        需要注意的是,条件编译是在预处理阶段完成的,而不是在编译阶段。在预处理阶段,预处理器会扫描源代码中的宏定义和条件编译指令,并根据它们生成一份新的代码,这份新的代码会成为编译器的输入。因此,在编译时,条件编译指令不会再起作用,已经被处理掉了

        我们不能将宏定义当成if else来使用,会出问题的。

四、宏定义实现代码重用

        例如实现多次调用相同的代码片段、实现简单的类型转换等。这种宏定义通常使用较多。

#define MY_MACRO(code) do { \
    // 执行代码片段 code \
    // ... \
} while (0)

#define CAST(type, ptr) reinterpret_cast<type>(ptr)

4.1 代码重用

#include <iostream>

#define PRINT_MSG(msg) cout << "Message: " << msg << endl

using namespace std;

int main() {
    PRINT_MSG("Hello World!");
    PRINT_MSG("Welcome to the world of macros!");

    return 0;
}

        我们使用了#define宏定义了一个名为PRINT_MSG的宏。这个宏接受一个参数msg,在控制台输出一个以"Message:"开头的字符串和msg参数。在main函数中,我们两次调用这个宏,分别输出了"Hello World!""Welcome to the world of macros!"

4.2 宏定义实现简单的类型转换

#include <iostream>

#define FLOAT_TO_INT(x) (int)(x)

using namespace std;

int main() {
    float f = 3.14159;
    int i = FLOAT_TO_INT(f);

    cout << "The value of float f is " << f << endl;
    cout << "The value of int i is " << i << endl;

    return 0;
}

        在上面的例子中,我们使用了#define宏定义了一个名为FLOAT_TO_INT的宏。这个宏接受一个浮点数类型的参数x,将其强制转换为整数类型,并返回整数值。在main函数中,我们定义了一个浮点数变量f,并将其赋值为3.14159。然后,我们调用FLOAT_TO_INT宏,将f转换为整数类型,并将转换后的值赋值给整数变量i。最后,我们分别输出了fi的值。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Thomas_Lbw

欣赏我就赏赐给我吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值