gpt4 book ai didi

C++:使用指向大型静态数组的指针安全地初始化类

转载 作者:行者123 更新时间:2023-11-27 22:37:12 25 4
gpt4 key购买 nike

我有一个低级嵌入式应用程序,其中有一些相对较大的常量、全局、静态数组(查找表等)。编译器(或链接器)将它们存储在闪存而不是 RAM 中,因为它们是常量。

现在,我有一个类需要用一个这样的数组进行初始化。它将在类对象的整个生命周期中使用该数组中的数据。

我的问题是:如何安全地将指向这个全局静态数组的指针传递给对象,同时防止错误地传递一个生命周期短的数组而不是静态数组?

例如,考虑不防止不正确初始化的简单实现:

class Interpolator
{
public:
Interpolator(const float table[], int size);
float interpolate(float x); // uses 'table' data member
private:
const float* table;
int size;
};


Interpolator::Interpolator(const float table[], int size) :
table(table), size(size)
{
}


const float table1[] = {1.0, 2.0, 42.0 /* a few thousand more */ };

void main()
{
Interpolator interpolator1(table1, sizeof(table1) / sizeof(float));
float x = interpolator1.interpolate(17.0); // OK

float* table2 = new float[1024];
// ... calculate and fill in values in table2 ...
Interpolator interpolator2(table2, 1024); // how to prevent this usage?
delete[] table2; // incorrectly assume the object created a copy for itself and the delete is safe...
float y = interpolator2.interpolate(17.0); // ERROR, undefined behavior
}

如何防止示例中的第二次实例化?也许通过某种方式通过 constexpr,或者模板的一些巧妙用法...?

注意事项:

  • 我意识到这里的问题是我的类不符合 RAII。但是,在上述限制条件下(使用闪存中的大型静态数组),我看不出如何使其符合 RAII。

  • 将数据从静态数组复制到对象中的本地数据成员是不可能的 - 单个数组实际上可能比我的整个 RAM 还大,它的大小只有几十 kB。

  • 我会有多个类的实例,多个静态数据表,类的几个实例可能用同一个静态数据表初始化。

有没有关于强制安全的设计模式的想法?

谢谢!

最佳答案

变量的地址是一个常量表达式。这意味着我们可以使用表的地址作为模板参数。

通过这种方式,我们可以为每个存在的插值表构建一个特定的模板类,而不是其他的。

这消除了创建指向 transient 表的插值器的可能性。

它还具有需要较少存储空间的优势,因为它不需要维护指向数据的指针。

例子:

#include <cstddef>


template<const float* const Table, std::size_t Size>
struct InterpolatorImpl
{
public:
float interpolate(float x)
{
// use Table and Size here as constant expressions
// or write in terms of begin() and end()

return 0;
}

constexpr std::size_t size() const
{
return Size;
}

constexpr const float* begin() const
{
return Table;
}

constexpr const float* end() const
{
return begin() + size();
}
};

const float table1[] = {1.0, 2.0, 42.0 /* a few thousand more */ };
using Interpolator1 = InterpolatorImpl<table1, sizeof(table1) / sizeof(float)>;

const float table2[] = {1.0, 3.0, 5.0 /* a few thousand more */ };
using Interpolator2 = InterpolatorImpl<table2, sizeof(table2) / sizeof(float)>;


int main()
{
Interpolator1 interpolator1;
float x = interpolator1.interpolate(17.0); // OK

float y = Interpolator2().interpolate(21);
}

但是如果在某些情况下我们想要有条件地对一个或另一个表进行插值怎么办?

在这种情况下,我们可以使 InterpolatorImpl 成为多态的,派生自一个公共(public)基础。然后,我们可以为公共(public)基础提供一种基于通过私有(private)虚函数获取的表详细信息执行插值的方法。

#include <cstddef>


struct Interpolator
{
float interpolate(float x) const
{
return interpolate(getDetails(), x);
}

protected:

struct Details
{
const float* first;
std::size_t length;
};

private:
virtual Details getDetails() const = 0;

static float interpolate(Details details, float x)
{
// do interpolation here
auto begin = details.first;
auto size = details.length;


// ...

return 0;
}
};

template<const float* const Table, std::size_t Size>
struct InterpolatorImpl : Interpolator
{
public:
constexpr std::size_t size() const
{
return Size;
}

constexpr const float* begin() const
{
return Table;
}

constexpr const float* end() const
{
return begin() + size();
}

private:
virtual Details getDetails() const override
{
return { Table, Size };
}

friend auto poly(InterpolatorImpl const& i) -> Interpolator const&
{
return i;
}
};

const float table1[] = {1.0, 2.0, 42.0 /* a few thousand more */ };
using Interpolator1 = InterpolatorImpl<table1, sizeof(table1) / sizeof(float)>;

const float table2[] = {1.0, 3.0, 5.0 /* a few thousand more */ };
using Interpolator2 = InterpolatorImpl<table2, sizeof(table2) / sizeof(float)>;

float doInterpolation(Interpolator const& interp, float x)
{
return interp.interpolate(x);
}

bool choice();

int main()
{
Interpolator1 interpolator1;
Interpolator2 interpolator2;

float x = doInterpolation(choice() ? poly(interpolator1) : poly(interpolator2) , 17.0); // OK

}

但是,如果我的编译器有点老,不将变量的地址视为常量表达式怎么办?

然后我们需要对每个插值器进行一些手动操作:

#include <cstddef>
#include <type_traits>


struct Interpolator
{
float interpolate(float x) const
{
return interpolate(getDetails(), x);
}

protected:

struct Details
{
const float* first;
std::size_t length;
};

private:
virtual Details getDetails() const = 0;

static float interpolate(Details details, float x)
{
// do interpolation here
auto begin = details.first;
auto size = details.length;


// ...

return 0;
}

friend Interpolator const& poly(Interpolator const& self) { return self; }
};


const float table1[] = {1.0, 2.0, 42.0 /* a few thousand more */ };
struct Interpolator1 : Interpolator
{
virtual Details getDetails() const override
{
return {
table1,
std::extent<decltype(table1)>::value
};
}
};

const float table2[] = {1.0, 3.0, 5.0 /* a few thousand more */ };
struct Interpolator2 : Interpolator
{
virtual Details getDetails() const override
{
return {
table2,
std::extent<decltype(table2)>::value
};
}
};

float doInterpolation(Interpolator const& interp, float x)
{
return interp.interpolate(x);
}

bool choice();

int main()
{
Interpolator1 interpolator1;
Interpolator2 interpolator2;

float x = doInterpolation(choice() ? poly(interpolator1) : poly(interpolator2) , 17.0); // OK

}

https://godbolt.org/z/6m2BM8

关于C++:使用指向大型静态数组的指针安全地初始化类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52751677/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com