在宏中指定虚拟函数的正确对象和实现

Specify the right object and implementation of a virtual function in a macro

本文关键字:对象 实现 函数 虚拟      更新时间:2023-10-16

我有一个抽象类DNXServo和继承抽象类的类XL320和AX12A,它们是具体的。我在main中的具体类的每个对象都对应于Arduino Mega上的串行端口,并且我有6个具有唯一id的伺服器,每个串行端口都挂钩。为了清晰起见,在我的代码中,我使用了与宏相关联的每个伺服的ID,例如:

    // SERIAL 3- hips - AX12A object
    #define HIP_LEFT_FRONT          11
    #define HIP_LEFT_MIDDLE         12
    #define HIP_LEFT_BACK           13
    #define HIP_RIGHT_FRONT         14
    #define HIP_RIGHT_MIDDLE        15
    #define HIP_RIGHT_BACK          16
    // SERIAL 2 - knees - XL320 object
    #define KNEE_LEFT_FRONT         17
    #define KNEE_LEFT_MIDDLE        18
    #define KNEE_LEFT_BACK          19
    #define KNEE_RIGHT_FRONT        20
    #define KNEE_RIGHT_MIDDLE       21
    #define KNEE_RIGHT_BACK         22       
etc.

目前我的main是这样的:

void setup(){
// Call constructors
    XL320 arms(Serial1, 112500);
    XL320 knees(Serial2, 112500);
    AX12A hips(Serial3, 112500); 
// Write to Servo
    int pos=SetGoalPosition(KNEE_LEFT_FRONT, 250);
}

//return pointer to the object associated with the proper Serial
DNXServo* lookUpServo(int ID){
    if (ID>=11 && ID<=16) return &hips;
    else if (ID>=17 && ID<=22)  return &knees;
    else return &arms;
}
int SetGoalPosition(int ID, int position){
        DNXServo * servo_ptr = lookUpServo(ID);
        return servo_ptr->SetGoalPosition(ID,position);
}
SetGoalPosition实际上是DNXServo类中的虚拟成员函数,在XL320和AX12A中有不同的具体实现。这种情况与许多其他成员函数相同。我当前实现的问题是,我必须在main中为每个成员函数定义一个额外的函数。

我可以避免使用宏吗?理想情况下,我想写像

这样的东西
SERVO(KNEE_LEFT_FRONT).SetGoalPosition(250);

其中SetGoalPosition是任意成员函数的名称,并自动调用lookUpServo,然后调用该虚函数的正确实现。

EDIT: int XL320::SetGoalPosition(int ID, int position)是函数的实际定义。注意,它有两个参数。所以我想把SERVO(KNEE_LEFT_FRONT).SetGoalPosition(250);展开成hips.SetGoalPosition(KNEE_LEFT_FRONT, 250);这样的东西。正如评论中所建议的,引用也可以工作,但随后我需要始终键入两次ID SERVO(KNEE_LEFT_FRONT).SetGoalPosition(KNEE_LEFT_FRONT, 250);

完全去掉SetGoalPosition(int, int)。代码调用应该是这样的:

lookupServo(theId)->SetGoalPosition(42);

那么你就解决了"需要为接口中的每个方法创建一个函数"的问题。虽然对我来说似乎没有必要,但上面的宏应该是这个

#define SERVO(theId) (*(lookupServo(theId)))

现在你可以写你想写的SERVO(5).SetGoalPosition(42)

另一方面,您可以让lookupServo返回其他人提到的引用:

DNXServo& lookUpServo(int ID) {
    if (ID>=11 && ID<=16) return hips;
    else if (ID>=17 && ID<=22)  return knees;
    else return arms;
}

现在你可以用"。"来代替难看的"->",所以我给你的第一行可以稍微改进一下。

lookupServo(theId).SetGoalPosition(42);

注意用点代替"->"。如果需要的话,宏可以更简单一些。

#define SERVO(theId) (lookupServo(theId))

但我想说的是,要么使用我上面展示的第一行代码,并省略SetGoalPosition(int, int)和宏,要么使用使用引用的第二个版本的lookupServo。在这种情况下,不需要返回指针,也没有很好的理由这样做,所以我认为引用版本稍微好一点。

关于将返回指针的函数转换为返回引用的函数的一个小设计注意事项,我想说,您可能喜欢指针返回空指针的能力,但这并不一定是您可以想象的优势(例如,访问冲突)。将一个函数转换为返回一个引用意味着你给自己强加了一个永远不会返回null的契约,这对于表达设计而言可能是一件非常好的事情。在以前您将返回空指针的情况下,问题出现了,您现在应该做什么?答案是在不能返回有效引用时抛出异常。