首页 > 解决方案 > 使用静态分配的派生类对象初始化基类指针数组

问题描述

我有一些类旨在保存指向某些变量的指针,以及与变量相关的一些其他属性。实际上,我正在尝试为 STM32 设计一个 CANopen 堆栈,并且我打算使用这些类来表示对象字典。

以下是这些类的简化版本:

class Object {
public:
    constexpr Object(int index) : index{index} {}
private:
    int index;
};

class Single : public Object {
public:
    constexpr Single(int index, void* var, std::size_t size)
    : Object{index}, ptr{var}, size{size} {}
private:
    void* ptr;
    std::size_t size;
};

class Array : public Object {
public:
    constexpr Array(int index, void* var, std::size_t size, std::size_t length)
    : Object{index}, ptr{var}, size{size}, length{length} {}
private:
    void* ptr;
    std::size_t size;
    std::size_t length;
};

我希望创建一个这些对象的数组。下面的代码编译没有问题:

int a {1}; // Single variable
float b[] {2.0, 3.0}; // Array

const Object* dictionary[] {
    new Single(0x1000, &a, sizeof(int)),
    new Array(0x1001, b, sizeof(float), std::size(b))
};

问题是,我尽量避免使用堆(动态分配)。嵌入式系统中动态分配的使用是一个有争议的主题,但我主要关心的是内存利用率:STM32 微控制器的闪存 ROM 比 RAM 多得多,因此最好将变量标记为const,因此链接器可以将它们放入 ROM 中内存。

但是当修改如下代码时,它不会编译:

const Object* dictionary[] {
    &Single(0x1000, &a, sizeof(int)),
    &Array(0x1001, b, sizeof(float), std::size(b))
};

它给出了error: taking address of rvalue错误。

我可以想到一种解决方案:

const Single a_obj {0x1000, &a, sizeof(int)};
const Array b_obj {0x1001, b, sizeof(float), std::size(b)};

const Object* dictionary[] {
    &a_obj,
    &b_obj
};

这可以毫无问题地工作,并将a_obj&b_obj放入 ROM。但我觉得它看起来很丑。它迫使我定义和维护额外的变量集,并想象如果你有大约 20 个变量会怎样。有没有更清洁的方法?

标签: c++c++17stm32

解决方案


我有点疯了,所以...

template<int index, auto& var>
constexpr Single Single_v{index, std::addressof(var), sizeof(var));
template<class T, std::size_t N>
constexpr Array MakeArray( int index, T(&arr)[N] ) {
  return {index, arr, sizeof(T), N};
}
template<std::size_t index, auto& Arr>
constexpr Array Array_v = MakeArray( index, Arr );

const Object* dictionary[] {
  &Single_v<0x1000, a>,
  &Array_v<0x1001, b>,
};

这可能会奏效。

请注意,具有相同参数的两个对象最终成为同一个对象。

这些模板变量可以获取它们的地址,并且是constexpr.

我们甚至可以走得更远

template<class T, std::size_t N>
constexpr Array MakeObject(int index, T(&arr)[N]) {
    return {index, arr, sizeof(T), N};
}

template<class T>
constexpr Single MakeObject(int index, T& obj) {
    return {index, &obj, sizeof(T)};
}


template<int index, auto& x>
constexpr auto Object_v = MakeObject(index, x);

现在你可以

const Object* dictionary[] {
  &Object_v<0x1000, a>,
  &Object_v<0x1001, b>,
};

我们得到了编译时多态正确的对象类型。

您可以使用模板参数参数执行的操作有一些限制,但我没有看到这里有任何影响。

活生生的例子


推荐阅读