首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >修改std::向量函数(继承?)

修改std::向量函数(继承?)
EN

Stack Overflow用户
提问于 2015-08-05 08:30:27
回答 5查看 993关注 0票数 3

我正在将一些Fortran90代码移植到C++ (因为我很笨,为了保存“为什么?!”)。

Fortran允许指定数组的范围,特别是从负值开始,例如

代码语言:javascript
复制
double precision :: NameOfArray(FirstSize, -3:3)

我可以在C++中这样写

代码语言:javascript
复制
std::array<std::array<double, 7>, FirstSize> NameOfArray;

但是现在我不得不像NameOfArray[0:FirstSize-1][0:6]一样索引。如果我想使用Fortran样式索引进行索引,我可以编写

代码语言:javascript
复制
template <typename T, size_t N, int start>
class customArray
{
public:
    T& operator[](const int idx) { return data_[idx+start]; }
private:
    std::array<T,N> data_;
}

然后

代码语言:javascript
复制
customArray<double, 7, -3> NameOfArray;
NameOfArray[-3] = 5.2;
NameOfArray[3] = 2.5;
NameOfArray[4] = 3.14; // This is out of bounds, 
                       // despite being a std::array of 7 elements

因此-一般的想法是“不要继承std::‘容器类在这里’”。我的理解是,这是因为,例如,std::向量没有虚拟析构函数,所以不应该(不能吗?)被多形性地使用。

是否有其他方法可以使用std::arraystd::vector等,并在重写特定函数的同时“免费”获得它们的函数?

代码语言:javascript
复制
template<typename T, size_t N>
T& std::array<T,N>::operator[](const int idx) { ... };

可能允许我覆盖操作符,但它不能让我访问有关自定义起点的知识--使它完全没有意义。此外,如果我乐观地认为我的所有customArray对象都有相同的偏移量,我可以硬编码这个值--但是我的std::数组坏了(我想)。

我怎么才能避开这一切?(忽略简单的答案-不要--只需根据需要编写myArray[idx-3] )

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2015-08-05 08:49:55

继承标准容器没有问题。这只是通常不鼓励的,因为这会带来一些限制,而这样的继承并不是最初在C++中预测要使用的继承的方式。如果您仔细了解这些限制,就可以在这里安全地使用继承。

您只需要记住,这个没有对进行子类化,这意味着什么。特别是,您不应该使用指针或对该类对象的引用。问题可能是如果您传递一个MyVector<x>*值,其中期望vector<x>*。您也不应该创建动态对象(使用new),因此也不应该通过指向基类的指针创建这些对象--因为析构函数调用不会转发到类的析构函数,因为它不是虚拟的。

不可能阻止将“派生指针”转换为“基本指针”,但可以通过重载&运算符来防止从对象中获取指针。您还可以通过在私有部分声明类中的operator new (或者= delete也应该工作)来防止动态创建该类的对象。

也不要考虑私人继承。这就像将这个东西作为字段包含在私有部分中一样,除了访问器的名称。

票数 6
EN

Stack Overflow用户

发布于 2015-08-05 08:53:50

range转换器类可能是解决方案,尽管您需要自己制作它,但是它允许您获得范围大小来初始化向量并进行转换。

未经测试的代码:

代码语言:javascript
复制
struct RangeConv // [start,end[
{
  int start, end;
  RangeConv(int s, int e) : start(s), end(e) { }
  int size() const { return end - start; }
  int operator()(int i) { return i - start; } // possibly check whether in range
}

RangeConv r(-3, 3);
std::vector<int> v(r.size());
v[r(-3)] = 5;
票数 4
EN

Stack Overflow用户

发布于 2015-08-05 09:23:05

那么不应该(不能吗?)被多形性地使用。

不要太早放弃。在C++中,继承基本上有两个问题要考虑。

生命周期

这些对象是基中含有非虚拟析构函数的派生类,如果您基本上遵循一个简单的规则:不要在任何地方使用delete,则可以安全地以多态方式使用这些对象。这自然意味着您不能使用new。通常,您应该避免使用现代new中的C++和原始指针。shared_ptr将做正确的事情,即只要使用make_shared,就可以安全地调用正确的析构函数。

代码语言:javascript
复制
std:: shared_ptr<Base> bp = std:: make_shared<Derived>( /* constructor args */ );

make_shared的类型参数(在本例中为Derived )不仅控制创建哪个类型。它还控制调用哪个析构函数。(因为底层的共享指针对象将存储一个适当的删除器。)

使用unique_ptr是很有诱惑力的,但不幸的是(默认情况下)它将导致使用错误的删除器(也就是说,它将天真地在基本指针上直接使用delete )。不幸的是,除了默认的unique_ptr之外,标准中并没有一个更安全、但效率更低的unique_ptr_with_nice_deleter

多态

即使std::array有一个虚拟析构函数,这个当前的设计仍然很奇怪。因为operator[]不是虚拟的,所以从customArray*转换到std:: array*会导致错误的operator[]。这并不是一个特定于C++的问题,这基本上就是你不应该假装customArraystd:: array的问题。

相反,只需确定customArray是一个单独的类型。这意味着您不能将一个customArray*传递给一个期望std::array*的函数--但您确定您甚至希望这样做吗?

是否有其他方法可以使用std::array、std::vector等,并在重载特定函数的同时获得它们的函数?

这是个好问题。您不希望您的新类型满足isa std::array。你只想让它表现得和它非常相似。仿佛您神奇地复制并粘贴了std::array中的所有代码来创建一个新类型。然后你想调整一些东西。

使用private继承和using子句输入所需的代码:

代码语言:javascript
复制
template <typename T, size_t N, int start>
struct customArray : private std::array<T,N>
{
    // first, some functions to 'copy-and-paste' as-is
    using std::array<T,N>  :: front;
    using std::array<T,N>  :: begin;

    // finally, the functions you wish to modify
    T& operator[](const int idx) { return data_[idx+start]; }
}

private继承将阻止从customArray *std::array *的转换,这正是我们想要的。

PS:我很少有这样的private继承经验。太多了,这不是最好的解决方案--任何反馈都值得赞赏。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31827291

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档