如何根据用户输入设置矢量的类型

How to set a vector's type based on user input

本文关键字:类型 设置 输入 何根 用户      更新时间:2023-10-16

我想避免为不同类型编写整个程序。这就是代码现在的样子:

switch(e) {
    case('i'): {            // if the user wants integers:
        std::vector<long double> v(b);
        std::cout << "nEnter the Minimum Size for the numbers: ";
        std::cin >> c;
        std::cout << "nEnter the Maximum Size for the numbers: ";
        std::cin >> d;
        for (i = 0; i <= b-1; i++)
            a[i] = random_int(c, d);
        .
        .
        .
        sort(a.data(), 0,b-1);
        break;
    }
    case('d'): {           // if the user wants decimal numbers:
        std::vector<long double> v(b);
        std::cout << "nEnter the Minimum Size for the numbers: ";
        std::cin >> m;
        std::cout << "nEnter the Maximum Size for the numbers: ";
        std::cin >> n;
        for (i = 0; i <= b-1; i++)
            a[i] = random_float(m, n);
        .
        .
        .
        sort(v.data(), 0,b-1);
        break;
    }
    default: {
        goto loop;
        break;
    }
}

正如你所看到的,它是不必要的长。

改变矢量的类型似乎不起作用(很难),而且由于random_int/random_float部分(他们使用std::uniform_int_distributionstd::uniform_real_distribution),我无法创建用于生成矢量的函数。

有很多方法可以做到这一点,但没有一种"最佳方法"。但最直接的方法可能是使用继承。根据类型列出所有必须更改的行为,并为每个行为实现函数。多形态地实现它们。例如:

class IntOrDouble
{
    public:
    bool isInt() const { return _isInt; }
    bool isDouble() const { return !_isInt; }
    int getAsInt() const; // throws if not integer
    double getAsDouble() const; // throws if not double
    protected:
    bool _isInt;
    int _valueIfInt;
    double _valueIfDouble;
};
class VectorOfIntsOrDoubles
{
    public:
    void makeVectorOfInts(); // must be empty
    void makeVectorOfDoubles(); // must be empty
    // Reads an IntOrDouble of the appropriate type for this container
    IntOrDouble ReadValue() const;
    // Generates a random IntOrDouble of the appropriate type for this container
    IntOrDouble GenRandom() const;
    private:
    bool _isInts;
    std::vector <IntOrDouble> _vector;
};

等等。

另一种多态方法。首先,我们定义了一个抽象基类,这是只有程序的大多数部分才会知道的。它只定义对数据的访问函数,不包含任何函数(除了kind类型标记)。派生类的大多数访问函数实现只会在运行时抛出,但适用于元素数据类型的实现除外:

/// The type which will alwys be returned
class VecHolder
{
    public:
    enum kindE { INTVEC, DBLVEC, /*...*/} ;
    kindE kind;
    VecHolder(kindE k) : kind(k) {}
    /// These expose the vectors for simplicity. The derived classes
    /// will hold just one of them.
    virtual vector<int> &intVec() = 0;
    virtual vector<double> &dblVec() = 0;
    // ...
    static VecHolder *MakeVec(kindE kind);
};

现在,我们将展示两个具体的实现,用于double和int:

/// An implementation for int elements
class IntVecHolder: public VecHolder
{
    public:
    IntVecHolder() : VecHolder(INTVEC) {} 
    vector<int> &intVec() { return vec; };
    vector<double> &dblVec()  { throw exception();}
    protected:
    vector<int> vec;
};
/// An implementation for double elements
class DblVecHolder: public VecHolder
{
    public:
    DblVecHolder() : VecHolder(DBLVEC) {} 
    vector<int> &intVec() { throw exception();}
    vector<double> &dblVec(){ return vec; };
    protected:
    /// This is the actual data (somewhat) hidden behind the 
    /// abstract VecHolder interface.
    vector<double> vec;
};

既然我们知道了这些类,我们就可以对工厂函数进行编码,该函数根据用户的选择创建适当的VecHolder实现(kind):

/// The factory function: Exact type of object behind returned
/// pointer dpends on parameter, but they are all derived from VecHolder
VecHolder *VecHolder::MakeVec(kindE kind)
{
    switch(kind)
    {
        case INTVEC: return new IntVecHolder; break;
        case DBLVEC: return new DblVecHolder; break;
    }
}

最后,这里是一个使用示例。我发现这比我想象的更尴尬——用户代码总是必须根据持有者的kind进行区分。当然,人们应该尽可能多地隐藏函数中丑陋的切换。

int main()
{
    /// An array of vec holder pointers
    VecHolder *vecHolders[] 
        {   VecHolder::MakeVec(VecHolder::DBLVEC),
            VecHolder::MakeVec(VecHolder::INTVEC)
        };
    for( auto &vhp: vecHolders )
    {
        // we always have to discriminate.
        switch(vhp->kind)
        {
            case VecHolder::DBLVEC: vhp->dblVec().push_back(1.0); break;
            case VecHolder::INTVEC: vhp->intVec().push_back(2); break;
        }
    }
    for( auto &vhp: vecHolders )
    {
        switch(vhp->kind)
        {
            case VecHolder::DBLVEC: 
                cout << vhp->dblVec()[0] << endl; 
            break;
            case VecHolder::INTVEC: 
                cout << vhp->intVec()[0] << endl; 
            break;
        }
        // clean up
        delete vhp; 
    }
}

示例会话:

$ g++ -std=c++11 -o polymorphic-vec polymorphic-vec.cpp && ./polymorphic-vec
1
2