Можно ли создать виртуальный объект в куче, но не в стеке? - PullRequest
0 голосов
/ 16 мая 2019

Позвольте мне предвосхитить это, сказав, что я из 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
}

Ответы [ 3 ]

1 голос
/ 16 мая 2019

Вы / можете / сделать это, сделав вашу переменную стека ссылкой:

Base& base = Derived(args);

Но в действительности это не приносит вам большой пользы, поскольку ссылка может быть назначена только там, где она объявленатак что лучшее, что вы можете сделать, чтобы избежать построения, это:

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

Вы должны быть очень осторожны с правилами продления срока службы ссылок.Сделайте из этого то, что вы будете:

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

1 голос
/ 16 мая 2019

Оператор:

MyLibraryHAL hal = HAL_RaspberryPi();

ломтики объект HAL_RaspberryPi, поэтому остается только объект MyLibraryHAL.См. Что такое нарезка объектов? .

Вам нужен указатель или ссылка для того, чтобы сделать полиморфные вызовы во время выполнения.Вот почему ваш пример кучи работает.См. Почему полиморфизм не работает без указателей / ссылок? .Вы можете использовать полиморфизм с объектами, которые создаются в стеке, но вам все еще нужен указатель / ссылка для выполнения вызовов, например:

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

Чистые абстрактные методы должны быть переопределены в классах-потомках.Класс, который содержит только чистые абстрактные методы и ничего более, - это самая близкая вещь, которую вы можете получить к «интерфейсу» в других языках.

1 голос
/ 16 мая 2019

Без указателя вы копируете HAL_RaspberryPi в экземпляр MyLibraryHAL, а затем вызываете метод в суперклассе (который не определен).

Так что да, используйте указатель, но вы должны использовать std :: shared_ptr или std :: unique_ptr вместо необработанных указателей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...