我想实现以下内容:
struct MyArray {
void* Elements;
int Capacity;
int ElementsCount;
size_t ElementSize;
//methods
void AddElement(void* item);
//...
};
void* Elements
应该指向任何类型的项目。每个元素都应具有特定大小(ElementSize
变量)和AddElement(void*)
方法应将项添加到现有数组。
问题是我不能用我的Array做指针arythmetic,我知道我每次想要使用它时都需要使用cast,但我完全不知道该怎么做。
而且我知道模板会更好地解决它,但在这种情况下我想用指针练习:))
提前感谢您的帮助。
答案 0 :(得分:2)
要移动指针,您可以执行以下操作:
int* nextInt = reinterpret_cast<int*>(Elements) + 1;
这将指向下一个int
。您可以使用此技术移动其他类型。
请注意,由于元素的大小不同,这会导致各种麻烦。
答案 1 :(得分:0)
是的,你不能对void *进行指针运算,你必须转换为char *才能进行算术运算,例如类似的东西:
void MyArray::AddElement( void * item )
{
// verify that ElementsCount is not already Capacity and if so, reallocate or throw
void * insertionPoint = static_cast<char *>(Elements) + (ElementSize * ElementsCount );
memcpy( insertionPoint, item, ElementSize );
++ElementsCount;
}
请注意,您需要static_cast
从void *转换为char *,并且您不需要明确地将其转换为void *,这就是我可以将其分配给insertionPoint的原因。
答案 2 :(得分:0)
我会这样追求:
迭代器将使您的阵列受益,因为您可能拥有遍历阵列的通用算法,而无需了解所需的存储类型。
答案 3 :(得分:0)
我不知道模板和指针是如何相互排斥的,我认为这是人们首先使用它们的情况。使用模板,您可以为指针指定一个类型,并对问题进行排序。
另一方面,如果完全避免模板,则需要该类型的大小。比如,CashCow如何处理问题:
void MyArray::AddElement( void * item )
{
auto insertionPoint = static_cast<char *>(Elements) + (ElementSize * ElementsCount );
memcpy( insertionPoint, item, ElementSize );
++ElementsCount;
}
然而,你还没有完成它。您需要确保永远不会超过预先分配的缓冲区。我会这样修改它:
void MyArray::AddElement( void * item )
{
if ((Capacity + 1) < ElementSize * ElementsCount)
{
Capacity <<= 1; // Double the size of the buffer.
auto newBlock = new char[Capacity];
memcpy(Elements, newBlock, Capacity >> 1); // Copy the old data
delete Elements;
Elements = static_cast<void*>(newBlock);
}
auto insertionPoint = static_cast<char *>(Elements) + (ElementSize * ElementsCount );
memcpy( insertionPoint, item, Elementize );
++ElementsCount;
}
像这样的东西。当然,这仍然不完整,但也许给你一个线索。
答案 4 :(得分:0)
我不得不质疑你为什么要这样做。除非你是出于学术/实验目的并且计划将其丢弃,否则你正在为自己做工作并且几乎肯定最终会得到比使用语言和STL设施更容易受到问题影响的代码。已经提供。在C中,你可能不得不这样做,但是使用C ++你不需要语言支持。
您正在做的事情有两个方面:拥有任何类型的元素,这些元素可以通过某种定义的方式一般使用,并将这些元素集合在一起。然后,第一个方面可以很容易地通过多元化来实现。创建一个定义公共接口的抽象基类:
struct BaseElement { virtual void doSomething( ); };
然后你可以从中得出结构,它涵盖你的元素在做什么:
struct DerivedElement1 : public BaseElement { void doSomething( ); };
struct DerivedElement2 : public BaseElement { void doSomething( ); };
要一起收集类型,您只需使用STL向量即可。它提供了我所能看到的所有内容。作为一个非常简单的示例,您可以执行以下操作:
// Convenient shorthand.
typedef std::vector< std::shared_ptr<BaseElement> > MyElements;
MyElements m;
// Create two different but commonly derived objects.
std::shared_ptr<DerivedElement1> e1(new DerivedElement1);
std::shared_ptr<DerivedElement2> e2(new DerivedElement2);
// Push them onto the collection.
m.push_back( e1.static_pointer_cast<BaseElement>( e1 ) );
m.push_back( e2.static_pointer_cast<BaseElement>( e2 ) );
此时你已经拥有了所需的一切。 Vector提供标准功能,例如begin()
,end()
和size()
,可帮助您遍历集合并在其上运行STL算法(如果您愿意)。集合是多态的这一事实意味着您可以在每个元素上运行doSomething()
,因为它知道它只会执行为该结构定义的内容。
(我没有理解C ++ 11编译器,所以我相信有人会在这里接受我的东西。但是,使用原始C ++ 11代码即使使用raw也可以很容易地实现同样的功能。指针,如果你小心地正确清理对象。)
我知道这不是你想要的答案,但我只是想强调一下,除非你只是试着用丢失的例子来学习,否则几乎总是更快,更短,更安全,更可靠地使用已有的那里。