C ++如何在泛型编程中声明自定义数组

时间:2015-10-25 16:09:56

标签: c++ templates generic-programming

我有一个课程模板

template <class T> class Collection
{
private:
    int size;
    int type;
    T* Arr;
    int Case;

public:

void ArrayGenerating() {
    switch(type) {
    case 1: 
        Arr = new T[size];

        for (int i = 0; i < size; i++) {
            srand((unsigned)time(NULL)); 
            Arr[i] = static_cast <T> (rand()) % size;
        }

    case 2:
        Arr = new T[size];

        for (int i = 0; i < size; i++) {
            srand((unsigned)time(NULL)); 
            Arr[i] = static_cast <T> (rand()) / (static_cast <T> (RAND_MAX/size));
        }

    case 3:
        Arr = new T[size];

        for (int i = 0; i < size; i++) {
            srand((unsigned)time(NULL)); 
            Arr[i].setNumerator(static_cast <int> (rand()) % size);

            srand((unsigned)time(NULL));
            Arr[i].setDenominator(static_cast <int> (rand()) % size);
        }

    }
  }
};

我想创建一个通用数据类型的随机数组

类型1,即整数数组。类型2,浮点数组。类型3,我有一个自定义数据类型&#34;分数&#34;。但是当我编译程序时,有错误:

Error   1   error C2228: left of '.setNumerator' must have class/struct/union

Error   2   error C2228: left of '.setDenominator' must have class/struct/union

那么如果有这种并发症的解决方案吗?

3 个答案:

答案 0 :(得分:2)

我猜,type是一个常数,取决于T.否则当T是浮点数时,将T *指向int是没有意义的。如果这是真的,则根本没有必要。

我认为,您正在寻找的是模板专业化(未经测试的代码):

// this is common to all cases.
class CollectionBase {
  protected:
    int size;
};

// the general template is not defined
// the compiler will complain whenever T is neither int, nor float, nor fraction.
template<class T> class Collection;

// here come the specializations
template<> class Collection<int>: private CollectionBase
{
  private:
    int* Arr;    
  public:
    void ArrayGenerating() {
      Arr = new int[size];
      for (int i = 0; i < size; i++) {
        srand((unsigned)time(NULL)); 
        Arr[i] = static_cast<int>(rand()) % size;
      }
    }
};

template<> class Collection<float>: private CollectionBase
{
  private:
    float* Arr;
  public:
    void ArrayGenerating() {
      Arr = new float[size];

      for (int i = 0; i < size; i++) {
          srand((unsigned)time(NULL)); 
          Arr[i] = static_cast<float>(rand()) / (static_cast<float>(RAND_MAX/size));
      }
    }
};

template<> class Collection<fraction>: private CollectionBase
{
  private:
    fraction* Arr;
  public:
    void ArrayGenerating() {
      Arr = new fraction[size];

      for (int i = 0; i < size; i++) {
          srand((unsigned)time(NULL)); 
          Arr[i].setNumerator(static_cast <int> (rand()) % size);

          srand((unsigned)time(NULL));
          Arr[i].setDenominator(static_cast <int> (rand()) % size);
      }
    }
};

请注意,这种代码很危险。考虑std::vector<>而不是自己管理动态分配的数组。

另外请注意,根据经验,只要构造函数完成,您的类的所有方法都应该可以安全地调用。在您的代码中,访问Arr的任何函数都会在ArrayGenerating()运行之前使用指向某个内存的随机指针。每当你因某种原因打电话给ArrayGenerating()两次时,你的代码就会泄漏内存,因为你在创建一个新数组之前从不费心delete[]

C ++为内存管理提供的最佳工具是构造函数和析构函数。当您封装每个资源时,您最好在一段时间内在处理程序对象中释放。在这种情况下,std::vector已经满足您的需求。

所以这是一个完整的(但未经测试的)最通用的解决方案。我从一个自由函数开始创建随机数:

template<typename T> struct dist{
    using uniform = std::uniuniform_int_distribution<T>;
};
template<> struct dist<float> {
    using uniform = std::uniuniform_real_distribution<float>;
};

template<typename T>
std::vector<T> createRandomNumbers(size_t s) {
    auto e1 = std::default_random_engine{std::random_device{}()};
    auto u = dist<T>::uniform{0, static_cast<T>(s)};

    auto r = std::vector<T>(s, 0);
    for( auto& i: r ) i = u(e1);

    return r;
}
// fraction need a specialization
template<>
std::vector<fraction> createRandomNumbers<fraction>(size_t s) {
    auto e1 = std::default_random_engine{std::random_device{}()};
    auto u = dist<int>::uniform{0, static_cast<int>(s)};

    auto r = std::vector<fraction>(s, 0);
    for( auto& i: r ) {
          i.setNumerator(u(e1));
          i.setDenominator(u(e1));
    }

    return r;
}

现在我们实现了一个类似你的Collection类模板,如果我们真的需要它:

template <typename T> Collection {
    private:
        // this will handle all your memory management needs
        std::vector<T> randoms;
    public:
        Collection(size_t s) :
            randoms{createRandomNumbers<T>(s)}
        {};

        createNewRandoms(size_t s) {
            std::swap(randoms, createRandomNumbers<T>(s));
        };
        // whatever else is necessary
};

答案 1 :(得分:0)

你为什么要这样做,让你的生活变得更加艰难? 它可以这么简单:

#include <iostream>
#include <chrono>
#include <random>

template<class type_t, std::size_t size>
class Array
{
private:
    type_t arr[size];
public:
    Array()
    {
        for (std::size_t i = 0; i < size; ++i)
        {
            //nice C++ random number generation
            auto seed = static_cast<unsigned>(std::chrono::system_clock::now().time_since_epoch().count());
            std::minstd_rand0 randm(seed);

            arr[i] = randm();
        }
    }

    //test function
    void print()
    {
        for (int i = 0; i < size; ++i)
            std::cout << arr[i] << " ";
    }
};

int main() 
{
    Array<int, 4> arr;
    arr.print();

    std::cin.get();
}

尝试摆脱C风格的C ++。加入黑暗面。

答案 2 :(得分:0)

注意:我不会评论您对C函数的使用或其他问题。其他人已经告诉过你如何避免它们。

类模板中的type成员以某种方式违背了泛型编程的目的,不是吗?您应该删除type并将switch替换为模板专业化。

这是一个简单的例子,可以帮助您入门:

// Collection for all T except of `fraction`
template <class T> class Collection
{
private:
    int size;
    T* Arr;
    int Case;

public:
    void ArrayGenerating() {
        Arr = new T[size];

        for (int i = 0; i < size; i++) {
            srand((unsigned)time(NULL)); 
            Arr[i] = static_cast <T> (rand()) % size;
        }
    };
};

// Collection for `fraction`
template <> class Collection<fraction>
{
private:
    int size;
    fraction* Arr;
    int Case;

public:
    void ArrayGenerating() {
        Arr = new fraction[size];

        for (int i = 0; i < size; i++) {
            srand((unsigned)time(NULL)); 
            Arr[i].setNumerator(static_cast <int> (rand()) % size);

            srand((unsigned)time(NULL));
            Arr[i].setDenominator(static_cast <int> (rand()) % size);
        }
    }
};

这是最简单的模板专业化,但可能导致大量代码重复。不过,你也可以解决这个问题。例如,您可以将所有公共部分提取到公共私有基类中,如下所示:

namespace detail
{
    template <class T> class CollectionBase
    {
    protected:
        int size;
        T* Arr;
        int Case;
    };
}

template <class T> class Collection : detail::CollectionBase<T>
{
public:
    void ArrayGenerating() {
        Base::Arr = new T[Base::size];

        for (int i = 0; i < Base::size; i++) {
            srand((unsigned)time(NULL)); 
            Base::Arr[i] = static_cast <T> (rand()) % Base::size;
        }
    };
private:
    using Base = detail::CollectionBase<T>;
};

template<> class Collection<fraction> : detail::CollectionBase<fraction>
{
public:
    void ArrayGenerating() {
        Base::Arr = new fraction[Base::size];

        for (int i = 0; i < size; i++) {
            srand((unsigned)time(NULL)); 
            Arr[i].setNumerator(static_cast <int> (rand()) % size);

            srand((unsigned)time(NULL));
            Arr[i].setDenominator(static_cast <int> (rand()) % size);
        }
    }
private:
    using Base = detail::CollectionBase<fraction>;
};

通常,阅读有关模板专业化的更多信息,您一定会找到正确的解决方案: