1

我想实现如下所示:

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

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

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

提前感谢您的帮助。

4

5 回答 5

2

要移动指针,您可以执行以下操作:

int* nextInt = reinterpret_cast<int*>(Elements) + 1;

这将指向下一个int。您可以使用此技术来移动其他类型。

请注意,由于元素的大小不同,这可能会导致各种麻烦。

于 2012-10-22T10:54:00.783 回答
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*,这就是我可以将其分配给插入点的原因。

于 2012-10-22T10:56:28.240 回答
0

我会这样追求:

  1. 创建特定于数组的迭代器,例如 STL 迭代器。
  2. 迭代器的重载运算符

迭代器将使您的数组受益,因为您可能拥有遍历数组的通用算法,而无需了解所需的存储类型。

于 2012-10-22T11:00:00.023 回答
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;
}

像这样的东西。当然,这仍然不完整,但也许会给你一个线索。

于 2012-10-22T11:17:54.960 回答
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(),如果您愿意,它可以帮助您遍历集合并在其上运行 STL 算法end()size()集合是多态的这一事实意味着您可以doSomething()在每个元素上运行,知道它只会执行为该结构定义的内容。

(我没有 C++11 编译器的访问权限,所以我相信有人会在这里接我。但是,如果你使用原始指针,即使使用原始指针也可以使用 C++11 之前的代码轻松实现相同的目标'小心正确地清理你的物品。)

我知道这不是你直接想要的答案,但我只是想强调,除非你只是想通过丢弃的例子来学习,否则使用已有的东西几乎总是更快、更短、更安全、更可靠。

于 2012-10-22T11:53:30.083 回答