在C++中重复使用向量

14

我有一个声明为全局变量的向量,我需要能够重复使用它。例如,我正在读取多个数据文件,解析数据以创建对象,然后将这些对象存储在向量中。

vector<Object> objVector(100);

void main()
{
    while(THERE_ARE_MORE_FILES_TO_READ)
    {
        // Pseudocode
        ReadFile();
        ParseFileIntoVector();
        ProcessObjectsInVector();
        /* Here I want to 'reset' the vector to 100 empty objects again */

    }
}

如果初始时分配的vector在堆栈上,我是否可以将其重置为vector<Object> objVector(100)? 如果我执行objVector.clear(),它会删除所有100个对象,并且我将得到一个大小为0的向量。 我需要在每次循环开始时将其大小设置为100。


你需要保留这些对象,还是想要将它们重置为默认构造状态? - Macke
1
在你的例子中,objVector是静态分配的。它没有在堆栈上分配。 - Alan
3
你的主函数返回类型不正确。在 C 和 C++ 中,main 函数必须始终返回 int 类型(但如果你实际上没有返回任何东西,C++ 和 C99 会自动帮你返回 0)。 - Tronic
4
为什么需要重复使用它?如果您担心性能,这几乎肯定是不相关的。为什么是100?这似乎是关于做一些可能不应该做的事情的问题,如果您能告诉我们您尝试实现的目标,那会有所帮助。 - David Thornley
@Tronic:实际上,如果我记得正确的话,实现必须保证允许将int作为返回类型。但是特定于平台的返回类型也是允许的。 - Maciej Piechotka
@Maciej:不,返回值必须是int类型,但它的其他方面可能是实现定义的。 - GManNickG
10个回答

11

我有一个向量声明为全局变量,我需要能够重复使用它。

为什么呢?从您的代码中不清楚为什么这个变量必须是全局的。为什么不能在循环内部声明它呢?然后你就不需要重置它,因为每次循环都会自动进行。

为了从其他方法中访问该变量,请将其作为参数传递(按引用传递,以便您可以修改它)。拥有全局变量很少是一个好的解决方案。

另外一件事情:main函数绝不能有返回类型void,这是无效的C++代码,许多编译器会拒绝它。


6
objVector.clear();
objVector.resize(100);

然而,这可能不是vector的推荐用法。您确定不应该使用push_back来初始化一个空的vector吗?从您的问题中看,您如何确保每个文件都恰好包含100个对象,没有多也没有少?
此外,vector可能不需要是全局的。最好传递参数。当您看到一堆没有参数的函数被调用时,很难甚至不可能跟踪发生了什么(因为除了您之外的所有其他人 - 包括您在几个月后回到这段代码时 - 都不知道这些函数用于输入和输出)。

4
vector<Object> objVector(100); 

int main() 
{ 
 while(THERE_ARE_MORE_FILES_TO_READ) 
 { 
    // Pseudocode 
     ReadFile(); 
     ParseFileIntoVector(); 
     ProcessObjectsInVector(); 
     /* Here I want to 'reset' the vector to 100 empty objects again */ 
     objVector.clear();
     objVector.resize(100);

 } 
}

2
我仍然认为swap()是这种工作的推荐方法。请参阅Effective STL的第17项:“使用“交换技巧”来修剪过量容量”。执行clear,然后resize实际上比使用swap()慢 - 它具有比swap()更多的内存分配操作。 - Jay Zhu
2
减少过剩容量通常是不需要的(这也是标准库没有这样做的原因)。频繁分配内存会导致性能下降。重用向量所寻求的性能优势也将完全丧失。 - Tronic
就像你所说的,交换技巧可以减少多余的容量 - 但在这种情况下似乎并没有。 - JoeG

2

为什么要重置全局变量?只需在每次循环中分配一个新的向量,并通过引用将向量传递到函数中。

void ParseFileIntoVector(vector<Object> &vector);
void ProcessObjectsInVector(const vector<Object> &vector);

int main() 
{ 
 while(THERE_ARE_MORE_FILES_TO_READ) 
 { 
     // Pseudocode 
     vector<Object> objVector(100); 
     ReadFile(); 
     ParseFileIntoVector(objVector); 
     ProcessObjectsInVector(objVector); 
 } 
}

2
在循环开始或结束时调用resize函数:http://www.cplusplus.com/reference/stl/vector/resize/ 这样应该可以达到你想要的效果。然而,我建议使用push和pop函数。它更加节省空间,也是Vector的设计初衷。当你向Vector中添加(push)或移除(pop)元素时,它会根据需要自动扩展和缩小。这样你就不必担心Vector的大小或内容了。它只是一个处理队列。

1
以下代码应该可以解决问题。
vector<Object> temp(100);
objVector.swap(temp);

那并没有太大帮助,因为你只是丢弃了一个不同的向量,但你仍然把它丢弃了。 - Šimon Tóth
@Let_Me_Be:这会丢弃你已经拥有的内容,并用100个默认构造的对象替换它——这是OP所要求的。 - UncleBens
@Let_Me_Be:我其实不太理解你的评论。能否请您再解释一下? - Jay Zhu
我想我误解了。我以为他想要速度优化(没有内存分配/释放),但情况似乎并非如此。 - Šimon Tóth
是的,这可能不是最快的方法。使用临时变量进行交换通常用于实现“缩小(容量)以适应”。 - UncleBens

1

与其他帖子相反,最有效的方法可能是这样做:

objVector.resize(0);
objVector.resize(100);

clear()函数在某些实现中会释放向量的内存(其唯一的后置条件是size()=0)。resize(0)则保留容量。

交换技巧也会引发不必要的内存分配。你交换的临时向量将分配一个新的内存块,交换后旧的内存块也会被释放。没有内存分配的性能应该更好。


实际上,我查了一下。在C++03中,.clear().resize(0)都不能引起重新分配(因为这将使.begin()无效,而C++03不允许这样做),但是C++0x似乎允许.clear().resize(0)都重新分配(因为它们现在可能会使.begin()无效)。当然,重新分配并不意味着容量的改变,因此不能像“由于容量不会改变,所以不会发生重新分配”这样争论,我认为。 - Johannes Schaub - litb
导致.resize(0).clear()可能重新分配的静默更改是:http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#414(请注意,两者仍必须重新分配到与容量存储的相同或更大的内存量。因此,它们重新分配将是毫无意义的。但从理论上讲,如果我读得正确,它们可能会这样做)。 - Johannes Schaub - litb
嘿,这比我想象的要复杂得多。感谢您的评论。 - AshleysBrain

0

调用objVector.clear()以删除先前的数据点,然后使用objVector.resize(100)将其调整为适当的大小。

请注意,这将使用默认构造函数分配1个Object实例,然后使用复制构造函数复制100个Object实例,这可能是您真正想要的,也可能不是。如果Object是指针类型,则可以选择使用objVector.resize(100, NULL)来避免可能不需要的分配。


如果Object是指针类型,则不会发生任何不必要的分配,将NULL传递给resize是完全多余的,因为即使没有它,也会发生相同的情况。 - Konrad Rudolph
此外,你对于复制构造函数的断言是错误的。默认构造函数会被调用100次,即每次都会调用它。 - Konrad Rudolph
@Konrad,你在第二点上弄错了。如果类只有隐式构造函数,则默认构造函数可能会被调用100次,但是快速测试应用程序将显示如果实现了复制构造函数,则确实会调用复制构造函数。然而,我错了:默认构造函数被调用一次,复制构造函数被调用100次,而不是99次。 - Dathan
你当然是对的,这是个愚蠢的错误。正如我自己所说的,调用 resize(n) 就相当于调用 resize(n, T()),所以当然会调用复制构造函数,而不是构造函数本身。呃。 - Konrad Rudolph

0

由于您正在使用“Object”的默认构造函数,我认为您应该能够依赖于向量的容量而不是其大小。因此,当您调用clear()时,很有可能您并没有改变由您的向量构造函数设置的容量(您可以至少保存100个元素,它们已经被分配)。请阅读关于容量、保留和大小以及它们之间的区别(保留是您要请求更改容量的调用,我只是在提醒您,以防您需要它)。

无论如何,如果您需要将对象重置为默认状态,而不是依赖于对整个向量进行重置对象的能力,您还可以调用名为“reset”的对象来将它们设置为该状态,并在重新使用这些相同对象之前调用它。从性能上讲,我认为这没有任何区别,从代码上看,它似乎是一个干净的解决方案。


0
我能否将向量重置为“vector objVector(100)”,因为它最初是在堆栈上分配的?
C++向量不是直接在堆栈上分配的。vector(和其他STL模板)的参数之一是allocator。您可以使用池分配器、堆分配器等。
实际上,std::vector实际上是带有附加信息(例如大小和容量)的数组指针。否则,向量的大小必须事先知道(这是不必要的)。
实际上,在上面的示例中,std::vector不在堆栈上,而是在程序的数据部分(或在非ELF平台上称为什么)。
因此问题是您是否需要:
1. 大小为100的向量。在这种情况下,正如已经指出的那样,您可能正在做一些奇怪的事情。但是仍然可以通过resize方法以不错的方式实现。
2. 容量为100的向量。在这种情况下,您可能不应该做任何事情,因为向量不会收缩其容量。并且肯定不需要,因为向量会动态更改其容量。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接