C++, как объявить самоопределенный массив в универсальном программировании
У меня есть шаблон класса
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, у меня есть определенный тип данных "дробь". Но когда я компилирую программу, возникают ошибки:
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 ответа
Похоже, type
является константой, зависящей от T. В противном случае не имеет смысла указывать точку T* на int, когда T - это число с плавающей точкой. Если это правда, в этом нет необходимости.
Я думаю, что вы ищете специализацию шаблона (непроверенный код):
// 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
};
Почему вы хотите сделать это и сделать вашу жизнь намного сложнее? Это может быть так просто, как это:
#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 или другие проблемы. Другие уже говорили вам, как их избежать.
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>;
};
Как правило, читайте больше о специализации шаблонов, и вы обязательно найдете правильное решение: