背景
對于嵌入式專案,我想要一個帶有結構串列的類。這個串列在編譯時是已知的,所以我不應該為此求助于動態記憶體分配。
但是,如何制作一個封裝此陣列的結構/類,而不必將其大小用作模板引數?
模板
我的第一個想法就是這樣做:
struct Point {
const uint16_t a;
const double b;
};
template<size_t n>
struct Profile {
Array<Point, n> points;
Profile(const Array<Point, n> &points) : points(points) {}
};
這里,Profile是存盤/封裝點陣列(2 成員結構)的類。n,陣列的大小,是一個模板引數。
我正在使用Array 的這個實作,類似于std::array,順便說一句,因為我無法訪問這個嵌入式平臺上的 STL。
但是,不,我有另一個使用它的類Profile,現在也必須模板化,因為Profile模板化了陣列的大小:
template<size_t n>
class Runner {
private:
const Profile<n> profile;
public:
Runner(const Profile<n> &profile) : profile(profile) {};
void foo() {
for(auto point : profile.points) {
// do something
}
}
};
可以看出,這個Runner類對 a 進行操作Profile并對其進行迭代。必須模板Runner本身并不是什么大問題,但這Runner反過來又被我專案中的另一個類使用,因為這個其他類呼叫Runner::foo(). 現在我也必須為該類模板!以及使用該類的類等。
這已經失控了!最初只使用一個模板引數來指定大小,現在在我的整個應用程式中傳播。因此,我認為這不是一個好的解決方案。
問題
有沒有辦法在Profileor 中“隱藏”陣列的大小Runner?Runner 只需要對其進行迭代,因此大小原則上應該只影響它的實作,而不影響它的公共介面。不過,我該怎么做呢?
另外,我是否可以完全避免手動指定n,只需將陣列傳遞給Profile' 的建構式并讓編譯器計算出它有多大?當然是在編譯時。我覺得這應該是可能的(考慮到這個陣列在編譯時是已知的),但我不知道具體如何。
其他方法
宏
我可以寫一個宏
#define n 12
并將其包含Profile.h在我實體化 a 的地方和地方Profile。不過,這感覺很臟,我想避免使用宏。
向量
I could avoid this fuss by just using a std::vector (or equivalent) instead, but that is allocated at run-time on the heap, which I would like to avoid here since it shouldn't be necessary.
uj5u.com熱心網友回復:
有沒有辦法在 Profile 或 Runner 中“隱藏”陣列的大小?
是的。解決方案是間接的。您可以指向它,而不是直接存盤物件。你不需要知道你指向的東西的大小。
一個方便的解決方案是指向動態存盤(例如std::vector),因為它允許您將動態大小的物件的生命周期“系結”到成員。這通常不是必需的,您可以改用自動存盤。但是,在這種情況下,您無法系結指向物件的生命周期,并且您必須非常小心,在停止使用它之前不要讓指向物件被破壞。
間接可以在您喜歡的任何級別上完成。如果您在最低級別執行此操作,則只需將陣列存盤在Profile. 事實上,如果該組態檔所做的只是包含一個陣列,那么您就不需要一個類。使用泛型span:
struct Runner {
span<const Point> profile;
void foo() {
for(auto point : profile) {
// do something
}
}
};
Point points[] {
// ... number of points doesn't matter
};
Runner runner {
.profile = points,
};
通過span,我的意思是類似的東西std::span。如果您不能使用標準庫,請使用其他實作。它基本上只是一個指標和大小,帶有方便的模板建構式。
澄清一下,你可以選擇任何兩個,但你不能同時擁有這三個:
- 系結到類的陣列的生命周期(安全)
- 沒有編譯時常量大小
- 沒有動態存盤
- 1,2(無 3)=
std::vector, RAII - 1,3 (no 2) =
std::array, 模板, 無間接 - 2,3 (no 1) =
std::span, 注意生命周期
uj5u.com熱心網友回復:
我將擴展此評論:
這個想法是 Runner 接受 Profiles,不管它們的大小。Runner 需要對其進行迭代,但除此之外,它的行為始終相同。使用 Runner 并呼叫 Runner::foo() 的類不需要知道大小。模板化 Runner 的問題是使用 Runner 的類也需要模板化,使用它的類等等。
只有當類直接使用模板時,這才是一個問題Runner。它具有比實際需要更多的依賴項。如果它不需要知道陣列的大小,那么它不應該知道陣列的大小。如果運行時多型是一個選項,您可以添加一個允許訪問陣列元素的基類,但不需要知道有關陣列大小的任何資訊。以下只是一個草圖:
#include <iostream>
struct RunnerInterface {
virtual int* begin() = 0;
virtual int* end() = 0;
virtual ~RunnerInterface(){}
};
template <unsigned size>
struct Runner : RunnerInterface {
int data[size];
int* begin() override { return data; }
int* end() override { return data size; } // pointer one past the end if fine (it won't get dereferenced)
};
void foo(RunnerInterface& ri) {
for (auto it = ri.begin(); it != ri.end(); it){
*it = 42;
}
}
void bar(RunnerInterface& ri){
for (auto it = ri.begin(); it != ri.end(); it){
std::cout << *it;
}
}
int main() {
Runner<42> r;
foo(r);
bar(r);
}
現在,如果一個類需要一個Runner成員,它們存盤 astd::unique_ptr<RunnerInterface>并且僅在構造時您需要決定陣列的大小(盡管您仍然需要在某處決定大小)。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/418004.html
標籤:
上一篇:Python多重繼承child(parent1,parent2)訪問parent2__init__
下一篇:PHP中的組合與聚合
