C++ “的全局变量”;“事物”;这是全球性的,而且;单身;?

C++ “的全局变量”;“事物”;这是全球性的,而且;单身;?,c++,design-patterns,embedded,microcontroller,C++,Design Patterns,Embedded,Microcontroller,当与微控制器打交道时,有些东西本质上是全局性的——我想到的是串行端口或其他接口之类的外围设备。还有一些外围设备不仅是全局的,而且只有一个(而且永远不会有更多的),比如外围设备控制核心时钟或中断控制器。这些外围设备确实具有某种全局状态(例如,核心时钟设置为某个值),因此反向计算这些值的效率很低 如果我想让我的程序很好地面向对象,我很难决定如何处理这些对象。。。全局变量是不好的,这是显而易见的,但我只是不知道(没有足够的经验)我是否应该尝试“隐藏”这些东西是全局的事实。。。例如,“cin”或“std

当与微控制器打交道时,有些东西本质上是全局性的——我想到的是串行端口或其他接口之类的外围设备。还有一些外围设备不仅是全局的,而且只有一个(而且永远不会有更多的),比如外围设备控制核心时钟或中断控制器。这些外围设备确实具有某种全局状态(例如,核心时钟设置为某个值),因此反向计算这些值的效率很低

如果我想让我的程序很好地面向对象,我很难决定如何处理这些对象。。。全局变量是不好的,这是显而易见的,但我只是不知道(没有足够的经验)我是否应该尝试“隐藏”这些东西是全局的事实。。。例如,“cin”或“stdout”也是全局变量(让我们忽略一个事实,即在多线程应用程序中,它们通常是特定于线程的),没有人会隐藏这一点。。。让我们继续使用时钟生成器外围设备-只有一个,因此我可以使用单例反模式(;或使类成为静态的,或使单个对象成为全局的(这是我通常所做的),因为此对象存储了当前时钟设置,并且许多其他事情都需要此值-需要设置RTOS使用的系统计时器,需要设置其他外围设备的时钟(UART波特率、SPI比特率等),需要为外部内存设置正确的时钟或配置内存等待状态。这就是为什么我认为在main()中创建一个对象并将其到处传递会有点麻烦的原因

我可以编写这些方法,以便所有“全局”信息都来自外围寄存器(例如,核心频率可以从当前的PLL设置中反向计算),但这似乎也是一个错误的想法,更不用说为无处不在的时钟发生器外围设备创建对象看起来很有趣

当前的时钟设置可以存储在类的静态成员中,但从这里开始,向完全静态类只有一小步(因为“this”指针对于没有状态的类是无用的)

通常在非面向对象程序中找到的解决方案最接近于完全静态类-只有对全局变量进行操作的函数


任何人都知道如何很好地处理这种情况,或者这个问题是否值得花时间?也许我应该只使用一个全局对象并完成它(;

您已经大致列出了您的选项:

  • 全球的
  • 类/单例的静态数据成员
这最终取决于你在两者之间做出选择,并从美学或其他方面选择你更喜欢的

最后,正如您所说,您仍然会有一个时钟发生器、一个UART等


需要记住的一点是,仅仅为了抽象的目的而进行太多的抽象并不会给您带来太多的好处。但是,它可能会让不熟悉您的代码的人更难了解类层背后的工作原理。因此,如果有的话,请考虑您的团队。

单例模式当然,这是一个争论的来源。有些人只是简单地说它是“坏的”,但我不同意;它只是被广泛地误用和误解。它在某些情况下肯定是有用的。事实上,它完全适用于你所描述的情况

如果您有一个需要全局可用的类,并且本质上不能有多个实例,那么单例是最好的选择

如果我想让我的程序很好地面向对象,我很难决定如何处理这些对象…全局变量并不好,这是显而易见的,但我只是不知道(没有足够的经验)我是否应该尝试“隐藏”这些东西是全局的这一事实

当我读到这篇文章时,我想知道你是否知道为什么要使用OOP,为什么不使用globals

首先,OOP是一个工具,而不是一个目标。在您的情况下,中断控制器不需要派生和虚拟函数之类的东西。您所需要的只是一个编程接口,封装在一个类中。您甚至可以使用一组普通函数来实现这一点(C=风格的模块化编程)在不放弃可维护性的情况下。在您的情况下,将单个实例变为全局更为清晰。设想另一种情况,程序的不同部分可以实例化一个类,该类用于访问下面的同一UART。如果您使用全局,则代码(或者更确切地说是作者)意识到这一点,并将考虑如何协调访问

现在,关于globals,下面是一个为什么不使用它们的示例:

int a, b, c;

void f1()
{
    c = a;
    f3();
}

void f2()
{
    c = b;
    f3();
}

void f3()
{
    // use c
}

int main()
{
    a = 4;
    f1();
    b = 5;
    f2();
}
这里的要点是,参数存储在全局中,而不是将其作为实际参数传递,这样就很难看到它们的使用位置和使用时间。此外,上述使用完全排除了任何递归调用。但就您的环境而言,有些东西本质上是全局的,因为它们是系统的唯一部分环境,比如中断控制器(类似于cin/cout/cerr/clog)。不要担心这些。在您需要考虑限制访问之前,必须有很多中断控制器被到处使用

有两个准则可以简化此过程:

  • 一个对象的范围越大,它就越需要一个会说话的名称(与上面的a、b、c相比)。在您的情况下,我会将所有特定于硬件的对象存储在一个公共名称空间中,因此很明显有些代码正在访问全局。这还允许对该名称空间进行单独测试和重用。许多硬件供应商甚至以库的形式提供类似的内容,以帮助应用程序开发
  • 在对硬件进行代码包装/建模的过程中,执行请求,但不要在上面做出特定于应用程序的决策。C