可以实例化堆上的虚拟对象,但不能实例化堆栈上的虚拟对象吗?

时间:2019-05-16 15:09:14

标签: c++ virtual-functions

首先让我说我来自Java背景,如果我在这里犯了一些愚蠢的错误,请原谅我。...

我正在编写一个C ++库,希望与Arduino以及RaspberryPi兼容。大部分代码都是纯净的标准C ++,但其中一些功能是特定于硬件的(例如GPIO调用)。我以Java为背景,我想创建一个HAL接口来定义那些特定于硬件的功能,然后我可以为RPi和Arduino创建该接口的实现,只是发现接口不是一回事。在C ++中,您必须改为使用虚拟方法。 请查看我问题的底部以获取我的源文件。

但是,当我实际使用它时,我发现了一个很奇怪的东西:在堆上使用HAL进行编译,但是在堆栈上使用HAL则不会

//THIS DOES NOT COMPILE
MyLibraryHAL hal = HAL_RaspberryPi();
hal.someHwSpecificFunction(65);

//BUT THIS DOES
MyLibraryHAL* hal = new HAL_RaspberryPi();
hal->someHwSpecificFunction(65);

错误是:
src.ino:67: undefined reference to MyLibraryHAL::someHwSpecificFunction(unsigned char)

这是为什么?这仅仅是虚拟方法如何工作的功能吗?还是这里我缺少一些概念?


MyLibraryHAL.h

#include <stdint.h>

#ifndef MyLibraryHAL_h
#define MyLibraryHAL_h

class MyLibraryHAL
{
    public:
        virtual void someHwSpecificFunction(uint8_t param);
};

#endif

HAL_Arduino.h

#include <stdint.h>
#include "MyLibraryHAL.h"

#ifndef HAL_Arduino_h
#define HAL_Arduino_h

class HAL_Arduino : public MyLibraryHAL
{
    public:
        void someHwSpecificFunction(uint8_t param);
};

#endif

HAL_Arduino.cpp

#include <stdint.h>
#include "HAL_Arduino.h"

void HAL_Arduino::someHwSpecificFunction(uint8_t param)
{
    //do things
}

HAL_RaspberryPi.h

#include <stdint.h>
#include "MyLibraryHAL.h"

#ifndef HAL_RaspberryPi_h
#define HAL_RapsberryPi_h

class HAL_RaspberryPi : public MyLibraryHAL
{
    public:
        void someHwSpecificFunction(uint8_t param);
};

#endif

HAL_RaspberryPi.cpp

#include <stdint.h>
#include "HAL_RaspberryPi.h"

void HAL_RaspberryPi::someHwSpecificFunction(uint8_t param)
{
    //do things
}

3 个答案:

答案 0 :(得分:1)

没有指针,您将HAL_RaspberryPi复制到MyLibraryHAL实例,然后在超类(未定义)上调用该方法。

是的,请使用指针,但是应该使用std :: shared_ptr或std :: unique_ptr代替原始指针。

答案 1 :(得分:1)

您可以通过将您的堆栈变量作为引用来做到这一点:

Base& base = Derived(args);

但是实际上,它并不会给您带来很多好处,因为只能在声明了引用的地方分配引用,因此,避免构建的最佳方法是:

Base& base = Do1 ? Derived1(args) : Derived2(args);

您必须非常警惕引用的生命周期延长规则。做到这一点,您将:

https://en.cppreference.com/w/cpp/language/reference_initialization#Lifetime_of_a_temporary

答案 2 :(得分:1)

声明:

MyLibraryHAL hal = HAL_RaspberryPi();

切片 HAL_RaspberryPi对象,因此仅剩下一个MyLibraryHAL对象。参见What is object slicing?

您需要一个指针或引用,以便在运行时进行多态调用。这就是为什么您的堆示例起作用的原因。参见Why doesn't polymorphism work without pointers/references?。您可以对在堆栈上实例化的对象使用多态,但是仍然需要一个指针/引用才能进行调用,例如:

HAL_RaspberryPi pi;
MyLibraryHAL &hal = pi;
hal.someHwSpecificFunction(65);
HAL_RaspberryPi pi;
MyLibraryHAL *hal = &pi;
hal->someHwSpecificFunction(65);

对于链接器的“未定义引用”错误,您实际上并没有实现 someHwSpecificFunction()类中的MyLibraryHAL方法,而只是声明了它,因此尝试在MyLibraryHAL对象上调用它时出错。如果您不想提供默认的实现,则可以将该方法声明为“纯抽象”方法:

class MyLibraryHAL {
public:
    virtual void someHwSpecificFunction(uint8_t param) = 0;
};

纯抽象方法必须在子孙类中被覆盖。仅包含纯抽象方法而没有其他任何东西的类是您在其他语言中最接近“接口”的东西。