C ++:如何在不使用全局变量的情况下通过系统传递用户输入? [英] C++: How to pass user input through the system without using global variables?

查看:138
本文介绍了C ++:如何在不使用全局变量的情况下通过系统传递用户输入?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我遇到问题,我的应用程序可能有很多用户输入,这决定了应用程序的运行方式。该应用程序是一个内存数据库系统,用户可以例如用'--pagesize 16384'(设置要使用的内存页大小),'--alignment 4096'(设置要使用的内存对齐)或'--measure'(设置一个标志来衡量某些例程)。

目前我将全部用户输入保存在头文件中定义为extern的全局变量中:

  // @文件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;

以及主要源文件:

  #includemodes.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 []){

...
//使用用户输入初始化全局变量
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,每个文件中需要一个全局变量(在系统中可以非常深入)。

我已经读了十几遍以防止全局变量变量,所以这显然是不好的风格。在史蒂夫麦康奈尔的代码完成2一书中,关于全局变量的章节也提到要防止全局变量并使用访问例程。在如何使用访问例程一节中,他写道:
$ b


隐藏类中的数据。使用static关键字$声明数据b $ b(...)以确保只存在单个数据实例,编写
例程,让您查看数据并进行更改。


首先,全球数据不会改变(可能稍后会改变,但至少在不久的将来会改变)。但是我不明白这些访问例程会更好吗?我还将在每个需要数据的文件中包含一个课程。唯一的区别是全局数据是通过getter函数访问的静态成员。



(已编辑)我也考虑过使用全局数据Singleton类。但是一个包含所有全局数据的对象听起来有点过分,因为在不同的目的地只需要一些全局变量。

全局变量?有更好的解决方案,我错过了什么?什么是最佳做法?

编辑:
如果我要确定一些最需要用户输入的类,我可以将全局数据更改为成员变量。将用户输入传递给这些类的最佳做法是什么?将数据作为整个系统的参数传递到最底层听起来是错误的。是否有设计模式(考虑像工厂这样的东西),这将适合在这里?

解决方案


如何在不使用全局
变量的情况下通过系统传递用户输入。


这很容易。惊喜,我创建了一堂课。

有段时间,我把这个班叫做旅行箱,因为我认为这与行程期间旅行箱的需求相似。 TC_t是一个非标准的容器,它为你目的地正在发生的事情保存有用的东西,并且只有一个容器创建,并将引用传递给可以使用这些信息的任何其他对象。不是全局的,在最严格的意义上。

这个TC_t是在main()线程中创建的,同时研究了命令行选项。






我最近写了又一个游戏。用户输入包括:a)输出目标(即tty num),b)初始填充模式选择,c)游戏板尺寸的覆盖,d)测试模式,包括最大速度,以及单元格的矢量与阵列选项行为。

GOLUtil_t(生命游戏实用程序)(以前的TC_t)包含了多种用途的方法。



对于你的问题,我避免的两个典型的全局变量是a)gameBoard和b)ansi终端访问。

  std :: cout<< 访问<< aTermPFN<< '与std :: ofstream
<<的std :: ENDL;
std :: ofstream * ansiTerm = new std :: ofstream(aTermPFN);如果(!ansiTerm-> is_open())
{
dtbAssert(nullptr!= ansiTerm)(aTermPFN);

;
std :: cerr<< 无法访问<< aTermPFN<< '<<的std :: ENDL;
assert(0); //中止
}

//创建游戏板 - 带有单元格的矢量*
CellVec_t gameBoard;
gameBoard.reserve(aMaxRow * aMaxCol);

GOLUtil_t gBrd(aMaxRow,aMaxCol,gameBoard,* ansiTerm);

最后一行调用GOLUtil_t的ctor。



然后,将实例gBrd传递给游戏的对象,并从那里传递给它包含的任何聚合对象。

  std :: string retVal; 
{
//初始化显示,初始化模式
GameOfLife_t GOL(gBrd,timeOfDay,fillPatternChoiceLetter,useArray);

std :: string retValS = GOL.exec2(testMode);

retVal = gBrd.clearGameBoard(retValS); //删除所有单元格
}
//在关闭之前强制GameOfLife_t dtor ansiTerm

ansiTerm-> close();






总结 - 无全局。



需要这些信息的任何类的每个实例(何处输出?什么是维度)都可以在整个生命周期中访问GOLUtil_t。 GOLUtil_t有减轻编码负担的方法。注意:由于单输出终端,我使用了单线程(主)




您的第一个重构工作可能是:

a)删除全局类,

b),而是在main()中进行实例化(用于终生控制),然后将这些以前的全局实例传递给那些非全局对象利用它们。

d)记得清理(删除如果是新建的)



< hr>

我的环境:Ubuntu 15.10,64位,g ++ V5


I am having the problem, that my application can has a lot of user input which determines how the application will be run. The application is an in memory database system and the user could for example invoke the program with commands like '--pagesize 16384' (sets the memory page size to use), '--alignment 4096' (sets the memory alignment to use) or '--measure' (sets a flag to measure certain routines).

Currently I save all the user input in global variables which are defined as extern in a header file:

//@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;

and in main source file:

#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();

    ...
}

I then include the header file common.hh in each file, where a global variable is needed (which can be very deep down in the system).

I already read a dozen times to prevent global variables so this is obviously bad style. In the book 'Code Complete 2' from Steve McConnell the chapter about global variables also stated to prevent global variables and use access routines instead. In the section 'How to Use Access Routines' he writes

"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."

First of all, the global data won't change (maybe this is changed later but at least not in the near future). But I don't get how these access routines are any better? I will also have a class I need to include at every file where the data is needed. The only difference is the global data are static members accessed through getter functions.

(Edited) I also thought about using a global data Singleton class. But an object with ALL the global data sounds overkill since only a few global variables of the object are needed at its different destinations.

My Question: Should I just stick to the global variables? Are there better solutions, what am I missing? What are the best practices?

Edit: If I would identify a few classes where the user input is needed the most, I could change the global data to member variables. What would be the best practice to pass the user input to these classes? Passing the data as parameters through the whole system down to the lowest layers sounds wrong. Is there are design pattern (thinking about something like a factory) which would be suited here?

解决方案

How to pass user input through the system without using global variables.

It is easy. Surprise, I created a class.

For a while, I called this class a travel case, because I considered it analogous to the needs of a suitcase during a trip. The TC_t is a non-standard container which held useful things for what is going on at your destination, and there is only one created, with references passed to any other objects that could use the information. Not global, in the strictest sense.

This TC_t is created in main() thread, while studying the command line options.


I recently wrote yet-another-game-of-life. User inputs included a) destination of output (i.e. a tty num), b) initial fill-pattern choices, c) 'overrides' for game board dimensions, d) test modes, including max speed, and vector vs. array options for cell behaviours.

The GOLUtil_t (Game Of Life Utility) (previously TC_t) includes methods that are useful in more than one effort.

For your question, the two typical globals I avoided are the a) gameBoard, and b) ansi terminal access.

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);

This last line invoked the ctor of GOLUtil_t.

The instance "gBrd" is then passed (by reference) to the ctor of the game, and from there, to any aggregate objects it contained.

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();


Summary - No globals.

Every instance of any class that needed this info (where to output? what are dimensions?) has access to the GOLUtil_t for their entire lifetime. And GOLUtil_t has methods to lighten the coding load.

Note: because single output terminal, I used a single thread (main)


Your first refactor effort might be to:

a) remove the global classes,

b) and instead instantiate these in main() (for lifetime control)

c) and then pass-by-reference these formerly global instances to those non-global objects that make use of them. I recommend in the ctor(s).

d) remember to clean up (delete if new'd)


my environment: Ubuntu 15.10, 64 bit, g++ V5

这篇关于C ++:如何在不使用全局变量的情况下通过系统传递用户输入?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆