[英]Best practices for run-time polymorphism for Embedded C++? ROMable run-time polymorphism?
我的嵌入式 C++ 項目需要運行時多態性。 在我對嵌入式 c++ 最佳實踐的研究中,我發現建議在靜態內存中聲明對象,並盡可能避免動態分配(new/delete/malloc/free),如果不是完全的話。
下面是我為嵌入式 C++ 中的運行時多態性提出的內容。 這個想法是你動態分配 InterfaceA 對象,立即使用它然后刪除它。 您不持有該接口類的任何實例。 任何持久數據都將從接口類中獲取(例如 get_index() )並存儲在外部。 任何時候需要對接口功能進行操作時,重新分配接口,立即使用並刪除它。 這將盡可能避免使用堆。
這似乎不是完全最優的。 由於我沒有在接口中存儲任何靜態數據,因此我希望有一種方法可以靜態使用該接口。
在避免動態分配的同時進行運行時多態性的最佳方法是什么?
獎勵:有什么方法可以在保持應用程序(主要是)ROMable 的同時進行運行時多態性?
class A
{
public:
int index;
int init(int type);
}
int A::init(int type)
{
Interface* interface = SelectInterface(type);
index = interface->get_index(type);
delete interface;
}
然后我有以下界面:
// ----------- INTERFACES -------------- //
class Interface
{
virtual int get_index() = 0;
}
// This is the interface factory
Interface* SelectInterface(int type)
{
if (type == 0)
{
return new InterfaceA();
}
else if (type == 1)
{
return new InterfaceB();
}
return null;
}
class InterfaceA :: public Interface
{
InterfaceA();
int get_index();
}
int InterfaceA::get_index()
{
return 5;
}
class InterfaceB :: public Interface
{
InterfaceB();
int get_index();
}
int InterfaceB::get_index()
{
return 6;
}
正如其他答案和評論中所建議的,我不建議在嵌入式環境中使用動態內存分配。 對於有限的記憶,有確定性的行為是非常重要的。 當運行裸機時,真的很難/不可能捕捉到內存不足的異常。 使用 RTOS 會給你更多的靈活性,但不會太多。
然而這是可能的。 為了使您的程序在沒有動態內存分配的情況下使用多態性,您可以通過使用 C++11 的聯合或 C++17 的變體來實現這一點。 您可以使用它們來靜態分配內存並稍后初始化實際對象。 要支持多個對象,您可以使用有限大小的數組。
以下示例為您的接口構造一個靜態聯合數組。 工廠函數初始化聯合中的內存。 clear 函數調用析構函數來清除聯合。
#include <array>
template<int SIZE>
class Buffer
{
private:
union DataUnion
{
// These are required. Please check the documenation of unions.
DataUnion() {};
~DataUnion() {};
// Actual data in the union.
InterfaceA a;
InterfaceB b;
};
std::array<DataUnion, SIZE> dataArray;
public:
Buffer() = default;
~Buffer() = default;
// This is the interface factory
Interface* SelectInterface(int type)
{
// This function will return null when there is no space. You can easily deal with this
// compared to an exception.
Interface* pointer = nullptr;
// First check there is space in the array
// Pseudo code
if(dataArray.has_space())
{
// Pseudo code to get a free union
DataUnion& union = GetFreeUnion();
if (type == 0)
{
// Initialize the memory in the union to be of type A.
// Use the placement new.
new &(union.a) InterfaceA{};
pointer = &(union.a);
}
else if (type == 1)
{
// Initialize the memory in the union to be of type B.
// Use the placement new.
new &(union.b) InterfaceB{};
pointer = &(union.b);
}
}
return pointer;
}
// After your done with the object you need to clear the memory.
void Clear(Interface* pointer_to_data)
{
// Pseudo code to find the index in the array.
int index = FindIndex(pointer_to_data)
DataUnion& union = dataArray[index];
// Pseudo code to retrieve the type stored at that index.
// You need to keep track of that, which is not in this example.
int type = GetType(index);
// Now call the destructor of the object to clear the union.
if(type = 0)
{
union.a.~InterfaceA();
}
else if(type = 1)
{
union.b.~InterfaceB();
}
// Update the administration that index is free.
}
};
// Define the buffer.
Buffer<10> buffer;
main()
{
// Initiate an instance of a.
Interface* pA = buffer.SelectInterface(0);
if(nullptr != pA)
{
// Do something.
}
// Initiate an instance of b.
Interface* pB = buffer.SelectInterface(1);
if(nullptr != pB)
{
// Do something.
}
// Stop using a.
buffer.Clear(pA);
// And on
}
您必須考慮以下限制:
在這一點上,我還沒有使用 std::variant 選項。 我懷疑它會更簡單,因為您不需要手動初始化和清除內存。
我希望這回答了你的問題。 是的,可以使用多態並進行靜態分配。
如果您正在尋找有關如何在嵌入式設備中使用 C++ 的更多示例,您可以查看MBED OS的代碼。 我發現它非常清晰的 C++ 代碼(我的觀點)並且我覺得有趣的是,ARM,世界上最大的 mcu 設計者之一,選擇了 C++ 作為他們的代碼。
運行時多態性、RTTI 和動態分配不應在裸機嵌入式系統應用程序中使用。 主要原因是它沒有任何意義。 但也因為嵌入式系統應該始終具有確定性的行為。
與其關注您想要使用的語言特性,不如關注應用程序的目的——您試圖解決的實際問題——然后相應地設計程序。 僅僅為了它而使用語言特性是你最終遇到性能瓶頸、元編程、奇怪的錯誤和一般代碼膨脹的方式。
這實際上是 C 更適合嵌入式系統的主要原因:您不應該使用的大多數功能根本不可用。 您可以用 C++ 編寫裸機程序,但它需要廣泛的技能和紀律,才能知道可以安全使用的 C++ 的哪些受限子集。 不幸的是,大約 90% 的 C++ 程序員不具備這樣做的能力(即使他們自己也這么認為),因此最好完全避免使用 C++。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.