C++:如何在不使用全局变量的情况下通过系统传递用户输入?
C++: How to pass user input through the system without using global variables?
我遇到了问题,我的应用程序可能有很多用户输入,这决定了应用程序的运行方式 运行。该应用程序是一个内存数据库系统,用户可以使用诸如“--pagesize 16384”(设置要使用的内存页面大小)、“--alignment 4096”(设置要使用的内存对齐)等命令调用程序或“--measure”(设置标志以测量某些例程)。
目前我将所有用户输入保存在头文件中定义为 extern 的全局变量中:
//@file common.hh
extern size_t PAGE_SIZE_GLOBAL;
extern size_t ALIGNMENT_GLOBAL;
extern size_t MEMCHUNK_SIZE_GLOBAL;
extern size_t RUNS_GLOBAL;
extern size_t VECTORIZE_SIZE_GLOBAL;
extern bool MEASURE_GLOBAL;
extern bool PRINT_GLOBAL;
extern const char* PATH_GLOBAL;
在主源文件中:
#include "modes.hh"
size_t PAGE_SIZE_GLOBAL;
size_t ALIGNMENT_GLOBAL;
size_t MEMCHUNK_SIZE_GLOBAL;
size_t RUNS_GLOBAL;
size_t VECTORIZE_SIZE_GLOBAL;
bool MEASURE_GLOBAL;
bool PRINT_GLOBAL;
const char* PATH_GLOBAL;
int main(const int argc, const char* argv[]){
...
//Initialize the globals with user input
PAGE_SIZE_GLOBAL = lArgs.pageSize();
ALIGNMENT_GLOBAL = lArgs.alignment();
MEMCHUNK_SIZE_GLOBAL = lArgs.chunkSize();
RUNS_GLOBAL = lArgs.runs();
VECTORIZE_SIZE_GLOBAL = lArgs.vectorized();
MEASURE_GLOBAL = lArgs.measure();
PRINT_GLOBAL = lArgs.print();
std::string tmp = lArgs.path() + storageModel + "/";
PATH_GLOBAL = tmp.c_str();
...
}
然后我在每个文件中包含头文件common.hh,其中需要一个全局变量(它可能在系统的深处)。
关于防止全局变量,我已经读了十几遍了,所以这显然是一种糟糕的风格。在 Steve McConnell 的 'Code Complete 2' 一书中,关于全局变量的章节也指出要防止全局变量并改用访问例程。在 'How to Use Access Routines' 部分,他写道
"Hide data in a class. Declare that data by using the static keyword
(...) to ensure only a single instance of the data exists. Write
routines that let you look at the data and change it."
首先,全局数据不会改变(也许以后会改变,但至少近期不会)。但是我不明白这些访问例程如何更好?我还将有一个 class 我需要包含在每个需要数据的文件中。唯一的区别是全局数据是通过 getter 函数访问的静态成员。
(已编辑)我还考虑过使用全局数据 Singleton class。但是具有所有全局数据的对象听起来有些过分,因为在其不同的目的地只需要对象的几个全局变量。
我的问题:我应该坚持使用全局变量吗?有没有更好的解决方案,我错过了什么?最佳做法是什么?
编辑:
如果我能找出几个最需要用户输入的 classes,我可以将全局数据更改为成员变量。将用户输入传递给这些 classes 的最佳做法是什么?将数据作为参数通过整个系统向下传递到最低层听起来是错误的。是否有适合这里的设计模式(考虑像工厂这样的东西)?
How to pass user input through the system without using global
variables.
很简单。惊喜,我创建了一个class。
有一段时间,我把这个 class 称为旅行箱,因为我认为它类似于旅行中对手提箱的需求。 TC_t 是一个非标准容器,其中包含对您目的地所发生的事情有用的东西,并且只创建了一个,并将引用传递给可以使用该信息的任何其他对象。不是全球性的,在最严格的意义上。
这个TC_t是在学习命令行选项时在main()线程中创建的。
我最近写了另一个人生游戏。用户输入包括 a) 输出目的地(即 tty num),b) 初始填充模式选择,c) 'overrides' 游戏板尺寸,d) 测试模式,包括最大速度,以及向量与数组选项对于细胞行为。
GOLUtil_t(生活实用程序游戏)(以前称为 TC_t)包括在不止一项努力中有用的方法。
对于你的问题,我避免的两个典型全局变量是 a) gameBoard 和 b) ansi 终端访问。
std::cout << "accessing '" << aTermPFN << "' with std::ofstream "
<< std::endl;
std::ofstream* ansiTerm = new std::ofstream(aTermPFN);
if (!ansiTerm->is_open())
{
dtbAssert(nullptr != ansiTerm)(aTermPFN);
std::cerr << "Can not access '" << aTermPFN << "'" << std::endl;
assert(0); // abort
}
// create game-board - with a vector of cell*
CellVec_t gameBoard;
gameBoard.reserve (aMaxRow * aMaxCol);
GOLUtil_t gBrd(aMaxRow, aMaxCol, gameBoard, *ansiTerm);
最后一行调用了 GOLUtil_t 的构造函数。
然后将实例 "gBrd" 传递(通过引用)给游戏的构造函数,并从那里传递给它包含的任何聚合对象。
std::string retVal;
{
// initialize display, initialize pattern
GameOfLife_t GOL(gBrd, timeOfDay, fillPatternChoiceLetter, useArray);
std::string retValS = GOL.exec2(testMode);
retVal = gBrd.clearGameBoard(retValS); // delete all cells
}
// force GameOfLife_t dtor before close ansiTerm
ansiTerm->close();
摘要 - 没有全局变量。
任何 class 需要此信息(输出到哪里?维度是什么?)的每个实例都可以在其整个生命周期内访问 GOLUtil_t。 GOLUtil_t 有减轻编码负担的方法。
注意:因为是单输出终端,所以我用的是单线程(main)
您的第一个重构工作可能是:
a) 删除全局 classes,
b) 而不是在 main() 中实例化它们(用于生命周期控制)
c) 然后将这些以前的全局实例通过引用传递给那些使用它们的非全局对象。我在 ctor(s) 中推荐。
d) 记得清理(有新的就删除)
我的环境:Ubuntu 15.10,64 位,g++ V5
我遇到了问题,我的应用程序可能有很多用户输入,这决定了应用程序的运行方式 运行。该应用程序是一个内存数据库系统,用户可以使用诸如“--pagesize 16384”(设置要使用的内存页面大小)、“--alignment 4096”(设置要使用的内存对齐)等命令调用程序或“--measure”(设置标志以测量某些例程)。
目前我将所有用户输入保存在头文件中定义为 extern 的全局变量中:
//@file common.hh
extern size_t PAGE_SIZE_GLOBAL;
extern size_t ALIGNMENT_GLOBAL;
extern size_t MEMCHUNK_SIZE_GLOBAL;
extern size_t RUNS_GLOBAL;
extern size_t VECTORIZE_SIZE_GLOBAL;
extern bool MEASURE_GLOBAL;
extern bool PRINT_GLOBAL;
extern const char* PATH_GLOBAL;
在主源文件中:
#include "modes.hh"
size_t PAGE_SIZE_GLOBAL;
size_t ALIGNMENT_GLOBAL;
size_t MEMCHUNK_SIZE_GLOBAL;
size_t RUNS_GLOBAL;
size_t VECTORIZE_SIZE_GLOBAL;
bool MEASURE_GLOBAL;
bool PRINT_GLOBAL;
const char* PATH_GLOBAL;
int main(const int argc, const char* argv[]){
...
//Initialize the globals with user input
PAGE_SIZE_GLOBAL = lArgs.pageSize();
ALIGNMENT_GLOBAL = lArgs.alignment();
MEMCHUNK_SIZE_GLOBAL = lArgs.chunkSize();
RUNS_GLOBAL = lArgs.runs();
VECTORIZE_SIZE_GLOBAL = lArgs.vectorized();
MEASURE_GLOBAL = lArgs.measure();
PRINT_GLOBAL = lArgs.print();
std::string tmp = lArgs.path() + storageModel + "/";
PATH_GLOBAL = tmp.c_str();
...
}
然后我在每个文件中包含头文件common.hh,其中需要一个全局变量(它可能在系统的深处)。
关于防止全局变量,我已经读了十几遍了,所以这显然是一种糟糕的风格。在 Steve McConnell 的 'Code Complete 2' 一书中,关于全局变量的章节也指出要防止全局变量并改用访问例程。在 'How to Use Access Routines' 部分,他写道
"Hide data in a class. Declare that data by using the static keyword (...) to ensure only a single instance of the data exists. Write routines that let you look at the data and change it."
首先,全局数据不会改变(也许以后会改变,但至少近期不会)。但是我不明白这些访问例程如何更好?我还将有一个 class 我需要包含在每个需要数据的文件中。唯一的区别是全局数据是通过 getter 函数访问的静态成员。
(已编辑)我还考虑过使用全局数据 Singleton class。但是具有所有全局数据的对象听起来有些过分,因为在其不同的目的地只需要对象的几个全局变量。
我的问题:我应该坚持使用全局变量吗?有没有更好的解决方案,我错过了什么?最佳做法是什么?
编辑: 如果我能找出几个最需要用户输入的 classes,我可以将全局数据更改为成员变量。将用户输入传递给这些 classes 的最佳做法是什么?将数据作为参数通过整个系统向下传递到最低层听起来是错误的。是否有适合这里的设计模式(考虑像工厂这样的东西)?
How to pass user input through the system without using global variables.
很简单。惊喜,我创建了一个class。
有一段时间,我把这个 class 称为旅行箱,因为我认为它类似于旅行中对手提箱的需求。 TC_t 是一个非标准容器,其中包含对您目的地所发生的事情有用的东西,并且只创建了一个,并将引用传递给可以使用该信息的任何其他对象。不是全球性的,在最严格的意义上。
这个TC_t是在学习命令行选项时在main()线程中创建的。
我最近写了另一个人生游戏。用户输入包括 a) 输出目的地(即 tty num),b) 初始填充模式选择,c) 'overrides' 游戏板尺寸,d) 测试模式,包括最大速度,以及向量与数组选项对于细胞行为。
GOLUtil_t(生活实用程序游戏)(以前称为 TC_t)包括在不止一项努力中有用的方法。
对于你的问题,我避免的两个典型全局变量是 a) gameBoard 和 b) ansi 终端访问。
std::cout << "accessing '" << aTermPFN << "' with std::ofstream "
<< std::endl;
std::ofstream* ansiTerm = new std::ofstream(aTermPFN);
if (!ansiTerm->is_open())
{
dtbAssert(nullptr != ansiTerm)(aTermPFN);
std::cerr << "Can not access '" << aTermPFN << "'" << std::endl;
assert(0); // abort
}
// create game-board - with a vector of cell*
CellVec_t gameBoard;
gameBoard.reserve (aMaxRow * aMaxCol);
GOLUtil_t gBrd(aMaxRow, aMaxCol, gameBoard, *ansiTerm);
最后一行调用了 GOLUtil_t 的构造函数。
然后将实例 "gBrd" 传递(通过引用)给游戏的构造函数,并从那里传递给它包含的任何聚合对象。
std::string retVal;
{
// initialize display, initialize pattern
GameOfLife_t GOL(gBrd, timeOfDay, fillPatternChoiceLetter, useArray);
std::string retValS = GOL.exec2(testMode);
retVal = gBrd.clearGameBoard(retValS); // delete all cells
}
// force GameOfLife_t dtor before close ansiTerm
ansiTerm->close();
摘要 - 没有全局变量。
任何 class 需要此信息(输出到哪里?维度是什么?)的每个实例都可以在其整个生命周期内访问 GOLUtil_t。 GOLUtil_t 有减轻编码负担的方法。
注意:因为是单输出终端,所以我用的是单线程(main)
您的第一个重构工作可能是:
a) 删除全局 classes,
b) 而不是在 main() 中实例化它们(用于生命周期控制)
c) 然后将这些以前的全局实例通过引用传递给那些使用它们的非全局对象。我在 ctor(s) 中推荐。
d) 记得清理(有新的就删除)
我的环境:Ubuntu 15.10,64 位,g++ V5