修改std::vector函数(继承?) 我把一些FORTRAN90代码移植到C++(因为我很笨,保存了“为什么?”)。

修改std::vector函数(继承?) 我把一些FORTRAN90代码移植到C++(因为我很笨,保存了“为什么?”)。,c++,c++11,inheritance,operator-overloading,overloading,C++,C++11,Inheritance,Operator Overloading,Overloading,Fortran允许指定数组上的范围,特别是从负值开始,例如 double precision :: NameOfArray(FirstSize, -3:3) 我可以在C++中写这个东西,比如 std::array<std::array<double, 7>, FirstSize> NameOfArray; 然后 customArray<double, 7, -3> NameOfArray; NameOfArray[-3] = 5.2; NameOfArra

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

double precision :: NameOfArray(FirstSize, -3:3)
<>我可以在C++中写这个东西,比如

std::array<std::array<double, 7>, FirstSize> NameOfArray;
然后

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
可能允许我重写操作符,但它不会让我访问有关自定义起点的知识-这使它完全没有意义。此外,如果我乐观地认为我所有的
customArray
对象都会有相同的偏移量,我可以硬编码该值——但是我认为我的std::array被破坏了


我怎样才能避开这件事?(忽略简单的答案-不要-根据需要编写
myArray[idx-3]

继承标准容器没有问题。这仅仅是<>强>一般劝告,因为这会带来一些限制,这样的继承不是最初使用C++预测继承的方式。如果您仔细并意识到这些限制,您可以在这里安全地使用继承

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

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


也不要考虑私有继承。这仅仅像是在私有部分中包含一个字段,除了访问器名称。

范围转换器类可能是解决方案,尽管您需要自己创建,但它允许您获取范围大小以初始化向量并进行转换

未测试代码:

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;
struct RangeConv//[start,end[
{
int开始,结束;
RangeConv(ints,inte):开始(s),结束(e){}
int size()常量{return end-start;}
int运算符()(int i){return i-start;}//可能检查是否在范围内
}
RangeConv r(-3,3);
向量v(r.size());
v[r(-3)]=5;
所以不应该(不能?)多态使用

不要过早放弃。在C++中继承与继承有两个基本问题要考虑。 一生

这种对象,在基类中具有非虚析构函数的派生类,如果基本遵循一个简单的规则,则可以安全地使用,即:不使用<代码>删除> /代码>任何地方。这自然意味着不能使用<代码>新< /C> >。总之,您应该避免在现代C++中使用<代码>新< /C> >和原始指针。

shared\u ptr
将做正确的事情,即安全地调用正确的析构函数,只要您使用
make\u shared

std:: shared_ptr<Base> bp = std:: make_shared<Derived>( /* constructor args */ );
private
继承将阻止从
customArray*
std::array*
的转换,这就是我们想要的


PS:我对像这样的
private
继承几乎没有什么经验。太多了,这不是最好的解决方案-任何反馈都值得赞赏。

一般想法

建议不要从标准向量继承,因为这类构造经常被误解,有些人试图让所有类型的对象都从向量继承,只是为了小的方便

但这条规则不应该成为教条,特别是如果你的目标是建立一个向量类,如果你知道你在做什么

危险1:不一致性

如果有一个非常重要的代码库处理范围为1..size而不是0..size-1的向量,则可以根据此逻辑选择保留该代码库,以避免将数千个-1添加到索引中,+1添加到显示的索引中,+1添加到大小中

一种有效的方法可以是使用以下方法:

template <class T> 
class vectorone : public vector<T> {
    public: 
    T& operator[] (typename vector<T>::size_type n) { return vector<T>::operator[] (n-1); }
const T& operator[] (typename vector<T>::size_type n) const { return  vector<T>::operator[] (n-1); }
};
模板
类vectorone:公共向量{
公众:
T&运算符[](typename向量::size_type n){返回向量::运算符[](n-1);}
常量T&运算符[](typename向量::size_type n)常量{返回向量::运算符[](n-1);}
};
但是你必须在所有向量界面上保持一致:

  • 首先,还有一个
    常量T&operator[]()
    。如果你不重载它,如果你在常量对象中有向量,你就会有错误的行为
  • 然后,上面缺少了,还有一个
    at()
    ,它应该与
    []
  • 然后你必须非常小心地对待构造器,因为有很多构造器,以确保你的论点不会被误解
因此,您有免费的功能,但前面的工作比最初设想的要多。使用更有限的接口和私有向量创建您自己的对象的选项最终可能是一种更安全的方法

危险2:更多不一致性

向量索引是
vector::size_type
。不幸的是,此类型是无符号的。必须仔细分析继承自向量,但使用有符号整数索引重新定义
运算符[]
的影响。根据索引的定义方式,这可能会导致细微的错误

结论:

您可能认为还有更多的工作需要提供一致的
std::vector
接口
std:: shared_ptr<Base> bp = std:: make_shared<Derived>( /* constructor args */ );
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]; }
}
template <class T> 
class vectorone : public vector<T> {
    public: 
    T& operator[] (typename vector<T>::size_type n) { return vector<T>::operator[] (n-1); }
const T& operator[] (typename vector<T>::size_type n) const { return  vector<T>::operator[] (n-1); }
};
template <typename T, size_t N, int start>
class customArray
{
public:
    std::array<T,N> data;

    T& operator[](int idx) { return data[idx+start]; }
    auto begin() { return data.begin(); }
    auto begin() const { return data.begin(); }
    auto end() { return data.end(); }
    auto end() const { return data.end(); }
    auto size() const { return data.size(); }
};

int main() {
    customArray<int, 7, -3> a;
    a.data.fill(5);  // can go through the `data` member...
    for (int& i : a) // ...or the wrapper functions (begin/end).
        cout << i << endl;
}