使用void指针的动态数组实现

时间:2012-10-22 10:51:07

标签: c++ arrays dynamic void-pointers

我想实现以下内容:

struct MyArray {
    void* Elements;
    int Capacity;
    int ElementsCount;
    size_t ElementSize;

    //methods
    void AddElement(void* item);
    //...
};

void* Elements应该指向任何类型的项目。每个元素都应具有特定大小(ElementSize变量)和AddElement(void*)方法应将项添加到现有数组。 问题是我不能用我的Array做指针arythmetic,我知道我每次想要使用它时都需要使用cast,但我完全不知道该怎么做。 而且我知道模板会更好地解决它,但在这种情况下我想用指针练习:))

提前感谢您的帮助。

5 个答案:

答案 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)

我会这样追求:

  1. 创建特定于数组的迭代器,就像STL一样。
  2. 迭代器的重载操作符
  3. 迭代器将使您的阵列受益,因为您可能拥有遍历阵列的通用算法,而无需了解所需的存储类型。

答案 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也可以很容易地实现同样的功能。指针,如果你小心地正确清理对象。)

我知道这不是你想要的答案,但我只是想强调一下,除非你只是试着用丢失的例子来学习,否则几乎总是更快,更短,更安全,更可靠地使用已有的那里。