基础C++设计

Basic C++ Design

本文关键字:设计 C++ 基础      更新时间:2023-10-16

我有一个关于C++类设计的问题。我经常面临这样的小问题,我想得到一些关于什么更容易被接受的建议。

我有一些类通过UDP监控某些设备的温度。如果设备接收到一个数据包,它将向stdout打印"x\n"以显示它接收到了什么。然后是检查数据包中的数据,并验证数据是否显示设备温度过高。如果它太高,我必须调用一些函数。如果不是,我必须调用其他函数。

我不确定我是否应该这样做:

enum temperature {TEMPERATURE_FINE, TEMPERATURE_EXCEEDED};
int main(int argc, char* argv[])
{
    std::vector<std::string> args(argv+1, argv + argc);
    if(!args.size())
        cout << "No parameters entered.n";
    else
    {
        CTemperatureMonitor tempMonitor(args);
        if(tempMonitor.MonitorTemperature() == TEMPERATURE_EXCEEDED)
            tempMonitor.ActivateAlarm();
        else
            tempMonitor.DisableAlarm();
    }
    return 0;
}

其中tempMonitor.MonitorTemperature()调用std::cout << "xn"。因此std::cout << "xn"被内置到类中。

或者:

enum temperature {TEMPERATURE_FINE, TEMPERATURE_EXCEEDED};
int main(int argc, char* argv[])
{
    std::vector<std::string> args(argv+1, argv + argc);
    if(!args.size())
        cout << "No parameters entered.n";
    else
    {
        CTemperatureMonitor tempMonitor(args);
        temperature tempExceeded = tempMonitor.MonitorTemperature();
        std::cout << "xn";
        if(tempExceeded == TEMPERATURE_EXCEEDED)
            tempMonitor.ActivateAlarm();
        else
            tempMonitor.DisableAlarm();
    }
    return 0;
}

其中CCD_ 3不包括在该类中。

std::cout << "xn"必须出现在调用CTemperatureMonitor::ActivateAlarm()CTemperatureMonitor::DisableAlarm()之前。

我知道这可能看起来很小,也很简单,但我经常想知道到底什么应该成为课堂的一部分。该类是否应该输出到stdout?我做一个还是做另一个有什么区别吗?我是不是太迂腐了?

此外,顺便说一句,我知道全局变量被认为是糟糕的做法。我在main和class中都使用了温度枚举。我应该声明两次,一次在main中,一次是在CTemperatureMonitor类中,还是全局声明一次?虽然这个问题看起来相当具体,但它实际上会为我澄清很多其他事情

谢谢。

首先,我想指出的是,项目有不同的规模,根据规模(和关键性),建议实际上会有所不同。因此,首先有一条经验法则:

您放置的"框架"(Logger、Option Parser等)的大小可能不应超过整个程序的10%。在这一点之后,这就太过分了。除非这是练习的目标!

话虽如此,我们可以开始看看你的实际问题。


此外,顺便说一句,我知道全局变量被认为是糟糕的做法。我在main和class中都使用了温度枚举。我应该声明两次,一次在main中,一次是在CTemperatureMonitor类中,还是全局声明一次?

实际上,您弄错了变量类型temperature是一种类型(枚举类型)。

通常,类型被用作程序各个部分之间的桥梁,为此,所有这些部分共享相同的类型定义是很重要的。因此,对于类型,实际声明两次是不好的做法。

此外,并非所有的全球都是邪恶的。全局变量是(共享状态),但全局常量很好,通常扮演类似于类型的角色。


我知道这可能看起来很小,也很简单,但我经常想知道到底什么应该成为课堂的一部分。该类是否应该输出到stdout?我做一个还是做另一个有什么区别吗?我是不是太迂腐了?

有两种输出:

  • 日志记录输出,用于在遇到问题时诊断问题
  • 真实输出,这是程序所做的

根据程序的不同,您可能同时拥有或没有。

从迂腐的角度来看,你通常不喜欢把它们混合在一起。例如,您可以完美地将日志记录发送到一个文件,或者当它很严重时发送到stderr,并使用stdout作为"有用"的东西。

这实际上在一定程度上推动了设计,因为您需要两个接收器:每个输出一个。

由于您有一个非常简单的程序,最简单的方法可能是在构造时简单地将两个不同的std::ostream&传递给您的类。或者,更简单的是,只需要两个泛型函数并使用(邪恶的)全局变量。

在较大的程序中,您可能会设计一个Logger类,该类将具有各种日志级别,并提供特定的宏来注册(自动)日志行的函数名、文件名和行号。您可能还有一个轻量级的日志记录机制,允许您在Release构建中禁用DEBUG/DEV级别的日志记录。

单一抽象级别的原则有利于用相同的方法进行所有I/O,而不是在高抽象级别和低抽象级别进行一些I/O。

换句话说,如果你相信这一原则,那么通过cin/cout将输入和输出保持在同一方法中,而不是显示和隐藏一些是一个好主意。它倾向于在每个类中提供可读性更强、依赖性更少的代码。

根据单一责任原则,第二种选择是首选(任何类都应该只有一个责任,即监测您的情况下的温度,而不是输出结果),尽管您可能希望设置另一个类来处理温度监测结果(例如将结果写入某个日志文件或其他文件)。

以某种方式记录有关程序的信息是正常的
这种方式在全球范围内也是很正常的。

否则,在调用方法时,事情就会变得过于复杂
你唯一应该做的就是:

有一个logger类(或使用现有的logger类),可以将其输出流设置为您选择的任何值(包括std::out和一个不打印任何内容的空流)
最终,要有一个可以隐藏在#define后面的记录器,这样它就不会减慢在Release模式下运行的代码。

在我看来,在这种情况下,您不应该将cout包含在类中,因为有时以后您需要输出到文件中,或者根本不输出。因此,如果cout没有放在类中,您将有机会在没有任何更改的情况下重用类代码。

两者都没有。main的职责是用命令行参数启动应用程序并给出返回值。其他一切都不应该存在。您可能想看一本书,如"对象设计、角色、责任和协作"