我在C ++中编写了一个固定大小的容器(确切地说是一个环形缓冲区)。目前我在构造函数中设置容器的大小,然后在堆上分配实际的缓冲区。但是,我一直在考虑将size参数从构造函数中移出到模板中。
从这里开始(RingBuffer拟合100个整数)
RingBuffer<int> buffer(size);
到这个
RingBuffer<int, 100> buffer;
据我所知,这将允许我在堆栈上分配整个缓冲区,这比堆分配更快。主要是可读性和可维护性问题。这些缓冲区通常显示为类的成员。我必须用大小初始化它们,所以我必须在类的每个构造函数的初始化列表中初始化它们。这意味着如果我想更改RingBuffer的容量,我必须记住在每个初始化列表中更改它,或者使用笨拙的static const int BUFFER_SIZE = 100;
成员变量。
我的问题是,将容器大小指定为模板参数是否有任何缺点,而不是在构造函数中?这两种方法的优点和缺点是什么?
据我所知,编译器将为每个不同大小的RingBuffer生成一个新类型。这可能会变成很多。这会伤害编译时间吗?它会使代码膨胀还是阻止优化?当然我知道这很大程度上取决于确切的用例,但做出这个决定时我需要注意哪些事项?
答案 0 :(得分:2)
我的问题是,将容器大小指定为模板参数是否有任何缺点,而不是在构造函数中?这两种方法的优点和缺点是什么?
如果您将size
作为模板参数,那么它需要是constexpr
(编译时常量表达式)。因此,您的缓冲区大小不能取决于任何运行时特征(如用户输入)。
作为一个编译时常量为一些优化打开了大门(循环展开和常量折叠让我更加高效)。
据我所知,编译器会为每个不同大小的RingBuffer生成一个新类型。
这是事实。但我不担心这一点,因为拥有许多不同类型本身不会对性能或代码大小产生任何影响(但可能在编译时)。
这对编译时间有多大影响吗?
它会使编译速度变慢。虽然我怀疑在你的情况下(这是一个非常简单的模板),这甚至会引人注意。因此,这取决于你对&#34;多&#34;。
的定义是否会使代码膨胀或阻止优化?
防止优化?没有。膨胀代码?有可能。这取决于您实现类的具体程度以及编译器的功能。例如:
template<size_t N>
struct Buffer {
std::array<char, N> data;
void doSomething(std::function<void(char)> f) {
for (size_t i = 0; i < N; ++i) {
f(data[i]);
}
}
void doSomethingDifferently(std::function<void(char)> f) {
doIt(data.data(), N, f);
}
};
void doIt(char const * data, size_t size, std::function<void(char)> f) {
for (size_t i = 0; i < size; ++i) {
f(data[i]);
}
}
doSomething
可能会被编译为(可能完全)展开的循环代码,并且您有Buffer<100>::doSomething
,Buffer<200>::doSomething
等等,每个都可能是一个大函数。 doSomethingDifferently
可能只编译成一个简单的跳转指令,因此拥有多个这些指令并不是一个大问题。虽然您的编译器也可以将doSomething
更改为类似doSomethingDifferently
,但反之亦然。
所以最后:
请勿尝试做出此决定取决于性能,优化,编译时间或代码膨胀。 确定在您的情况下哪些更有意义。是否只有编译时间已知大小的缓冲区?
此外:
这些缓冲区通常显示为类的成员。我必须用大小初始化它们,所以我必须在类的每个构造函数的初始化列表中初始化它们。
你知道&#34;委派施工人员&#34;?
答案 1 :(得分:2)
正如Daniel Jour已经说过,代码膨胀不是一个大问题,如果需要可以处理。
将大小设置为constexpr
的好处是,它允许您在编译时检测到一些错误,否则这些错误将在运行时发生。
据我所知,这将允许我在堆栈上分配整个缓冲区,这比堆分配更快。 这些缓冲区通常显示为类
的成员
只有在自动内存中分配了拥有类时才会发生这种情况。通常情况并非如此。请考虑以下示例:
struct A {
int myArray[10];
};
struct B {
B(): dynamic(new A()) {}
A automatic; // should be in the "stack"
A* dynamic; // should be in the "heap"
};
int main() {
B b1;
b1; // automatic memory
b1.automatic; // automatic memory
b1.automatic.myArray; // automatic memory
b1.dynamic; // automatic memory
(*b1.dynamic); // dynamic memory
(*b1.dynamic).myArray; // dynamic memory
B* b2 = new B();
b2; // automatic memory
(*b2); // dynamic memory
(*b2).automatic; // dynamic memory
(*b2).automatic.myArray; // dynamic memory
(*b2).dynamic; // dynamic memory
(*(*b2).dynamic).myArray; // dynamic memory
}