Go编程语言有这个切片类型,它是一个指向实际数组的指针。这允许将子数组作为实际数组来处理,几乎没有性能开销。这个代码片段是关于为C++数组和向量实现片类型的。
#ifndef SLICE_H
#define SLICE_H
#include <sstream>
#include <stdexcept>
#include <string>
template<class T>
class slice {
private:
T* m_array;
size_t m_length;
public:
slice(T* array, size_t length) {
this->m_array = array;
this->m_length = length;
}
slice(T* array, size_t length, size_t skip) {
this->m_array = array + skip;
this->m_length = length;
}
T& operator[](size_t index) {
if (index >= m_length) {
std::ostringstream os;
os << "Bad index: " << index << ", slice size: " << m_length;
throw std::runtime_error(os.str());
}
return m_array[index];
}
size_t size() {
return m_length;
}
T* begin() {
return m_array;
}
T* end() {
return m_array + m_length;
}
const T* begin() const {
return m_array;
}
const T* end() const {
return m_array + m_length;
}
};
#endif /* SLICE_H */#include <algorithm>
#include <iostream>
#include <vector>
#include "slice.h"
using std::cout;
using std::endl;
using std::vector;
int main(int argc, char** argv) {
int int_array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
//// Should be 4, 5, 6, 7, 8.
slice<int> pizza_slice = slice<int>(int_array, 5, 3);
cout << "Arrays slice size: " << pizza_slice.size() << endl;
for (int i : pizza_slice)
{
cout << i << endl;
}
vector<int> int_vector = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
//// Should be 2, 3, 4
pizza_slice = slice<int>(&int_vector[0], 3, 1);
cout << "Vector slice size: " << pizza_slice.size() << endl;
for (int i : pizza_slice)
{
cout << i << endl;
}
return 0;
}任何批评都是非常感谢的。
发布于 2015-12-29 15:00:45
这个看起来不错。
当你写
slice(T* array, size_t length) {
this->m_array = array;
this->m_length = length;
}默认情况下,构建m_array和m_length,然后分配它们。对于指针和积分类型,这并不是非常昂贵的,但这是一种浪费,最好不要养成这种习惯。更喜欢直接构造:
slice(T* array, size_t length)
: m_array(array)
, m_length(length)
{ }另一方面,您的另一个构造函数可以委托给这个构造函数:
slice(T* array, size_t length, size_t skip)
: slice(array + skip, length)
{ }但另一方面,无论如何也不需要存在。让用户进入他想要开始的地方。我觉得这只是有点臃肿。
size()应该是const,而你错过了T const& operator[](size_t) const。
您还可以提供一个T* data()和T const* data() const,在本例中只是别名begin()。
另外,考虑提供一个不同的构造函数,以便我可以从一个slice<const T>构造一个slice<T>。这是有意义的。
如果您是因为超出范围的索引而抛出的,则应该抛出std::out_of_range。但是通常,避免operator[]抛出。这是你最常使用的东西,所以最好尽量简单。如果您想提供抛出替代方案,标准库所遵循的典型模式是提供一个at()函数,该函数执行边界检查,然后转发到operator[]。
这里的一切都可以是constexpr。你就过火吧。
发布于 2015-12-29 21:12:35
这是一个好的开端,但你做得还不够:
您的类型基本上是一个范围,但仅限于由指针表示的连续类型。
虽然我想知道为什么您认为一个通用的构建块有任何业务生产任何输出没有明确要求?
而且,如果你使用的是制造功能的话,它会更加舒适。
如果您泛化了它,那么使用mem初始化器来初始化您的成员,而不是在ctor-body中分配它们,可能会产生不同的效果。
而且,您的许多成员并不是noexcept,即使他们从不抛出,即使他们很可能是constexpr,size const也不是,即使它没有变异。
如前所述,抱怨超限指数的正确方法是抛出std::out_of_range。
不过只有在那些情况下你才应该检查它们。
考虑使用assert进行额外的检查,这些检查只应在调试-构建中完成。
您还缺少了成员函数cbegin()、cend()和用于反向迭代器的函数,这些函数是空范围的默认构造函数、成员类型value_type、size_type、difference_type、reference、const_reference、pointer、const_pointer、reverse_iterator和const_reverse_iterator。
关于你的测试套件:
std::endl。如果你不需要它,那就不必要地扼杀了你的表现。return 0;隐含在main中。std::vector,但你可能有一个很好的理由.https://codereview.stackexchange.com/questions/115319
复制相似问题