我对c ++很新,并开始编写一个小型扑克游戏,以帮助我获得更多的矢量体验。我在第二个函数中遇到运行时错误,在阅读下面的帖子并发现我误解了reserve()类成员的行为后,这并不奇怪。我相信我可以通过resize()来解决这个问题,但是我希望能够以一种方式重新构建这个函数来动态分配。我在动态分配方面遇到的困难在于,我正在努力使交易功能将牌分发给每个玩家,就像真正的纸牌游戏一样(是的,我意识到这在某种程度上是统计上多余的)。我可以很容易地用c中的指针数学做这个,我想我可以通过静态分配这些向量中的一些来解决这个问题,但我更愿意尽可能优雅高效地使用c ++。关于动态执行此操作的方法的任何建议?也就是说,如何完全避免为deal()
函数中使用的容器分配或保留空间?
Choice between vector::resize() and vector::reserve()
#include <cassert>
#include <algorithm>
#include <iostream>
#include <ctime>
#include <cstdlib>
#include <vector>
const int __handSize__ = 5;
class Hand {
public:
std::vector<int> held;
std::vector<int> played;
};
std::vector<int> shuffle() {
std::vector<int> deck;
for( int i = 0; i < 52; i++ ){
deck.push_back( i );
}
std::random_shuffle(deck.begin(), deck.end());
assert( 52 == deck.size() );
return deck;
}
std::vector<Hand> deal( int nPlayers, std::vector<int> &deck ) {
std::vector<Hand> playerHands;
playerHands.reserve( nPlayers );
for( int i = 0; i > __handSize__; i++ ) {
for( int j = 0; j < nPlayers; j++ ) {
playerHands.at(j).held.push_back( deck.back() );
}
}
return playerHands;
}
int main() {
srand(time( NULL ));
std::vector<int> deck = shuffle();
std::vector<Hand> hand = deal( 3, deck );
std::cout << hand.at(1).held.at(1) <<std::endl;
}
输出:
dmf86@tux3:~/src/poker$ ./poker
terminate called after throwing an instance of 'std::out_of_range'
what(): vector::_M_range_check: __n (which is 1) >= this->size() (which is 0)
Aborted
回溯:
#0 0x00007ffff74aa428 in __GI_raise (sig=sig@entry=6)
at ../sysdeps/unix/sysv/linux/raise.c:54
#1 0x00007ffff74ac02a in __GI_abort () at abort.c:89
#2 0x00007ffff7ae484d in __gnu_cxx::__verbose_terminate_handler() ()
from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3 0x00007ffff7ae26b6 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4 0x00007ffff7ae2701 in std::terminate() ()
from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#5 0x00007ffff7ae2919 in __cxa_throw ()
from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#6 0x00007ffff7b0b3f7 in std::__throw_out_of_range_fmt(char const*, ...) ()
from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#7 0x000000000040218c in std::vector<int, std::allocator<int> >::_M_range_check(unsigned long) const ()
#8 0x0000000000401785 in std::vector<int, std::allocator<int> >::at(unsigned long) ()
#9 0x0000000000401030 in main ()
答案 0 :(得分:3)
保留或调整向量的厌恶是什么?当我知道向量需要的大小时,我总是保留或调整大小。这个选择取决于我打算如何填补它。
特别是对于大型向量,将大小保留为适当的值是一种更好的做法,而不是允许它随着它的增长而继续调整大小(它必须这样做)。
我认为没有理由不将牌组保留或调整到52,将牌子调到5,因为这是他们在使用时必须增长的。
如果您打算使用push_back来填充向量,请保留它们。如果您打算使用作业,请将其调整大小。
e.g。
vector<int> deck; // create empty vector
deck.resize(52); // resize to 52 elements
// now size = 52, and capacity = 52
for(int i = 0; i < 52; i++)
deck[i] = i; // assign the elements
或:
vector<int> deck(52); // create vector with 52 elements
// size = 52 and capacity = 52
for(int i = 0; i < 52; i++)
deck[i] = i; // assign to the elements
或:
vector<int> deck;
deck.reserve(52); // reserve space for 52 elements
// size = 0, capacity = 52
for(int i = 0; i < 52; i++)
deck.push_back(i); // push to create the elements
// now size = 52, capacity = 52
答案 1 :(得分:1)
无论何时添加或删除元素,向量都不会重新分配。分配的大小可能与使用的大小不同 - 正如您已经注意到reserve()
和resize()
之间的差异。随着向量大小的增加,分配变得更大,因此分配的数量会减少&#34;松弛&#34;是一个常数因子(IIANM)乘以向量的长度。因此,对于n次插入,您只进行O(log(n))重新分配调用。类似地,当您删除元素时,仅当大小与保留大小的比率低于某个点时才会取消分配空间 - 如果有的话。
修改:在澄清之后 - 关于不希望任何调整大小或保留 - 可能的选项可能是使用地图而不是矢量。 std::map
和std::unordered_map
有operator[]
,如果缺少&#34;插入,否则更新&#34;语义。事实上,我还会添加一些更改,并提出一个&#34;交易&#34;功能:
auto deal(const SomeKindOfContainer& players, std::vector<Card>& deck) {
std::unordered_map<Player, Hand> playerHands;
auto handSize = deck.size() / players.size();
for(int i = 0; i < hand_size; i++) {
for(const auto& player : Players) {
playerHands[player].held.push_back( deck.pop_back() );
}
}
return playerHands;
}
这里的好处是它并不假设玩家是整数;而且,事实上,也许你会改用字符串。玩家可以是std::vector
或std::unordered_set
。
此外,使用地图或集合会浪费&#34;,因为它们使用指针并执行许多分配;所以,如果你正在研究数以百万计的玩家和卡片,我会提出别的建议。但在你的例子中,效率并不是真正的问题。
PS - 正如@PeterBecker所提到的,C ++标准只需要分摊的常量时间插入,而不是我所描述的所有内容 - 这是对库实现通常具有的描述。 < / p>
答案 2 :(得分:0)
我设法找到了我自己的问题的解决方案,并认为我应该分享。感谢swift Aziuth的投入。它远未完成,甚至功能,但这将很好。我决定跟踪一个玩家是否折叠并因此不再活动是有用的,所以我添加了一个布尔成员&#34; active&#34;到手班。这有一个额外的好处,允许我强制Deck :: deck向量初始化空结构而不必手动调整它们。
我还把它分成了三个类,以保持每一个小而简单。
class Hand {
public:
std::vector<int> held;
std::vector<int> played;
bool active;
};
class Deck {
const int _handSize_ = 5;
std::vector<int> deck;
public:
void shuffle();
int getCard();
};
class Players {
std::vector<Hand> playerNo;
int activePlayers;
public:
Players( int n );
void addCard( int player, Deck &deck );
bool isActive( int n );
};