【问题标题】:Can instantiate a virtual object on the heap, but not on the stack?可以在堆上实例化一个虚拟对象,但不能在栈上?
【发布时间】:2019-05-16 15:09:13
【问题描述】:

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

我正在编写一个 C++ 库,我希望它与 Arduino 和 RaspberryPi 兼容。大多数代码只是干净的标准 C++,但有一些函数是特定于硬件的(例如 GPIO 调用)。作为一个 Java 背景,我想,我将创建一个 HAL 接口来定义那些特定于硬件的功能,然后我可以为 RPi 和 Arduino 创建该接口的实现,结果发现接口不是一个东西在 C++ 中,您必须使用虚拟方法。 请在我的问题底部查看我的源文件。

但是,当我实际使用它时,我发现了一个很奇怪的东西:在堆上使用 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
}

【问题讨论】:

标签: c++ virtual-functions


【解决方案1】:

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

所以是的,使用指针,但你应该使用 std::shared_ptr 或 std::unique_ptr 而不是原始指针。

【讨论】:

  • 你的意思是超类需要定义那个方法?
  • 它必须在某个地方定义,在 PURE func()=0 样式方法的情况下由父类(不是 PURE)或由子类定义。如果您没有定义实现并尝试在堆中实例化,您将获得运行时惊喜。
  • 希望这会有所帮助,@Android 开发人员
  • 感谢您实际上解释了为什么我的 sn-p 没有编译,而不是告诉我读一本 C++ 书(我坦率地承认,这将是一件好事)
  • 注意,AFAIK、unique_ptrshared_ptr 不是 Arduino 工具链的一部分,但您可以使用 Tools\Manage libraries... 并将 ArduinoSTL 库添加到您的工具链中。然后执行:Sketch\Include library 并在列表底部找到ArduinoSTL。然后执行#include &lt;memory&gt;,您现在可以访问很旧的std::auto_ptr,但总比没有好,所以您可以执行std::auto_ptr&lt;MyLibraryHAL&gt; hal( new HAL_RaspberryPi() );,并在智能指针超出范围时自动销毁您的HAL_RaspberryPi 实例。跨度>
【解决方案2】:

您/可以/通过将堆栈变量作为参考来做到这一点:

Base& base = Derived(args);

但实际上它并没有给你很多好处,因为引用只能在声明它的地方分配,所以你可以做的最好的避免构造是:

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

您必须非常警惕引用的生命周期延长规则。随心所欲:

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

【讨论】:

    【解决方案3】:

    声明:

    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);
    

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

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

    必须在后代类中重写纯抽象方法。一个只包含纯抽象方法而不包含其他任何东西的类是最接近其他语言的“接口”的东西。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-03-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-21
      • 2022-11-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多