在 Stack 实现中使用哪个智能指针?
Which smart pointer to use in Stack implementation?
据我正确理解:
scoped_ptr: 无开销,无法复制或移动。
unique_ptr:无开销,不可复制,可移动。
shared_ptr: 一些开销(引用计数),可以复制。
话虽如此,如果需要多个所有者,则应使用shared_ptr。
现在,在下面的这个程序中,它是一个简单的 C++ 堆栈实现。我不明白应该使用哪种类型的智能指针。
我问这个问题的原因是 unique_ptr 和 shared_ptr 都不能被复制,而这正是我在这个简单堆栈的实现中所做的。我在我使用 C++ 指针的程序中注释掉了 //HERE,如果您正确阅读程序,您将看到数据是如何在几乎所有函数中被复制的。
GameStateStack.h
#ifndef _H_GAMESTATE_
#define _H_GAMESTATE_
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/scoped_ptr.hpp>
#include <memory>
class node
{
public:
std::string gameState;
node * nextGameState; // HERE
};
class GameStateStack
{
private:
node * _topState; // HERE
void Destory();
public:
int gameStatesCount;
void PushGameState(std::string element);
void PopGameState();
std::string CurrentGameState();
GameStateStack();
~GameStateStack();
};
extern GameStateStack state;
#endif
GameStateStack.cpp
#include <iostream>
#include <stdlib.h>
#include <string>
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <memory>
#include "GameStateStack.h"
#include "template.h"
GameStateStack state;
GameStateStack::GameStateStack()
{
_topState = NULL;
gameStatesCount = 0;
}
GameStateStack::~GameStateStack()
{
}
void GameStateStack::PushGameState(std::string gameStateName)
{
node *newTopState = new node; // HERE
if (_topState == NULL)
{
newTopState->gameState = gameStateName;
newTopState->nextGameState = NULL;
_topState = newTopState;
gameStatesCount++;
}
else
{
newTopState->gameState = gameStateName;
newTopState->nextGameState = _topState;
_topState = newTopState;
gameStatesCount++;
}
}
void GameStateStack::PopGameState()
{
if (_topState == NULL)
std::cout << "Error: no gamestates available to pop";
else
{
node * old = _topState; // HERE
_topState = _topState->nextGameState;
delete(old);
gameStatesCount--;
}
}
std::string GameStateStack::CurrentGameState()
{
node *temp; // HERE
temp = _topState;
return temp->gameState;
}
void GameStateStack::Destory()
{
node *abc; // HERE
delete _topState;
delete abc->nextGameState;
}
这里 unique_ptr
非常好,特别是如果您 return 按值 CurrentGameState
时。
你的顶部应该是 unique_ptr<node>&
但由于开头没有状态你可以使用 std::reference_wrapper
来包装它。
当然,如果您打算 return 通过引用或指针访问游戏状态,情况就会发生变化,因为弹出状态会使包含的状态无效(除非它是动态分配然后设置的)。
以下是如何使用 std::unique_ptr
实现您的堆栈。请注意使用 std::move()
重新分配 std::unique_ptr
使原始指向空无一物。
此外,我使用了更惯用的 if(topState)
而不是 if(topState == NULL)
。 A std::unique_ptr
returns 如果指向某处则为真,否则为假。
另外 standard C++
规定我们不应该以 _
.
开头的变量名
#include <string>
#include <memory>
#include <iostream>
struct node
{
std::string gameState;
std::unique_ptr<node> nextGameState;
~node()
{
std::cout << "deleting: " << gameState << '\n';
}
};
class GameStateStack
{
// should not use _ to begin variable names in std C++
std::unique_ptr<node> topState;
int gameStatesCount;
public:
GameStateStack();
void PushGameState(std::string gameStateName);
void PopGameState();
std::string CurrentGameState();
void Destory();
};
GameStateStack::GameStateStack()
: gameStatesCount(0) // initialize here
{
//topState = NULL; // no need to initialize unique_ptr
//gameStatesCount = 0; // not here
}
void GameStateStack::PushGameState(std::string gameStateName)
{
std::unique_ptr<node> newTopState(new node);
newTopState->gameState = gameStateName;
newTopState->nextGameState = std::move(topState);
topState = std::move(newTopState);
gameStatesCount++;
}
void GameStateStack::PopGameState()
{
if(!topState)
std::cout << "Error: no gamestates available to pop";
else
{
topState = std::move(topState->nextGameState);
gameStatesCount--;
}
}
std::string GameStateStack::CurrentGameState()
{
if(topState)
return topState->gameState;
return "error: nothing on stack"; // error
}
void GameStateStack::Destory()
{
// deleting topState will first destroy the pointed to
// node's own unique_ptr<node> nextGameState
// which in turn will first delete its own nextGameState etc...
topState.reset();
}
int main()
{
GameStateStack stack;
std::cout << "\ndestroy test" << '\n';
stack.PushGameState("a");
stack.PushGameState("b");
stack.PushGameState("c");
stack.PushGameState("d");
stack.PushGameState("e");
stack.PushGameState("f");
stack.Destory();
std::cout << "\npush-pop test" << '\n';
stack.PushGameState("a");
stack.PushGameState("b");
stack.PushGameState("c");
std::cout << stack.CurrentGameState() << '\n';
stack.PopGameState();
stack.PushGameState("d");
stack.PushGameState("e");
std::cout << stack.CurrentGameState() << '\n';
stack.PopGameState();
std::cout << stack.CurrentGameState() << '\n';
stack.PopGameState();
std::cout << stack.CurrentGameState() << '\n';
stack.PopGameState();
std::cout << stack.CurrentGameState() << '\n';
stack.PopGameState();
std::cout << stack.CurrentGameState() << '\n';
stack.PopGameState();
}
据我正确理解:
scoped_ptr: 无开销,无法复制或移动。
unique_ptr:无开销,不可复制,可移动。
shared_ptr: 一些开销(引用计数),可以复制。
话虽如此,如果需要多个所有者,则应使用shared_ptr。
现在,在下面的这个程序中,它是一个简单的 C++ 堆栈实现。我不明白应该使用哪种类型的智能指针。
我问这个问题的原因是 unique_ptr 和 shared_ptr 都不能被复制,而这正是我在这个简单堆栈的实现中所做的。我在我使用 C++ 指针的程序中注释掉了 //HERE,如果您正确阅读程序,您将看到数据是如何在几乎所有函数中被复制的。
GameStateStack.h
#ifndef _H_GAMESTATE_
#define _H_GAMESTATE_
#include <iostream>
#include <boost/shared_ptr.hpp>
#include <boost/scoped_ptr.hpp>
#include <memory>
class node
{
public:
std::string gameState;
node * nextGameState; // HERE
};
class GameStateStack
{
private:
node * _topState; // HERE
void Destory();
public:
int gameStatesCount;
void PushGameState(std::string element);
void PopGameState();
std::string CurrentGameState();
GameStateStack();
~GameStateStack();
};
extern GameStateStack state;
#endif
GameStateStack.cpp
#include <iostream>
#include <stdlib.h>
#include <string>
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <memory>
#include "GameStateStack.h"
#include "template.h"
GameStateStack state;
GameStateStack::GameStateStack()
{
_topState = NULL;
gameStatesCount = 0;
}
GameStateStack::~GameStateStack()
{
}
void GameStateStack::PushGameState(std::string gameStateName)
{
node *newTopState = new node; // HERE
if (_topState == NULL)
{
newTopState->gameState = gameStateName;
newTopState->nextGameState = NULL;
_topState = newTopState;
gameStatesCount++;
}
else
{
newTopState->gameState = gameStateName;
newTopState->nextGameState = _topState;
_topState = newTopState;
gameStatesCount++;
}
}
void GameStateStack::PopGameState()
{
if (_topState == NULL)
std::cout << "Error: no gamestates available to pop";
else
{
node * old = _topState; // HERE
_topState = _topState->nextGameState;
delete(old);
gameStatesCount--;
}
}
std::string GameStateStack::CurrentGameState()
{
node *temp; // HERE
temp = _topState;
return temp->gameState;
}
void GameStateStack::Destory()
{
node *abc; // HERE
delete _topState;
delete abc->nextGameState;
}
这里 unique_ptr
非常好,特别是如果您 return 按值 CurrentGameState
时。
你的顶部应该是 unique_ptr<node>&
但由于开头没有状态你可以使用 std::reference_wrapper
来包装它。
当然,如果您打算 return 通过引用或指针访问游戏状态,情况就会发生变化,因为弹出状态会使包含的状态无效(除非它是动态分配然后设置的)。
以下是如何使用 std::unique_ptr
实现您的堆栈。请注意使用 std::move()
重新分配 std::unique_ptr
使原始指向空无一物。
此外,我使用了更惯用的 if(topState)
而不是 if(topState == NULL)
。 A std::unique_ptr
returns 如果指向某处则为真,否则为假。
另外 standard C++
规定我们不应该以 _
.
#include <string>
#include <memory>
#include <iostream>
struct node
{
std::string gameState;
std::unique_ptr<node> nextGameState;
~node()
{
std::cout << "deleting: " << gameState << '\n';
}
};
class GameStateStack
{
// should not use _ to begin variable names in std C++
std::unique_ptr<node> topState;
int gameStatesCount;
public:
GameStateStack();
void PushGameState(std::string gameStateName);
void PopGameState();
std::string CurrentGameState();
void Destory();
};
GameStateStack::GameStateStack()
: gameStatesCount(0) // initialize here
{
//topState = NULL; // no need to initialize unique_ptr
//gameStatesCount = 0; // not here
}
void GameStateStack::PushGameState(std::string gameStateName)
{
std::unique_ptr<node> newTopState(new node);
newTopState->gameState = gameStateName;
newTopState->nextGameState = std::move(topState);
topState = std::move(newTopState);
gameStatesCount++;
}
void GameStateStack::PopGameState()
{
if(!topState)
std::cout << "Error: no gamestates available to pop";
else
{
topState = std::move(topState->nextGameState);
gameStatesCount--;
}
}
std::string GameStateStack::CurrentGameState()
{
if(topState)
return topState->gameState;
return "error: nothing on stack"; // error
}
void GameStateStack::Destory()
{
// deleting topState will first destroy the pointed to
// node's own unique_ptr<node> nextGameState
// which in turn will first delete its own nextGameState etc...
topState.reset();
}
int main()
{
GameStateStack stack;
std::cout << "\ndestroy test" << '\n';
stack.PushGameState("a");
stack.PushGameState("b");
stack.PushGameState("c");
stack.PushGameState("d");
stack.PushGameState("e");
stack.PushGameState("f");
stack.Destory();
std::cout << "\npush-pop test" << '\n';
stack.PushGameState("a");
stack.PushGameState("b");
stack.PushGameState("c");
std::cout << stack.CurrentGameState() << '\n';
stack.PopGameState();
stack.PushGameState("d");
stack.PushGameState("e");
std::cout << stack.CurrentGameState() << '\n';
stack.PopGameState();
std::cout << stack.CurrentGameState() << '\n';
stack.PopGameState();
std::cout << stack.CurrentGameState() << '\n';
stack.PopGameState();
std::cout << stack.CurrentGameState() << '\n';
stack.PopGameState();
std::cout << stack.CurrentGameState() << '\n';
stack.PopGameState();
}