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
};
我有一个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
};