C++ 什么';只有在第一次使用变量时才计算它是一个好模式吗?
这不是一个实际问题,但我正在寻找一种模式来改进以下逻辑:C++ 什么';只有在第一次使用变量时才计算它是一个好模式吗?,c++,C++,这不是一个实际问题,但我正在寻找一种模式来改进以下逻辑: void PrintToGameMasters() { std::string message = GetComplicatedDebugMessage(); // This will create a big string with various info for (Player* player : GetAllPlayers()) if (player->IsGameMaster())
void PrintToGameMasters()
{
std::string message = GetComplicatedDebugMessage(); // This will create a big string with various info
for (Player* player : GetAllPlayers())
if (player->IsGameMaster())
player->SendMessage(message);
}
这段代码是有效的,但我遇到的问题是,在大多数情况下,没有任何gamemasters
玩家,因此消息合成将是免费的
我想写一些只会在第一次使用该变量时创建消息的东西,但是我不能在这里想出一个好的解决方案
编辑:
为了使这个问题更精确,我正在寻找一个解决方案,它不是特定于字符串的,它可以是一个没有函数来测试它是否初始化的类型。
另外,如果我们能够将对
getComplementedBugMessage
的调用保持在循环的顶部,我认为一个包含包装器的解决方案可以解决这一问题。不确定这是否是最佳模式,但可以使用lambda延迟计算:
void PrintToGameMasters()
{
std::string message = "";
auto getDebugMessage = [&message]() -> const std::string& {
if (message.empty()) {
message = GetComplicatedDebugMessage();
}
return message;
};
for (Player* player : GetAllPlayers())
if (player->IsGameMaster())
player->SendMessage(getDebugMessage());
}
您可以使用lambda在第一次找到像这样的游戏大师时调用该函数
void PrintToGameMasters()
{
std::once_flag of;
std::string message;
for (Player* player : GetAllPlayers())
if (player->IsGameMaster())
{
std::call_once(of, [&](){ message = GetComplicatedDebugMessage(); });
player->SendMessage(message);
}
}
这里有一些好主意,但我想让它更简单一点:
void PrintToGameMasters()
{
std::string message;
for (Player* player : GetAllPlayers())
{
if (player->IsGameMaster())
{
if (message.empty())
message = GetComplicatedDebugMessage();
player->SendMessage(message);
}
}
}
每个人都可以遵循这一点,而且它像芯片一样便宜……而且调试起来也像馅饼一样简单。将消息包装在可变的lambda中:
auto makeMessage = [message = std::string()]() mutable -> std::string&
{
if (message.empty()) {
message = GetComplicatedDebugMessage();
}
return message;
};
for (Player* player : GetAllPlayers())
if (player->IsGameMaster())
player->SendMessage(makeMessage());
真希望这有帮助
尝试实现以下逻辑:
#include <iostream>
using namespace std;
int main()
{
bool GameMaster,first_time,used_before;
GameMaster = true;
first_time = false;
used_before = false;
GameMaster = first_time;
first_time = used_before;
for( int i = 0; i < 5; i++ ) {
if(GameMaster==used_before) {
cout<<" First Time";
GameMaster = true;
}
if(GameMaster!=used_before) {
cout<<" Old News";
}
}
return 0;
}
使用;保留两个玩家列表:游戏大师和非游戏大师(或者像你现在这样的所有玩家+一个单独的指向游戏大师的指针向量)
目标是最小化循环中的if
-语句
针对最常见的情况而不是最一般的情况进行优化;最常见的情况是,玩家不是游戏大师;因此,避免在它们上面循环
另外,由于您正在开发一个游戏,我想将此链接添加到Mike Acton的链接,您可能会对此感兴趣。而
std::string
有空值,可能意味着“未计算”,您可以更普遍地使用std::optional
来处理空字符串和非默认可构造类型:
void PrintToGameMasters()
{
std::optional<std::string> message;
for (Player* player : GetAllPlayers()) {
if (player->IsGameMaster()) {
if (!message) {
message = GetComplicatedDebugMessage();
}
player->SendMessage(*message);
}
}
}
void PrintToGameMasters()
{
std::可选消息;
对于(Player*Player:GetAllPlayers()){
如果(player->IsGameMaster()){
如果(!消息){
message=getComplementedBugMessage();
}
播放器->发送消息(*消息);
}
}
}
您可以扩展使用std::optional
(如Jarod41的答案所示)的方法,并将惰性评估放在顶部。这也满足了“将对getComplementedBugMessage
的调用保持在循环顶部”的要求
模板
类:public std::optional{
公众:
惰性(std::函数f):fn(f){}
T算子*(){
如果(!*这个)
std::optional::operator=(fn());
返回此->值();
}
私人:
std::功能fn;
};
作废PrintToGameMasters()
{
惰性消息(GetComplementedBugMessage);
对于(Player*Player:GetAllPlayers())
如果(player->IsGameMaster())
播放器->发送消息(*消息);
}
我不知道为什么要将消息的定义保持在循环上方。如果有人在阅读代码以分析在std::end(GetAllPlayers())==std::begin(GetAllPlayers)(
)的情况下会发生什么,那么你不希望他们的心理工作区被不相关的变量弄得乱七八糟
如果你愿意放弃,那么static
就是你的朋友:
void PrintToGameMasters()
{
for (auto const &player : GetAllPlayers())
if (player->IsGameMaster())
{
//Initialization of a static variable occurs exactly once, even when multithreaded,
//precisely when the defining line is hit for the first time
static auto const &message{GetComplicatedDebugMessage()};
player->SendMessage(message);
}
}
这很有效。正如麻省理工学院许可证所说: 软件按“原样”提供,无任何形式的担保, 明示或暗示
#包括
#包括
#包括
#包括
结构播放器{
bool-isGameMaster;
int-id;
};
int uu stdcall IsGameMaster(玩家*自身){
返回self->isGameMaster?1:0;
}
//可能是“发送消息”。。。但是Windows
void uu stdcall SendMessageToPlayer(Player*self,std::string*msg){
printf(“玩家%d说:%s\n”,self->id-1000+1,msg->c_str());
}
玩家g_玩家[18];
玩家*\uu stdcall GetAllPlayers(无效){
return&g_玩家[0];
}
std::string le_message=“嗨,我是游戏大师”;
std::string*\uu stdcall getcomplementmessage(void){
puts(“生成复杂消息。占用CPU 3天!”);
return&le_message;//使我的程序集更轻松
}
__declspec(裸版)作废打印相机母版(作废版){
__asm{
推动ebp;
mov-ebp,esp;
亚esp,8;
调用GetAllPlayer;
mov[ebp-4],eax;
//这是“i”,循环迭代计数器
//我选择esi是因为它由stdcall保存
xor esi,esi;
do_循环:
//Player*Player=&g_players[i];
mov-ebx,esi;
imul ebx,尺寸播放器;
添加ebx,[ebp-4];//ebx=g_玩家+sizeof(玩家)*i,或&g_玩家[i]
//如果(player->IsGameMaster()){
推ebx;
呼叫IsGameMaster;
测试eax,eax;
jz无印;
//msg=getcomplementmessage();
获取\u消息\u开始:
调用getcomplementmessage;
mov[ebp-8],eax;
jmp-short-delete_-self;
获取\u消息\u结束:
//播放器->发送消息(msg);
推压[ebp-8];
推ebx;
调用SendMessageToPlayer;
// }
无打印:
公司esi;
cmp-esi,18;
jb-do_环;
mov-esp,ebp;
pop-ebp;
ret;
删除你自己:
mov ecx,获取消息启动;
mov eax,get_msg_end;
子eax、ecx;
mov字节ptr[ecx],0xEB;//jmp短
mov字节ptr[ecx+1],al;//相对偏移量
jmp get_msg_end;
}
}
int main(){
对于(int i=0;i<18;i++){
g|U玩家[i].isGameMaster=(i==12 | | i==15);//玩家13和16
g_玩家[i].id=i+1000;
}
德沃德·奥尔德普雷特;
VirtualProtect(&PrintToGameMasters,0x1000,第页\u执行\u读写,&oldProtect);
PrintToGameMasters();
返回0;
}
它比if(!message)消息快得多
void PrintToGameMasters()
{
std::optional<std::string> message;
for (Player* player : GetAllPlayers()) {
if (player->IsGameMaster()) {
if (!message) {
message = GetComplicatedDebugMessage();
}
player->SendMessage(*message);
}
}
}
void PrintToGameMasters()
{
for (auto const &player : GetAllPlayers())
if (player->IsGameMaster())
{
//Initialization of a static variable occurs exactly once, even when multithreaded,
//precisely when the defining line is hit for the first time
static auto const &message{GetComplicatedDebugMessage()};
player->SendMessage(message);
}
}
#include <Windows.h>
#include <cstdlib>
#include <cstdio>
#include <string>
struct Player {
bool isGameMaster;
int id;
};
int __stdcall IsGameMaster(Player* self) {
return self->isGameMaster ? 1 : 0;
}
// Could've been "SendMessage"... but Windows.h
void __stdcall SendMessageToPlayer(Player* self, std::string* msg) {
printf("Player %d says: %s\n", self->id - 1000 + 1, msg->c_str());
}
Player g_players[18];
Player* __stdcall GetAllPlayers(void){
return &g_players[0];
}
std::string le_message = "hi, I'm a game master";
std::string* __stdcall GetComplicatedMessage(void) {
puts("GENERATING COMPLICATED MESSAGE. HOGGING CPU FOR 3 DAYS!");
return &le_message; // to make my assembly life easier
}
__declspec(naked) void PrintToGameMasters(void){
__asm {
push ebp;
mov ebp, esp;
sub esp, 8;
call GetAllPlayers;
mov [ebp-4], eax;
// this is 'i', the loop iteration counter
// I chose esi because it is preserved by stdcalls
xor esi, esi;
do_loop:
// Player* player = &g_players[i];
mov ebx, esi;
imul ebx, SIZE Player;
add ebx, [ebp-4]; // ebx = g_players + sizeof(Player) * i, or &g_players[i]
// if (player->IsGameMaster()) {
push ebx;
call IsGameMaster;
test eax, eax;
jz no_print;
// msg = GetComplicatedMessage();
get_msg_start:
call GetComplicatedMessage;
mov [ebp-8], eax;
jmp short delete_self;
get_msg_end:
// player->SendMessage(msg);
push [ebp-8];
push ebx;
call SendMessageToPlayer;
// }
no_print:
inc esi;
cmp esi, 18;
jb do_loop;
mov esp, ebp;
pop ebp;
ret;
delete_self:
mov ecx, get_msg_start;
mov eax, get_msg_end;
sub eax, ecx;
mov byte ptr [ecx], 0xEB; // jmp short
mov byte ptr [ecx+1], al; // relative offset
jmp get_msg_end;
}
}
int main(){
for (int i = 0; i < 18; i++) {
g_players[i].isGameMaster = (i == 12 || i == 15); // players 13 and 16
g_players[i].id = i + 1000;
}
DWORD oldProtect;
VirtualProtect(&PrintToGameMasters, 0x1000, PAGE_EXECUTE_READWRITE, &oldProtect);
PrintToGameMasters();
return 0;
}
void PrintToGameMasters()
{
struct {
operator std::string const &(void)
{
static auto const real_value{GetComplicatedDebugMessage()};
return real_value;
}
} message;
for (auto const &player : GetAllPlayers())
if (player->IsGameMaster())
player->SendMessage(message);
}
void PrintToGameMasters()
{
for (Player* player : GetAllPlayers())
if (player->IsGameMaster()) {
static std::string message = GetComplicatedDebugMessage(); // This will create a big string with various info
player->SendMessage(message);
}
}
void PrintToGameMasters()
{
auto message = std::async(
std::launch::deferred,
[]{return GetComplicatedDebugMessage();}
);
for (Player* player : GetAllPlayers())
if (player->IsGameMaster())
player->SendMessage(message.get());
}