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

C++ how to declare an self defined array in generic programming

我有一个class模板

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,我有一个自定义的数据类型"fraction"。但是当我编译程序时,出现错误:

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

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

那么对于这个并发症有什么解决办法吗?

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

#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++。加入黑暗面。

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


class 模板中的 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);
        }
    }
};

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

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>;
};

一般来说,阅读更多关于模板专业化的内容,您一定会找到正确的解决方案:

我想,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<> 而不是自己管理动态分配的数组。

另请注意,根据经验,您的 class 的所有方法都应在构造函数完成后立即安全调用。在您的代码中,任何访问 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 class 模板,如果我们真的仍然需要它的话:

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
};