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

时间:2017-08-07 12:11:43

标签: c++ global-variables extern

我遇到了问题,我的应用程序可能有很多用户输入,决定了应用程序的运行方式。该应用程序是一个内存数据库系统,用户可以例如使用'--pagesize 16384'(设置要使用的内存页面大小),' - align 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”一书中,关于全局变量的章节也说明了防止全局变量并使用访问例程。在“如何使用访问例程”一节中,他写了

  

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

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

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

我的问题:我应该坚持全球变量吗?是否有更好的解决方案,我缺少什么?什么是最佳实践?

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

1 个答案:

答案 0 :(得分:1)

  

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

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

有一段时间,我把这个班级称为旅行案例,因为我认为它类似于旅行期间旅行箱的需求。 TC_t是一个非标准的容器,它为目的地的内容提供有用的东西,并且只创建了一个,其中引用传递给可以使用该信息的任何其他对象。从严格意义上说,不是全球性的。

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

我最近写了另一种生活游戏。用户输入包括a)输出目的地(即tty数),b)初始填充模式选择,c)'覆盖'对于游戏板尺寸,d)测试模式,包括最大速度,以及用于细胞行为的矢量与阵列选项。

GOLUtil_t(Game Of Life Utility)(以前的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的ctor。

实例&#34; gBrd&#34;然后(通过引用)传递给游戏的ctor,并从那里传递给它包含的任何聚合对象。

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

摘要 - 没有全局变量。

需要此信息的任何类的每个实例(在哪里输出?维度是什么?)都可以在整个生命周期内访问GOLUtil_t。并且GOLUtil_t具有减轻编码负荷的方法。

注意:因为单输出端,我使用单线程(主)

你的第一个重构努力可能是:

a)删除全局类

b)而是在main()中实例化这些(用于生命周期控制)

c)然后将这些以前的全局实例传递给那些使用它们的非全局对象。我推荐在ctor(s)中。

d)记得清理(删除新的&#39; d)

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